PostgreSQL + Sequelize
Node — Sequelize models, migrations, associations, and seeding.
Use Sequelize for PostgreSQL when you need mature migration tooling, associations, and an established ORM pattern.
Install
pnpm add sequelize pg pg-hstore
pnpm add -D @types/pg sequelize-cliConnection setup
// src/db/sequelize.ts
import { Sequelize } from "sequelize";
export const sequelize = new Sequelize(process.env.DATABASE_URL!, {
dialect: "postgres",
logging: process.env.NODE_ENV === "development" ? console.log : false,
pool: { max: 10, min: 0, acquire: 30_000, idle: 10_000 },
});Model definition
// src/models/user.model.ts
import { DataTypes, Model, Optional } from "sequelize";
import { sequelize } from "../db/sequelize";
interface UserAttributes {
id: string;
email: string;
cognitoSub: string;
createdAt: Date;
}
type UserCreation = Optional<UserAttributes, "id" | "createdAt">;
export class User extends Model<UserAttributes, UserCreation> implements UserAttributes {
declare id: string;
declare email: string;
declare cognitoSub: string;
declare createdAt: Date;
}
User.init(
{
id: { type: DataTypes.UUID, defaultValue: DataTypes.UUIDV4, primaryKey: true },
email: { type: DataTypes.STRING, allowNull: false, unique: true },
cognitoSub: { type: DataTypes.STRING, allowNull: false, unique: true },
createdAt: { type: DataTypes.DATE, allowNull: false },
},
{ sequelize, tableName: "users", timestamps: true, updatedAt: false },
);Associations
// src/models/index.ts
import { User } from "./user.model";
import { Project } from "./project.model";
User.hasMany(Project, { foreignKey: "ownerId", as: "projects" });
Project.belongsTo(User, { foreignKey: "ownerId", as: "owner" });
export { User, Project };Migrations
npx sequelize-cli init
npx sequelize-cli migration:generate --name create-users-table
npx sequelize-cli db:migrate
npx sequelize-cli db:migrate:undo # Rollback last// migrations/20260524-create-users-table.js
"use strict";
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable("users", {
id: { type: Sequelize.UUID, primaryKey: true, defaultValue: Sequelize.UUIDV4 },
email: { type: Sequelize.STRING, allowNull: false, unique: true },
cognito_sub: { type: Sequelize.STRING, allowNull: false, unique: true },
created_at: { type: Sequelize.DATE, allowNull: false },
});
},
async down(queryInterface) {
await queryInterface.dropTable("users");
},
};Usage in services
// src/services/users.service.ts
import { User } from "../models/user.model";
export async function findByCognitoSub(sub: string) {
return User.findOne({ where: { cognitoSub: sub } });
}Scripts
{
"scripts": {
"db:migrate": "sequelize-cli db:migrate",
"db:migrate:undo": "sequelize-cli db:migrate:undo",
"db:seed": "sequelize-cli db:seed:all"
}
}