← Back to list

server
by ohall
⭐ 0🍴 0📅 Jan 18, 2026
SKILL.md
name: server description: Fastify API server with Drizzle ORM and PostgreSQL. Use when building API routes, database operations, or the sync endpoint in apps/server/.
Server Development
Tech Stack
- Runtime: Node.js (LTS)
- Framework: Fastify
- ORM: Drizzle
- Database: PostgreSQL
- Validation: Zod (via
@dink-derby/shared-types)
Project Structure
src/
index.ts # Fastify app setup and routes
sync.ts # Sync endpoint handler
db/
index.ts # Drizzle client
schema.ts # Table definitions
migrate.ts # Migration runner
drizzle/
*.sql # Generated migrations
Database Schema
Tables defined in src/db/schema.ts:
users— anglersderbies— competitionsderby_participants— who's in each derbycatches— fish caught (soft delete viadeletedAt)chat_messages— future group chat
Key patterns:
- IDs are
text(client-generated CUIDs) - Timestamps use
timestamptype withdefaultNow() - Foreign keys reference parent tables
API Routes
Core Endpoints
GET /derbies # List derbies
POST /derbies # Create derby
GET /derbies/:id # Get derby details
PATCH /derbies/:id # Update derby
POST /derbies/:id/join # Join a derby
GET /derbies/:id/catches # List catches
POST /derbies/:id/catches # Log a catch
PATCH /catches/:id # Update catch
DELETE /catches/:id # Soft delete catch
POST /sync # Offline sync endpoint
Route Pattern
fastify.post('/derbies', async (request, reply) => {
const body = CreateDerbySchema.parse(request.body);
const derby = await db.insert(derbies).values({ ...body }).returning();
return derby[0];
});
Sync Endpoint
The /sync endpoint handles offline-first reconciliation.
Request
{
clientId: string,
lastSyncedAt?: string, // ISO timestamp
outbox: SyncOutboxItem[] // Pending operations
}
Response
{
serverTime: string,
appliedOperationIds: string[], // Successfully processed
patches: {
users: User[],
derbies: Derby[],
derbyParticipants: DerbyParticipant[],
catches: Catch[],
chatMessages: ChatMessage[]
}
}
Processing Logic
- For each outbox item, apply the operation (create/update/delete)
- Use last-write-wins by
updatedAtfor conflicts - Return all entities modified since
lastSyncedAt - Return list of successfully applied operation IDs
Drizzle Patterns
Queries
import { db } from './db';
import { derbies, catches } from './db/schema';
import { eq, gte } from 'drizzle-orm';
// Select
const derby = await db.select().from(derbies).where(eq(derbies.id, id));
// Insert
await db.insert(catches).values({ ...data });
// Update
await db.update(catches).set({ species: 'Bass' }).where(eq(catches.id, id));
// Soft delete
await db.update(catches).set({ deletedAt: new Date() }).where(eq(catches.id, id));
Migrations
# Generate migration from schema changes
npx drizzle-kit generate
# Run migrations
npm run migrate
Common Tasks
Add a new entity
- Add Zod schema to
packages/shared-types - Add table to
src/db/schema.ts - Generate and run migration
- Add CRUD routes in
src/index.ts - Update sync handler in
src/sync.ts
Add a new route
- Define request/response schemas (use Zod)
- Add route in
src/index.ts - Validate input with
.parse() - Use Drizzle for DB operations
- Return typed response
Environment
Required env vars (see .env.example):
DATABASE_URL=postgres://...
PORT=3000
Testing
- Tests in
test/server.test.ts - Use in-memory or test database
- Test both happy paths and error cases
Score
Total Score
50/100
Based on repository quality metrics
✓SKILL.md
SKILL.mdファイルが含まれている
+20
○LICENSE
ライセンスが設定されている
0/10
○説明文
100文字以上の説明がある
0/10
○人気
GitHub Stars 100以上
0/15
○最近の活動
3ヶ月以内に更新がある
0/10
○フォーク
10回以上フォークされている
0/5
✓Issue管理
オープンIssueが50未満
+5
✓言語
プログラミング言語が設定されている
+5
○タグ
1つ以上のタグが設定されている
0/5
Reviews
💬
Reviews coming soon