Add Drizzle + Postgres setup (task 03)
drizzle-orm + postgres driver + drizzle-kit wired up. Core schema declares users, households, and household_members. docker-compose.dev.yaml runs Postgres 16 locally. Initial migration generated and verified applied. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
09d6adbf18
commit
37977568ed
@@ -6,10 +6,11 @@ Living progress tracker. Update at the end of each task. The canonical brief is
|
||||
|
||||
- **01 — Repo init & tooling** (commit `b89690a`). pnpm 10 + TS strict + ESLint flat + Prettier. All acceptance criteria green.
|
||||
- **02 — Next.js app skeleton**. Next.js 15 + React 19 + Tailwind v4 + shadcn/ui (button, card, input, dialog). `pnpm dev` serves placeholder, `pnpm build` produces `.next/standalone/`, `pnpm lint` clean. Added `.npmrc` with `node-linker=hoisted` for Windows symlink compatibility.
|
||||
- **03 — Drizzle + Postgres setup**. drizzle-orm + postgres driver + drizzle-kit wired up. `src/modules/_core/schema.ts` declares `users`, `households`, `household_members`. `docker-compose.dev.yaml` starts Postgres 16. `drizzle/0000_silent_magma.sql` generated and applied. `tsc --noEmit` passes.
|
||||
|
||||
## Next up
|
||||
|
||||
- **03 — Drizzle + Postgres setup** ([brief](docs/tasks/03-drizzle-postgres.md)). Phase 1 is sequential through 08.
|
||||
- **04 — Module loader** ([brief](docs/tasks/04-module-loader.md)). Phase 1 is sequential through 08.
|
||||
|
||||
## Phase 1 remaining
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
services:
|
||||
famapp-db:
|
||||
image: postgres:16-alpine
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_DB: famapp
|
||||
POSTGRES_USER: famapp
|
||||
POSTGRES_PASSWORD: famapp
|
||||
ports:
|
||||
- "5432:5432"
|
||||
volumes:
|
||||
- famapp-db-data:/var/lib/postgresql/data
|
||||
|
||||
volumes:
|
||||
famapp-db-data:
|
||||
@@ -0,0 +1,10 @@
|
||||
import { defineConfig } from "drizzle-kit";
|
||||
|
||||
export default defineConfig({
|
||||
dialect: "postgresql",
|
||||
schema: "./src/modules/**/schema.ts",
|
||||
out: "./drizzle",
|
||||
dbCredentials: {
|
||||
url: process.env["DATABASE_URL"]!,
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,24 @@
|
||||
CREATE TYPE "public"."household_member_role" AS ENUM('owner', 'member');--> statement-breakpoint
|
||||
CREATE TABLE "household_members" (
|
||||
"household_id" uuid NOT NULL,
|
||||
"user_id" uuid NOT NULL,
|
||||
"role" "household_member_role" DEFAULT 'member' NOT NULL,
|
||||
CONSTRAINT "household_members_household_id_user_id_pk" PRIMARY KEY("household_id","user_id")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "households" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"name" varchar(255) NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "users" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"email" varchar(255) NOT NULL,
|
||||
"display_name" varchar(255) NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT "users_email_unique" UNIQUE("email")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "household_members" ADD CONSTRAINT "household_members_household_id_households_id_fk" FOREIGN KEY ("household_id") REFERENCES "public"."households"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "household_members" ADD CONSTRAINT "household_members_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;
|
||||
+7
-1
@@ -15,7 +15,10 @@
|
||||
"lint:fix": "eslint . --fix",
|
||||
"format": "prettier --write .",
|
||||
"format:check": "prettier --check .",
|
||||
"typecheck": "tsc --noEmit"
|
||||
"typecheck": "tsc --noEmit",
|
||||
"db:generate": "drizzle-kit generate",
|
||||
"db:migrate": "drizzle-kit migrate",
|
||||
"db:studio": "drizzle-kit studio"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3.3.5",
|
||||
@@ -24,6 +27,7 @@
|
||||
"@types/node": "^22.9.0",
|
||||
"@types/react": "^19.2.14",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"drizzle-kit": "^0.31.10",
|
||||
"eslint": "^9.15.0",
|
||||
"eslint-config-next": "^16.2.4",
|
||||
"globals": "^15.12.0",
|
||||
@@ -36,8 +40,10 @@
|
||||
"@base-ui/react": "^1.4.1",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"drizzle-orm": "^0.45.2",
|
||||
"lucide-react": "^1.14.0",
|
||||
"next": "^15.5.15",
|
||||
"postgres": "^3.4.9",
|
||||
"react": "^19.2.5",
|
||||
"react-dom": "^19.2.5",
|
||||
"shadcn": "^4.7.0",
|
||||
|
||||
Generated
+942
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,6 @@
|
||||
import { drizzle } from "drizzle-orm/postgres-js";
|
||||
import postgres from "postgres";
|
||||
|
||||
const client = postgres(process.env["DATABASE_URL"]!);
|
||||
|
||||
export const db = drizzle(client);
|
||||
@@ -0,0 +1,30 @@
|
||||
import { pgEnum, pgTable, primaryKey, timestamp, uuid, varchar } from "drizzle-orm/pg-core";
|
||||
|
||||
export const roleEnum = pgEnum("household_member_role", ["owner", "member"]);
|
||||
|
||||
export const users = pgTable("users", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
email: varchar("email", { length: 255 }).notNull().unique(),
|
||||
displayName: varchar("display_name", { length: 255 }).notNull(),
|
||||
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
||||
});
|
||||
|
||||
export const households = pgTable("households", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
name: varchar("name", { length: 255 }).notNull(),
|
||||
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
||||
});
|
||||
|
||||
export const householdMembers = pgTable(
|
||||
"household_members",
|
||||
{
|
||||
householdId: uuid("household_id")
|
||||
.notNull()
|
||||
.references(() => households.id, { onDelete: "cascade" }),
|
||||
userId: uuid("user_id")
|
||||
.notNull()
|
||||
.references(() => users.id, { onDelete: "cascade" }),
|
||||
role: roleEnum("role").notNull().default("member"),
|
||||
},
|
||||
(t) => [primaryKey({ columns: [t.householdId, t.userId] })],
|
||||
);
|
||||
Reference in New Issue
Block a user