About backend-patterns
backend-patterns is a skill for backend developers that helps select and implement server-side patterns in light of an existing architecture. It covers request handling, databases, caching, background jobs, and distributed systems, with an emphasis on validation and safety.
Key features
- 01Inspect existing architecture and implementations first
- 02Provide Server Actions and tRPC patterns
- 03Cover databases, caching, and background jobs
- 04Describe rate limiting and distributed-system patterns
- 05Validate authentication, inputs, errors, and idempotency
Use cases
- 01Design safe write flows for Next.js Server Actions
- 02Move retryable order side effects into background jobs
- 03Handle concurrent updates or soft deletes in databases
- 04Add rate limiting and input validation to sensitive endpoints
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ kensaurus/ skills --skill backend-patternsAfter installing, type/backend-patternsin Claude Code to confirm it loaded.
Bundled files
References and scripts installed along with SKILL.md.
references/architecture-patterns.mddata-and-jobs.mdrequest-patterns.mdDetails
From the project READMEBackend Patterns Skill
Degree of freedom: MIXED. Which pattern to apply [HIGH freedom];
existing-architecture probes and the validation list [LOW freedom — run exactly].
How to reason
- Observe — existing server/api/actions, ORM, queues, cache
- Interpret — missing pattern vs duplicate vs wrong layer
- Classify — reuse / add-queue / add-cache / add-rate-limit / edge-fn
- Severity — unauthenticated mutation outranks a missing cache
Worked example
Observe:
createOrdersends email inline; no Inngest/queue; checkout p95 4s. Interpret: confirmation is post-response work, not request-path. Classify:after()or anorder/createdjob; keep the write transactional. Verify: order row commits; email/inventory run after response; retries do not double-charge.
Self-critique before reporting
- Existing first — searched server/api/actions before adding a second pattern
- Auth + validate — every mutation checks session and Zod
- Idempotent — retries on the new path do not double-apply
- Right owner — which architecture to pick →
audit-backend-architecture
Design scalable, maintainable backend architectures using modern patterns and best practices.
Code examples lean on Next.js App Router + Supabase/Prisma. The patterns are stack-agnostic — adapt ORMs, client libraries, and deploy targets to your detected ecosystem.
Check existing first [LOW freedom — run exactly]
Before implementing ANY backend pattern, verify:
- Check existing architecture:
ls -la src/server/ src/api/ app/api/ supabase/functions/ 2>/dev/null
cat package.json | grep -i "prisma\|drizzle\|supabase\|trpc"
- Check existing patterns:
rg "createTRPCRouter|publicProcedure" --type ts -l
rg "'use server'" --type ts -l
ls -la supabase/migrations/*.sql 2>/dev/null | tail -5
- Check database setup:
cat prisma/schema.prisma 2>/dev/null | head -50
cat supabase/config.toml 2>/dev/null
Why: Backend changes have wide impact. Understand existing architecture first.
Server Actions (Next.js 16+) [HIGH freedom]
Next.js 16: Turbopack default; 'use cache' + cacheComponents; reactCompiler: true; middleware.ts → proxy.ts (grep both). Instant navigations → enhance-web-instant-nav.
- Order inside every action: auth check → Zod
safeParse→ execute → map known errors (PrismaP2002) → generic failure message. - Return
ActionResult<T>:{ success: true, data }or{ success: false, error, fieldErrors? }; never throw to the client. revalidatePathafter a successful write.- Post-response work (confirmation email, inventory, notifications) goes in
after()fromnext/server, keeping the write transactional.
'use server'
import { after } from 'next/server'
export async function createOrder(formData: FormData) {
const order = await db.order.create({ data: { /* ... */ } })
after(async () => { // runs after the response is sent
await sendOrderConfirmation(order.id)
await updateInventory(order.items)
})
revalidatePath('/orders')
return { success: true, data: order }
}
Full createUser action and the after() example: references/request-patterns.md §Server Actions.
tRPC Setup [HIGH freedom]
publicProcedurefor reads anyone may do;protectedProcedurefor anything that usesctx.session.- Every procedure has a Zod
.input(); mutations stampcreatedByIdfrom the session, not the input. - List endpoints paginate by cursor: fetch
limit + 1, pop the extra row asnextCursor.
Full usersRouter (getById, create, cursor-paginated list): references/request-patterns.md §tRPC router definition.
Supabase Edge Functions [HIGH freedom]
Deno.serve; answerOPTIONSwith the CORS headers first.- Verify the webhook signature against the raw body before parsing JSON;
401on mismatch. - Admin client from
SUPABASE_SERVICE_ROLE_KEYbypasses RLS; use it only inside the function. - Catch everything; log server-side, return a generic
500JSON body with CORS headers.
Full process-webhook function: references/request-patterns.md §Supabase Edge Function.
Database Patterns [HIGH freedom]
- Optimistic locking —
version INTcolumn;UPDATE ... WHERE id = $1 AND version = $2and treat 0 rows as a concurrent update. - Soft deletes — nullable
deletedAtwith an index; every read filtersdeletedAt: null; delete sets the timestamp. - Audit logging —
audit_logs(table_name, record_id, action, old_data, new_data, user_id)filled by aSECURITY DEFINERtrigger usingTG_OP,row_to_json,auth.uid().
UPDATE orders SET status = 'shipped', version = version + 1
WHERE id = $1 AND version = $2; -- 0 rows → concurrent update, retry or surface conflict
Soft-delete Prisma model and the full audit trigger: references/data-and-jobs.md §Soft deletes, §Audit logging.
Caching Patterns [HIGH freedom]
- Next.js:
fetch(url, { next: { revalidate, tags } });revalidateTagon demand;unstable_cachearound DB queries with tags. - Redis (Upstash): one
getCachedData(key, fetcher, ttl)helper, cache-aside; key by entity and id (user:${id}). - Pick TTL by staleness tolerance, and always tag so a write can bust the cache.
const getCachedUser = unstable_cache(
async (id: string) => db.user.findUnique({ where: { id } }),
['user'],
{ revalidate: 3600, tags: ['users'] }
)
Next.js cache calls and the Redis helper: references/data-and-jobs.md §Next.js cache, §Redis caching.
Background Jobs [HIGH freedom]
- Default: Inngest.
inngest.createFunction({ id }, { event }, async ({ event, step }) => ...); each side effect in its ownstep.runso retries resume, not restart. - Trigger with
inngest.send({ name: 'order/created', data })from the Server Action after the write commits. - Early exit (out of stock) is a
step.run+return { status: 'cancelled' }, not a throw.
export const processOrder = inngest.createFunction(
{ id: 'process-order' }, { event: 'order/created' },
async ({ event, step }) => {
const payment = await step.run('charge-payment', () => chargeCustomer(event.data.paymentMethod))
await step.run('send-confirmation', () => sendOrderConfirmation(event.data.orderId))
return { status: 'completed', paymentId: payment.id }
}
)
Full Inngest function and the Trigger.dev scheduled-task alternative: references/data-and-jobs.md §Background jobs, §Alternatives.
Rate Limiting [HIGH freedom]
@upstash/ratelimitwithRatelimit.slidingWindow(10, '10 s')overRedis.fromEnv().- Key by user id (or IP for anonymous); on
!successreturnToo many requestswithretryAfterseconds fromreset.
Full rateLimitedAction: references/request-patterns.md §Rate limiting.
Architecture patterns (distributed systems) [HIGH freedom]
Gateway, BFF, bulkhead, circuit breaker, outbox+CDC, saga, hexagonal, ACL, and strangler-fig →
references/architecture-patterns.md. Pick the pattern for the
topology (no mesh on a monolith; no CQRS unless reads/writes diverge). Timeouts/retries/idempotency
→ audit-resilience; structural gap report → audit-backend-architecture.
Validation [LOW freedom — do not skip]
After implementing backend patterns:
- Error handling → All errors caught, logged, safe response returned
- Auth checks → Every mutation verifies authentication
- Input validation → Zod schema on all inputs
- Rate limiting → Sensitive endpoints protected
- Idempotency → Critical operations handle retries
- Logging → Structured logs without sensitive data
- Testing → Unit tests for business logic, integration for APIs