| Code | Meaning | Tell user |
|---|---|---|
| 200 | OK | It worked |
| 201 | Created | New resource made |
| 400 | Bad request | Sent invalid data |
| 401 | Unauthorized | Need login |
| 403 | Forbidden | Logged in but not allowed |
| 404 | Not found | Wrong URL or missing id |
| 500 | Server error | Bug on server — check logs |
- Keys in double quotes
- No trailing commas (strict JSON)
JSON.stringify(obj)→ string for wireJSON.parse(string)→ object in code
app.get('/api/todos', (req, res) => {
res.json(todos);
});
app.post('/api/todos', (req, res) => {
const { text } = req.body;
const todo = { id: crypto.randomUUID(), text };
todos.push(todo);
res.status(201).json(todo);
});Adapt paths to Next.js route handlers, Fastify, etc. — teach pattern not framework wars.
// app/api/todos/route.js
export async function GET() {
return Response.json(todos);
}
export async function POST(request) {
const { text } = await request.json();
// ...
return Response.json(todo, { status: 201 });
}# .env.example (committed)
DATABASE_URL=
API_SECRET=
# .env (gitignored)
DATABASE_URL=postgres://...Teach: example documents keys; real values local only.
Your page runs on
http://localhost:5173. API runs onhttp://localhost:3000. Different ports = different origins. Browser blocks the response unless the API saysAccess-Control-Allow-Originfor your page (or you proxy through same origin).
| Need | Pick |
|---|---|
| Solo demo, one browser | localStorage |
| Share link with friends' data | API + deploy |
| Accounts | API + auth |
curl -s http://localhost:3000/api/todos | jq
curl -s -X POST http://localhost:3000/api/todos \
-H 'Content-Type: application/json' \
-d '{"text":"Learn APIs"}'Shows API without browser UI.
- Validate input on server (never trust client)
- Rate limit public APIs in production
- Hash passwords; never store plain text
- HTTPS in production (deploy skill)