contentintech
Intermediate

Backend & REST APIs Cheatsheet

Quick reference for backend REST APIs — HTTP methods, status codes, Express middleware patterns, validation, pagination, auth headers, CORS, rate limiting, and REST vs GraphQL.

RESTHTTPExpressAPI Design
NotesCheatsheet

HTTP Methods

Method Use Safe Idempotent
GETRead✓✓
POSTCreate✗✗
PUTReplace✗✓
PATCHPartial update✗~
DELETERemove✗✓

Status Codes

CodeMeaning
200OK — GET/PUT/PATCH success
201Created — POST success
204No Content — DELETE success
400Bad Request — malformed
401Unauthorized — no/invalid auth
403Forbidden — no permission
404Not Found
409Conflict — duplicate/version
422Unprocessable — invalid data
429Too Many Requests
500Internal Server Error
503Service Unavailable

REST URL Design

GET    /v1/users            # list
POST   /v1/users            # create
GET    /v1/users/42         # read one
PUT    /v1/users/42         # full replace
PATCH  /v1/users/42         # partial update
DELETE /v1/users/42         # delete
GET    /v1/users/42/orders  # nested resource

# plural nouns, no verbs. version in path.
# GET /getUser  ✗      GET /v1/users/42  ✓

Express Essentials

Server + Route

import express from 'express';
const app = express();
app.use(express.json());

app.get('/v1/users/:id', async (req, res, next) => {
  try {
    const u = await db.users.find(req.params.id);
    if (!u) return res.status(404).json({ error: 'not found' });
    res.json(u);
  } catch (e) { next(e); }
});

app.post('/v1/users', async (req, res, next) => {
  try {
    const c = await db.users.create(req.body);
    res.status(201).json(c);
  } catch (e) { next(e); }
});

app.listen(3000);

Middleware & Error Handler

// middleware: (req, res, next)
app.use((req, res, next) => { console.log(req.path); next(); });

// error handler: 4 args, registered LAST
app.use((err, req, res, next) => {
  const status = err.status ?? 500;
  if (status >= 500) console.error(err);
  res.status(status).json({ error: err.publicMessage ?? 'error' });
});

Validation (Zod)

import { z } from 'zod';
const schema = z.object({
  email: z.string().email(),
  age: z.number().int().min(0),
});

const r = schema.safeParse(req.body);
if (!r.success) return res.status(422).json({ errors: r.error.issues });
req.body = r.data;   // typed, sanitized

Pagination & Filtering

# query params
GET /v1/orders?status=paid&sort=-createdAt&limit=20&cursor=abc

const limit = Math.min(Number(req.query.limit) || 20, 100);
res.json({ data: items, nextCursor });   // cursor > offset for big data

Auth, CORS, Rate Limit

Bearer Auth

// Authorization: Bearer <token>
const token = (req.get('authorization') || '').replace('Bearer ', '');
if (!token) return res.status(401).json({ error: 'missing token' });
req.user = verifyJwt(token);           // 401 if invalid
// authorize: 403 if authenticated but not allowed
if (!req.user.roles.includes('admin')) return res.sendStatus(403);

CORS

import cors from 'cors';
app.use(cors({
  origin: ['https://app.example.com'],  // allow-list, not '*'
  credentials: true,
}));   // browser preflights non-simple reqs with OPTIONS

Rate Limit

import rateLimit from 'express-rate-limit';
app.use(rateLimit({ windowMs: 60_000, limit: 100 }));
// respond 429 + Retry-After; back with Redis across instances

REST vs GraphQL

AspectRESTGraphQL
EndpointsManyOne
Fetch shapeFixedClient-chosen
CachingEasy (HTTP)Harder
Over-fetchCommonAvoided
Best forPublic/cacheableRich clients

Design Checklist

  • Plural noun resources, no verbs in URLs
  • Correct method + precise status code
  • Validate all input at the edge → 400/422
  • Paginate every list (cursor preferred)
  • Version the base path (/v1)
  • Authenticate (401) then authorize (403)
  • Allow-list CORS origins; rate-limit → 429
  • Publish an OpenAPI 3.1 spec as the contract

Section navigation