一键导入
backend-patterns
API design, database operations, error handling, and backend architecture patterns. Use when creating APIs, database schemas, or server-side logic.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
API design, database operations, error handling, and backend architecture patterns. Use when creating APIs, database schemas, or server-side logic.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Event-driven coordination for teamwork orchestrators. Replaces polling-based monitoring loop with TeammateIdle and TaskCompleted hooks.
Hybrid task decomposition strategy for teamwork projects. Covers plan-based and semantic decomposition, granularity rules, role assignment, dependency management, and complexity assessment.
Reset and clean up teamwork project state. Use when project is stuck, failed, or needs a fresh start. Uses native TeamDelete API for cleanup.
8-phase task execution lifecycle for teamwork workers using native Claude Code APIs. Covers task discovery, claiming, structured description parsing, TDD workflow, implementation, verification, selective commit, and completion reporting.
Architecture design review skill for reviewer agents. Component boundaries, separation of concerns, modularity, and design pattern validation. Use during PLANNING phase to validate design decisions and VERIFICATION to ensure adherence.
Code quality verification skill for reviewer agents. Provides checklists for logic clarity, error handling, maintainability, and performance. Use during VERIFICATION phase to assess implementation quality.
| name | backend-patterns |
| description | API design, database operations, error handling, and backend architecture patterns. Use when creating APIs, database schemas, or server-side logic. |
| user-invocable | false |
Comprehensive patterns for API design, database operations, error handling, and backend architecture.
// app/api/users/route.ts
export async function GET(request: Request) {
const { searchParams } = new URL(request.url)
const page = parseInt(searchParams.get('page') || '1')
const limit = parseInt(searchParams.get('limit') || '10')
const users = await db.users.findMany({
skip: (page - 1) * limit,
take: limit
})
return NextResponse.json({
success: true,
data: users,
pagination: {
page,
limit,
total: await db.users.count()
}
})
}
export async function POST(request: Request) {
const body = await request.json()
// Validate input
const validated = CreateUserSchema.parse(body)
const user = await db.users.create({ data: validated })
return NextResponse.json(
{ success: true, data: user },
{ status: 201 }
)
}
// app/api/users/[id]/route.ts
export async function GET(
request: Request,
{ params }: { params: { id: string } }
) {
const user = await db.users.findUnique({
where: { id: params.id }
})
if (!user) {
return NextResponse.json(
{ error: 'User not found' },
{ status: 404 }
)
}
return NextResponse.json({ success: true, data: user })
}
export async function PATCH(
request: Request,
{ params }: { params: { id: string } }
) {
const body = await request.json()
const validated = UpdateUserSchema.parse(body)
const user = await db.users.update({
where: { id: params.id },
data: validated
})
return NextResponse.json({ success: true, data: user })
}
export async function DELETE(
request: Request,
{ params }: { params: { id: string } }
) {
await db.users.delete({ where: { id: params.id } })
return NextResponse.json({ success: true }, { status: 204 })
}
type ApiResponse<T> = {
success: true
data: T
meta?: {
pagination?: {
page: number
limit: number
total: number
}
}
} | {
success: false
error: string
details?: any
}
// Success response helper
function successResponse<T>(data: T, meta?: any): Response {
return NextResponse.json({ success: true, data, meta })
}
// Error response helper
function errorResponse(error: string, status: number, details?: any): Response {
return NextResponse.json(
{ success: false, error, details },
{ status }
)
}
// Usage
export async function GET(request: Request) {
try {
const users = await db.users.findMany()
return successResponse(users)
} catch (error) {
return errorResponse('Failed to fetch users', 500)
}
}
// app/api/v1/users/route.ts
export async function GET(request: Request) {
// Version 1 implementation
return NextResponse.json({ version: 1, users: [] })
}
// app/api/v2/users/route.ts
export async function GET(request: Request) {
// Version 2 implementation with breaking changes
return NextResponse.json({ version: 2, data: { users: [] } })
}
import { z } from 'zod'
const CreateMarketSchema = z.object({
name: z.string().min(3).max(100),
description: z.string().min(10).max(1000),
category: z.enum(['politics', 'sports', 'entertainment', 'crypto']),
endDate: z.string().datetime(),
options: z.array(z.object({
name: z.string().min(1).max(50),
probability: z.number().min(0).max(1).optional()
})).min(2).max(10)
})
export async function POST(request: Request) {
try {
const body = await request.json()
const validated = CreateMarketSchema.parse(body)
// Safe to use validated data
const market = await db.markets.create({ data: validated })
return NextResponse.json({ success: true, data: market })
} catch (error) {
if (error instanceof z.ZodError) {
return NextResponse.json(
{
success: false,
error: 'Validation failed',
details: error.errors
},
{ status: 400 }
)
}
return NextResponse.json(
{ success: false, error: 'Internal server error' },
{ status: 500 }
)
}
}
// Prisma schema example
model User {
id String @id @default(cuid())
email String @unique
name String
passwordHash String
role Role @default(USER)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
posts Post[]
comments Comment[]
@@index([email])
@@index([createdAt])
}
model Post {
id String @id @default(cuid())
title String
content String @db.Text
published Boolean @default(false)
authorId String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
comments Comment[]
tags Tag[]
@@index([authorId])
@@index([published])
@@index([createdAt])
@@fulltext([title, content]) // For search
}
model Comment {
id String @id @default(cuid())
content String @db.Text
postId String
authorId String
createdAt DateTime @default(now())
post Post @relation(fields: [postId], references: [id], onDelete: Cascade)
author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
@@index([postId])
@@index([authorId])
}
enum Role {
USER
MODERATOR
ADMIN
}
// ❌ WRONG: No transaction (race condition)
export async function transferFunds(fromId: string, toId: string, amount: number) {
const sender = await db.accounts.findUnique({ where: { id: fromId } })
if (sender.balance < amount) {
throw new Error('Insufficient funds')
}
// Race condition: balance could change between these operations
await db.accounts.update({
where: { id: fromId },
data: { balance: sender.balance - amount }
})
await db.accounts.update({
where: { id: toId },
data: { balance: { increment: amount } }
})
}
// ✅ CORRECT: Use transactions
export async function transferFunds(fromId: string, toId: string, amount: number) {
await db.$transaction(async (tx) => {
// Lock sender account for update
const sender = await tx.accounts.findUnique({
where: { id: fromId }
})
if (!sender || sender.balance < amount) {
throw new Error('Insufficient funds')
}
// Atomic updates
await tx.accounts.update({
where: { id: fromId },
data: { balance: { decrement: amount } }
})
await tx.accounts.update({
where: { id: toId },
data: { balance: { increment: amount } }
})
// Create transaction record
await tx.transactions.create({
data: {
fromId,
toId,
amount,
status: 'completed'
}
})
})
}
// ❌ WRONG: N+1 query problem
const users = await db.users.findMany()
for (const user of users) {
user.posts = await db.posts.findMany({
where: { authorId: user.id }
})
}
// ✅ CORRECT: Use includes/eager loading
const users = await db.users.findMany({
include: {
posts: true
}
})
// ✅ CORRECT: Select only needed fields
const users = await db.users.findMany({
select: {
id: true,
name: true,
email: true,
posts: {
select: {
id: true,
title: true,
createdAt: true
}
}
}
})
// ✅ CORRECT: Pagination for large datasets
const posts = await db.posts.findMany({
take: 20,
skip: (page - 1) * 20,
orderBy: { createdAt: 'desc' }
})
import { createClient } from '@supabase/supabase-js'
const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.SUPABASE_SERVICE_ROLE_KEY! // Use service key for server-side
)
// Basic CRUD operations
export async function getMarkets() {
const { data, error } = await supabase
.from('markets')
.select('*')
.order('created_at', { ascending: false })
if (error) throw error
return data
}
export async function getMarketById(id: string) {
const { data, error } = await supabase
.from('markets')
.select(`
*,
creator:users(id, name, avatar),
bets(id, amount, outcome)
`)
.eq('id', id)
.single()
if (error) throw error
return data
}
export async function createMarket(market: MarketInput) {
const { data, error } = await supabase
.from('markets')
.insert(market)
.select()
.single()
if (error) throw error
return data
}
// Full-text search
export async function searchMarkets(query: string) {
const { data, error } = await supabase
.from('markets')
.select('*')
.textSearch('fts', query, {
type: 'websearch',
config: 'english'
})
if (error) throw error
return data
}
// Define error hierarchy
export class AppError extends Error {
constructor(
public statusCode: number,
public message: string,
public code?: string,
public details?: any
) {
super(message)
this.name = this.constructor.name
}
}
export class ValidationError extends AppError {
constructor(message: string, details?: any) {
super(400, message, 'VALIDATION_ERROR', details)
}
}
export class NotFoundError extends AppError {
constructor(resource: string, id: string) {
super(404, `${resource} not found: ${id}`, 'NOT_FOUND')
}
}
export class UnauthorizedError extends AppError {
constructor(message = 'Unauthorized') {
super(401, message, 'UNAUTHORIZED')
}
}
export class ForbiddenError extends AppError {
constructor(message = 'Forbidden') {
super(403, message, 'FORBIDDEN')
}
}
// Usage
export async function getUser(id: string) {
const user = await db.users.findUnique({ where: { id } })
if (!user) {
throw new NotFoundError('User', id)
}
return user
}
// lib/error-handler.ts
export function handleApiError(error: unknown): Response {
// Log error for monitoring
console.error('API Error:', error)
// Handle known errors
if (error instanceof AppError) {
return NextResponse.json(
{
success: false,
error: error.message,
code: error.code,
details: error.details
},
{ status: error.statusCode }
)
}
// Handle Zod validation errors
if (error instanceof z.ZodError) {
return NextResponse.json(
{
success: false,
error: 'Validation failed',
code: 'VALIDATION_ERROR',
details: error.errors
},
{ status: 400 }
)
}
// Handle database errors
if (error instanceof Prisma.PrismaClientKnownRequestError) {
if (error.code === 'P2002') {
return NextResponse.json(
{ success: false, error: 'Duplicate entry', code: 'DUPLICATE' },
{ status: 409 }
)
}
}
// Generic error (don't expose internals)
return NextResponse.json(
{
success: false,
error: 'Internal server error',
code: 'INTERNAL_ERROR'
},
{ status: 500 }
)
}
// Usage in API route
export async function GET(request: Request) {
try {
const data = await fetchData()
return NextResponse.json({ success: true, data })
} catch (error) {
return handleApiError(error)
}
}
export async function searchMarketsWithFallback(query: string) {
try {
// Try semantic search with Redis
const results = await searchVectorDatabase(query)
return { results, method: 'semantic' }
} catch (error) {
console.warn('Vector search failed, falling back to full-text:', error)
try {
// Fallback to Supabase full-text search
const results = await supabase
.from('markets')
.select('*')
.textSearch('fts', query)
return { results: results.data, method: 'fulltext' }
} catch (fallbackError) {
console.error('Full-text search failed, using substring:', fallbackError)
// Last resort: substring match
const results = await supabase
.from('markets')
.select('*')
.ilike('name', `%${query}%`)
return { results: results.data, method: 'substring' }
}
}
}
import Redis from 'ioredis'
const redis = new Redis(process.env.REDIS_URL!)
// Cache-aside pattern
export async function getMarketWithCache(id: string) {
const cacheKey = `market:${id}`
// Try cache first
const cached = await redis.get(cacheKey)
if (cached) {
return JSON.parse(cached)
}
// Fetch from database
const market = await db.markets.findUnique({ where: { id } })
if (market) {
// Cache for 5 minutes
await redis.setex(cacheKey, 300, JSON.stringify(market))
}
return market
}
// Cache invalidation
export async function updateMarket(id: string, data: any) {
const market = await db.markets.update({
where: { id },
data
})
// Invalidate cache
await redis.del(`market:${id}`)
return market
}
// Bulk cache invalidation
export async function invalidateMarketCaches(ids: string[]) {
const keys = ids.map(id => `market:${id}`)
if (keys.length > 0) {
await redis.del(...keys)
}
}
import { LRUCache } from 'lru-cache'
// Cache with automatic eviction
const cache = new LRUCache<string, any>({
max: 500, // Max 500 items
ttl: 1000 * 60 * 5, // 5 minutes TTL
updateAgeOnGet: true,
updateAgeOnHas: false
})
export async function getCachedData(key: string, fetcher: () => Promise<any>) {
// Check cache
const cached = cache.get(key)
if (cached) return cached
// Fetch and cache
const data = await fetcher()
cache.set(key, data)
return data
}
import { Queue, Worker } from 'bullmq'
// Define job queue
const emailQueue = new Queue('email', {
connection: {
host: process.env.REDIS_HOST,
port: parseInt(process.env.REDIS_PORT!)
}
})
// Add job to queue
export async function sendWelcomeEmail(userId: string, email: string) {
await emailQueue.add('welcome', {
userId,
email
}, {
attempts: 3,
backoff: {
type: 'exponential',
delay: 2000
}
})
}
// Process jobs
const emailWorker = new Worker('email', async (job) => {
const { userId, email } = job.data
switch (job.name) {
case 'welcome':
await sendEmail({
to: email,
subject: 'Welcome!',
template: 'welcome',
data: { userId }
})
break
default:
throw new Error(`Unknown job: ${job.name}`)
}
}, {
connection: {
host: process.env.REDIS_HOST,
port: parseInt(process.env.REDIS_PORT!)
}
})
emailWorker.on('failed', (job, error) => {
console.error(`Job ${job?.id} failed:`, error)
})
// Authentication middleware
export async function withAuth(handler: Function) {
return async (request: Request, ...args: any[]) => {
const token = request.cookies.get('token')?.value
if (!token) {
return NextResponse.json(
{ error: 'Unauthorized' },
{ status: 401 }
)
}
const user = await verifyToken(token)
if (!user) {
return NextResponse.json(
{ error: 'Invalid token' },
{ status: 401 }
)
}
// Add user to request context
request.user = user
return handler(request, ...args)
}
}
// Usage
export const GET = withAuth(async (request: Request) => {
const userId = request.user.id
// Handle request
})