Backend Debugging
Step 0 — If it's a regression, check git history first
When the user reports "it worked before X" / "broke after the refactoring" / "used to work" — read the git history of the affected code path before proposing any fix. A defensive try/catch or null check is the wrong opening move on a regression; the bug is almost always something a recent commit dropped (a break, a return, a case arm, a relation in a query).
# Find the commits that touched the failing handler/file:
git log --oneline -20 -- <path/to/handler.ts>
# Diff against the last-known-good state:
git show <commit>:<path/to/handler.ts>
git log -p -5 -- <path/to/handler.ts>
The fix is then "restore what was lost," not "patch around the symptom." Only after you have the diff against the working version should you consider defensive code.
Step 1 — Read the logs
Before reading code, guessing, or querying the database, check the backend logs:
# From the repo root (slot is remembered from ./dev up):
./dev logs backend # Last 80 lines
./dev logs --tail 200 backend # More context
# Or read the log file directly:
cat .dev/slot-$(cat .dev/slot)/backend.log
The logs contain full stack traces with file names and line numbers. This tells you exactly what's broken — no guessing needed.
Do not skip this step. Code review without the actual error is guesswork.
Step 2 — Reproduce the error
Confirm the error independently with curl. This isolates whether the problem is backend vs. frontend vs. CORS:
# Login first (adjust credentials as needed):
curl -s -c /tmp/cookies.txt http://localhost:3020/api/auth/login \
-X POST -H 'Content-Type: application/json' \
-d '{"email":"...","password":"..."}'
# Hit the failing endpoint:
curl -s -b /tmp/cookies.txt "http://localhost:3020/api/..." | head -50
Replace port 3020 with whatever the current slot uses. Check with ./dev status.
Recognizing CORS errors
If the browser console shows "blocked by CORS policy" but the request returns a valid status code (e.g., 201), the backend works — the browser is rejecting the response. Look for:
- Hardcoded
Access-Control-Allow-Origin headers in the controller that override the global CORS middleware
- The global CORS config in
src/main.ts — in development mode (NODE_ENV !== 'production') it should allow all origins
Step 3 — Go to the error location
The stack trace gives you the exact file and line. Read that code. Common patterns:
"Cannot read properties of undefined (reading 'map')"
A relation wasn't loaded by TypeORM but the mapper assumes it's always present. Fix with optional chaining:
// Before — crashes when relation not loaded:
items.map(x => ...)
// After:
items?.map(x => ...) ?? []
This is especially common when:
- A
findAll query doesn't load the same relations as findOne
- Eager relations don't cascade through deeply nested joins (e.g.,
thread → sourceAssignments → source → details → contentChunks)
"Invalid source type" / "Invalid message role"
A mapper's instanceof or switch doesn't cover all cases. Check what the database actually contains:
# Quick database query through the dev stack:
cd ayunis-core-backend
pnpm exec ts-node -r tsconfig-paths/register -e "
import './src/config/env';
import { DataSource } from 'typeorm';
// ... query the relevant table
"
Step 4 — Fix, verify, check logs again
- Make the fix
- Wait for
nest --watch to reload (or check ./dev logs backend for compilation errors)
- Re-run the curl command from Step 2
- Check
./dev logs backend to confirm no new errors
- Load
nestjs-hexagonal-backend and run its validation sequence at the level required by the repository's Proportional Workflow.