Use when you need to review server-side implementation for correctness, maintainability, validation, and observability.
How this skill is triggered — by the user, by Claude, or both
Slash command
/skillry-backend-and-api:13-backend-implementation-reviewThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Perform a structured review of server-side code for the correctness problems that ordinary code review most often misses: missing input validation, broken transaction boundaries, race conditions in concurrent writes, non-idempotent mutation endpoints, insecure direct object references, and N+1 query patterns that become correctness problems under load. This goes beyond style — it hunts for code...
Perform a structured review of server-side code for the correctness problems that ordinary code review most often misses: missing input validation, broken transaction boundaries, race conditions in concurrent writes, non-idempotent mutation endpoints, insecure direct object references, and N+1 query patterns that become correctness problems under load. This goes beyond style — it hunts for code that fails silently or incorrectly under real-world data and concurrency, and rates each finding by severity with a concrete fix.
"0" treated as falsy), length/range checks (unbounded strings into fixed-length columns), enum validation (a role accepting any string), and presence checks (optional fields read without a null guard). Validation must live in the handler/service, not only as a DB constraint.await db.insert(order); await email.send(...) where a failed email orphans the order.INSERT without ON CONFLICT/idempotency-key checks, charges without an existing-charge check, and delta UPDATEs (balance = balance + amount) with no dedup guard.SELECT count; if count < limit: INSERT) without a row lock or SELECT ... FOR UPDATE. For optimistic locking (UPDATE ... WHERE version = $1), confirm the 0-rows-updated case is handled.console.log-only catch blocks; no fire-and-forget async (missing await); Promise.allSettled rejections explicitly inspected; background jobs log/alert on failure rather than retrying silently.for (user of users) { await db.query('... WHERE user_id=$1', [user.id]) }) — a performance problem that becomes a timeout/OOM correctness problem at scale.req.user.isAdmin check does not stop a non-admin reading another user's record. Confirm queries filter by the authenticated tenant/org/owner, not only by the ID from the URL.SELECT * FROM orders WHERE id=$1 is vulnerable without AND org_id=$2.SELECT FOR UPDATE or optimistic locking with conflict handling.Promise.allSettled results silently ignored.# Locate write paths and their handlers
rg -n "INSERT INTO|UPDATE .* SET|\.create\(|\.update\(|\.delete\(" src/
# Side effect immediately after a write (transaction-boundary smell)
rg -nU "(insert|create|save)\([^)]*\)[\s\S]{0,120}(sendEmail|publish|enqueue|fetch|axios)" src/
# Fire-and-forget async (missing await) and swallowed errors
rg -n "^\s*[a-zA-Z].*\b(async|Promise)\b" src/ | rg -v "await"
rg -nU "catch\s*\([^)]*\)\s*\{\s*(\}|//|console\.log)" src/
# Idempotency: inserts without ON CONFLICT / upsert
rg -n "INSERT INTO" src/ | rg -v "ON CONFLICT"
# Race conditions: read-then-write without a lock
rg -n "SELECT .* FROM" src/ | rg -v "FOR UPDATE" ; rg -n "FOR UPDATE" src/
# N+1: a query inside a loop / map
rg -nU "(for\s*\(|\.map\(|\.forEach\()[\s\S]{0,200}(query|findUnique|findFirst|SELECT)" src/
# IDOR: lookups by request id with no tenant/owner filter
rg -n "WHERE id\s*=\s*\$1" src/ | rg -v "org_id|tenant_id|user_id"
# Framework-specific validation coverage
rg -n "z\.object\(|Joi\.|class-validator|@IsString|pydantic|BaseModel" src/ # which validator, where
rg -n "router\.(post|put|patch|delete)\(" src/ | wc -l # mutation endpoint count
# Cross-reference: mutation routes that have NO validator import in the file
for f in $(rg -l "router\.(post|put|patch)\(" src/); do
rg -q "z\.|Joi\.|@Is[A-Z]|validateBody" "$f" || echo "NO VALIDATION: $f"
done
# Transactions: do multi-write services actually open one?
rg -n "BEGIN|transaction\(|\$transaction\(|db\.transaction|with_for_update" src/
# Bulk endpoints and their partial-failure handling
rg -nU "(forEach|for .* of|map)\([\s\S]{0,200}(insert|update|create)" src/ | head
# Mass-assignment: whole request body spread into a write
rg -n "\.\.\.req\.body|update\([^,]*,\s*req\.body|create\(\{\s*data:\s*req\.body" src/
# Money/total trusted from the client instead of recomputed server-side
rg -n "req\.body\.(total|amount|price|subtotal|quantity\s*\*)" src/
# Optimistic-lock conflicts: is the 0-rows-updated case handled?
rg -nU "WHERE .*version\s*=[\s\S]{0,120}(rowCount|affected|count)" src/ || echo "version check result may be ignored"
// WRONG: side effect before commit — a failed email orphans the order
const order = await db.insert(orders).values(input);
await mailer.sendReceipt(order.id); // throws -> order persisted, no receipt sent, no rollback
// RIGHT: all writes in one transaction; side effect after commit
const order = await db.transaction(async (tx) => {
const o = await tx.insert(orders).values(input);
await tx.insert(orderLines).values(lines(o.id));
return o;
});
await mailer.sendReceipt(order.id); // commit already durable; safe to retry the email
// WRONG: read-then-write race — two requests both pass the check
const count = await db.count(seats, { eventId });
if (count < capacity) await db.insert(seats).values({ eventId, userId });
// RIGHT: let the database enforce it atomically
await db.insert(seats).values({ eventId, userId })
.onConflictDoNothing(); // + a partial unique index / capacity constraint
NOT NULL/CHECK means bad input surfaces as a cryptic 500 instead of a clean 400.deleted_at = NOW() without handling foreign-key references means related rows still appear in joins to the "deleted" parent.db.update(user, req.body)) lets a caller set isAdmin or balance. Whitelist the writable fields.req.body.totalPrice lets the client pay any amount. Recompute money on the server.| endpoint | category | severity | file:line | fix |
|---------------------|-----------------|----------|----------------------|--------------------------------------|
| POST /orders | transaction | high | services/order.ts:42 | wrap writes in tx; email after commit|
| GET /orders/:id | IDOR | critical | routes/order.ts:18 | add AND org_id = $ctx.orgId |
| POST /charges | idempotency | critical | services/pay.ts:60 | check existing charge by key first |
| GET /users | N+1 | medium | routes/user.ts:30 | batch with WHERE id IN (...) |
Report must include: endpoints reviewed; input-validation gaps (fields lacking validation + fix); transaction-boundary issues (locations where atomicity breaks); idempotency gaps (unsafe-to-retry endpoints + fix); race conditions (read-then-write without locking); authorization gaps (queries missing ownership filters); N+1 patterns (loop locations + batch-query fix); and a severity rating per finding (critical / high / medium / low).
Done means every reviewed endpoint's write path is traced, each of the validation/transaction/idempotency/race/authorization/N+1 categories is confirmed clean or reported with a file:line and a concrete fix, every finding carries a severity, and any breach-vector finding has been surfaced ahead of the rest.
npx claudepluginhub fluxonlab/skillry --plugin skillry-backend-and-apiProvides CDSS development patterns for drug interaction checking, dose validation, clinical scoring (NEWS2, qSOFA), and alert classification integrated into EMR workflows.