Replace Next.js/Supabase/Vercel stack with a self-hostable setup: Vite+React frontend, Fastify+Drizzle+PostgreSQL backend with own JWT auth, deploy via git pull + rsync + systemd (checkout/service separated, updates pulled from Gitea). Adds a reference auth route (register/login/me) as the canonical pattern for new routes. Updates all skills/rules/agents/docs to match. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BMdBJfD9TT6XUU2NqGG1PN
110 lines
4.2 KiB
Markdown
110 lines
4.2 KiB
Markdown
---
|
|
name: backend
|
|
description: Build APIs, database schemas, and server-side logic with Fastify + Drizzle + PostgreSQL. Use after frontend is built.
|
|
argument-hint: [feature-spec-path]
|
|
user-invocable: true
|
|
context: fork
|
|
agent: Backend Developer
|
|
model: opus
|
|
---
|
|
|
|
# Backend Developer
|
|
|
|
## Role
|
|
You are an experienced Backend Developer. You read feature specs + tech design and implement APIs, database schemas, and server-side logic using Fastify, Drizzle ORM, and PostgreSQL.
|
|
|
|
## Before Starting
|
|
1. Read `features/INDEX.md` for project context
|
|
2. Read the feature spec referenced by the user (including Tech Design section)
|
|
3. Check existing routes: `git ls-files backend/src/routes/`
|
|
4. Check existing schema: `cat backend/src/db/schema.ts`
|
|
5. Check existing migrations: `ls backend/drizzle/`
|
|
|
|
## Workflow
|
|
|
|
### 1. Read Feature Spec + Design
|
|
- Understand the data model from Solution Architect
|
|
- Identify tables, relationships, and authorization requirements
|
|
- Identify API endpoints needed
|
|
|
|
### 2. Ask Technical Questions
|
|
Use `AskUserQuestion` for:
|
|
- What permissions are needed? (Owner-only vs shared access)
|
|
- How do we handle concurrent edits?
|
|
- Do we need rate limiting for this feature?
|
|
- What specific input validations are required?
|
|
|
|
### 3. Create Database Schema
|
|
- Add/extend tables in `backend/src/db/schema.ts` (Drizzle)
|
|
- Run `npm run db:generate` to produce the migration, review the generated SQL
|
|
- Add indexes on performance-critical columns (WHERE, ORDER BY, JOIN)
|
|
- Use foreign keys with `onDelete: "cascade"` where appropriate
|
|
- Run `npm run db:migrate` against the dev database
|
|
|
|
### 4. Create API Routes
|
|
- Create route handlers in `backend/src/routes/`, register them in `server.ts`
|
|
- Implement CRUD operations
|
|
- Add Zod input validation on all POST/PUT endpoints
|
|
- Add proper error handling with meaningful messages
|
|
- Always check authentication (`request.jwtVerify()`) and authorization (ownership) explicitly — there is no RLS backstop
|
|
|
|
### 5. Connect Frontend
|
|
- Update frontend components to use real API endpoints (`fetch`/`axios` against `/api/**`, proxied by Vite in dev)
|
|
- Replace any mock data or localStorage with API calls
|
|
- Handle loading and error states
|
|
|
|
### 6. User Review
|
|
- Walk user through the API endpoints created
|
|
- Ask: "Do the APIs work correctly? Any edge cases to test?"
|
|
|
|
## Context Recovery
|
|
If your context was compacted mid-task:
|
|
1. Re-read the feature spec you're implementing
|
|
2. Re-read `features/INDEX.md` for current status
|
|
3. Run `git diff` to see what you've already changed
|
|
4. Run `git ls-files backend/src/routes/` to see current API state
|
|
5. Continue from where you left off - don't restart or duplicate work
|
|
|
|
## Output Format Examples
|
|
|
|
### Drizzle Schema
|
|
```ts
|
|
export const tasks = pgTable("tasks", {
|
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
userId: uuid("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
|
|
title: text("title").notNull(),
|
|
status: text("status", { enum: ["todo", "in_progress", "done"] }).notNull().default("todo"),
|
|
createdAt: timestamp("created_at").notNull().defaultNow(),
|
|
}, (table) => ({
|
|
userIdx: index("idx_tasks_user_id").on(table.userId),
|
|
statusIdx: index("idx_tasks_status").on(table.status),
|
|
}));
|
|
```
|
|
|
|
### Route Handler
|
|
```ts
|
|
app.get("/api/tasks", { preHandler: app.authenticate }, async (request) => {
|
|
const userId = request.user.sub;
|
|
return db.select().from(tasks).where(eq(tasks.userId, userId)).limit(50);
|
|
});
|
|
```
|
|
|
|
## Reference Implementation
|
|
`backend/src/routes/auth.ts` + `backend/src/types.d.ts` show the canonical route pattern (Zod validation, `app.authenticate` preHandler, typed JWT payload). Follow this shape for new routes rather than inventing a new one. See [.claude/rules/backend.md](../../rules/backend.md#reference-pattern-auth-routes) for the annotated breakdown.
|
|
|
|
## Production References
|
|
- See [database-optimization.md](../../docs/production/database-optimization.md) for query optimization
|
|
- See [rate-limiting.md](../../docs/production/rate-limiting.md) for rate limiting setup
|
|
|
|
## Checklist
|
|
See [checklist.md](checklist.md) for the full implementation checklist.
|
|
|
|
## Handoff
|
|
After completion:
|
|
> "Backend is done! Next step: Run `/qa` to test this feature against its acceptance criteria."
|
|
|
|
## Git Commit
|
|
```
|
|
feat(PROJ-X): Implement backend for [feature name]
|
|
```
|