Database Configure DrizzleORM with PostgreSQL and manage your database schema
Datyze uses DrizzleORM with PostgreSQL for type-safe database operations. The schema is defined in the @lucashochart/database package.
packages/database/
├── src/
│ ├── schema.ts # Database schema definitions
│ └── index.ts # Database client and exports
├── drizzle/ # Migration files
└── drizzle.config.ts # Drizzle configuration
Set your database URL in .env:
DATABASE_URL = "postgresql://user:password@localhost:5432/datyze"
// User table
export const user = pgTable ( "user" , {
id: uuid ( "id" ). defaultRandom (). primaryKey (),
name: text ( "name" ),
email: text ( "email" ). unique (). notNull (),
emailVerified: timestamp ( "emailVerified" ),
image: text ( "image" ),
isAdmin: boolean ( "isAdmin" ). default ( false ),
createdAt: timestamp ( "createdAt" ). defaultNow (). notNull (),
updatedAt: timestamp ( "updatedAt" ). defaultNow (). notNull (),
});
// Account (OAuth connections)
export const account = pgTable ( "account" , {
userId: uuid ( "userId" )
. notNull ()
. references (() => user.id),
type: text ( "type" ). notNull (),
provider: text ( "provider" ). notNull (),
providerAccountId: text ( "providerAccountId" ). notNull (),
// ... OAuth tokens
});
// Session
export const session = pgTable ( "session" , {
sessionToken: text ( "sessionToken" ). primaryKey (),
userId: uuid ( "userId" )
. notNull ()
. references (() => user.id),
expires: timestamp ( "expires" ). notNull (),
});
// Team
export const team = pgTable ( "team" , {
id: uuid ( "id" ). defaultRandom (). primaryKey (),
name: text ( "name" ). notNull (),
slug: text ( "slug" ). unique (). notNull (),
createdAt: timestamp ( "createdAt" ). defaultNow (). notNull (),
updatedAt: timestamp ( "updatedAt" ). defaultNow (). notNull (),
});
// Team Member
export const teamMember = pgTable ( "team_member" , {
id: uuid ( "id" ). defaultRandom (). primaryKey (),
teamId: uuid ( "teamId" )
. notNull ()
. references (() => team.id),
userId: uuid ( "userId" )
. notNull ()
. references (() => user.id),
role: teamRoleEnum ( "role" ). default ( "member" ). notNull (),
createdAt: timestamp ( "createdAt" ). defaultNow (). notNull (),
});
// Team Roles
export const teamRoleEnum = pgEnum ( "team_role" , [ "owner" , "admin" , "member" ]);
export const subscription = pgTable ( "subscription" , {
id: uuid ( "id" ). defaultRandom (). primaryKey (),
teamId: uuid ( "teamId" )
. notNull ()
. references (() => team.id),
// Stripe fields
stripeCustomerId: text ( "stripeCustomerId" ),
stripeSubscriptionId: text ( "stripeSubscriptionId" ),
// Lemon Squeezy fields
lemonSqueezyCustomerId: text ( "lemonSqueezyCustomerId" ),
lemonSqueezySubscriptionId: text ( "lemonSqueezySubscriptionId" ),
// Common fields
status: subscriptionStatusEnum ( "status" ). default ( "inactive" ),
plan: text ( "plan" ),
currentPeriodEnd: timestamp ( "currentPeriodEnd" ),
});
export const invitation = pgTable ( "invitation" , {
id: uuid ( "id" ). defaultRandom (). primaryKey (),
teamId: uuid ( "teamId" )
. notNull ()
. references (() => team.id),
email: text ( "email" ). notNull (),
role: teamRoleEnum ( "role" ). default ( "member" ). notNull (),
token: text ( "token" ). unique (). notNull (),
invitedBy: uuid ( "invitedBy" )
. notNull ()
. references (() => user.id),
expiresAt: timestamp ( "expiresAt" ). notNull (),
acceptedAt: timestamp ( "acceptedAt" ),
createdAt: timestamp ( "createdAt" ). defaultNow (). notNull (),
});
import { db } from "@lucashochart/database" ;
import { user, team, teamMember } from "@lucashochart/database/schema" ;
import { eq } from "drizzle-orm" ;
// Find user by email
const foundUser = await db.query.user. findFirst ({
where: eq (user.email, "user@example.com" ),
});
// Find user's teams
const userTeams = await db.query.teamMember. findMany ({
where: eq (teamMember.userId, userId),
with: {
team: true ,
},
});
import { db } from "@lucashochart/database" ;
import { team } from "@lucashochart/database/schema" ;
const newTeam = await db
. insert (team)
. values ({
name: "My Team" ,
slug: "my-team" ,
})
. returning ();
import { db } from "@lucashochart/database" ;
import { user } from "@lucashochart/database/schema" ;
import { eq } from "drizzle-orm" ;
await db. update (user). set ({ name: "New Name" }). where ( eq (user.id, userId));
import { db } from "@lucashochart/database" ;
import { invitation } from "@lucashochart/database/schema" ;
import { eq } from "drizzle-orm" ;
await db. delete (invitation). where ( eq (invitation.id, invitationId));
Command Description pnpm db:pushPush schema changes to database (dev) pnpm db:generateGenerate migration files pnpm db:migrateRun migrations (production) pnpm db:studioOpen Drizzle Studio GUI
Define the table in packages/database/src/schema.ts:
export const myTable = pgTable ( "my_table" , {
id: uuid ( "id" ). defaultRandom (). primaryKey (),
name: text ( "name" ). notNull (),
createdAt: timestamp ( "created_at" ). defaultNow (). notNull (),
});
Export from the schema file
Run pnpm db:push to apply changes
In production, use pnpm db:generate followed by pnpm db:migrate for safer migrations.
Define relations for Drizzle's query builder:
export const teamRelations = relations (team, ({ many , one }) => ({
members: many (teamMember),
subscription: one (subscription, {
fields: [team.id],
references: [subscription.teamId],
}),
invitations: many (invitation),
}));
Add indexes for frequently queried columns:
export const invitation = pgTable (
"invitation" ,
{
// ... columns
},
( table ) => ({
emailIdx: index ( "invitation_email_idx" ). on (table.email),
teamIdIdx: index ( "invitation_team_id_idx" ). on (table.teamId),
})
);