1. Cheat sheets
  2. Developer Foundations

8 minReference

Backend APIs cheat sheet

HTTP APIs on a few screens: methods and which are safe to retry, the status codes worth knowing, headers, resource design, pagination, error bodies, auth, rate limits, idempotency keys, webhooks, versioning, NestJS and Prisma snippets, and how to test and document it all. Keep it open while you design or review an endpoint.

HTTP methods

Safe means the request does not change anything on the server. Idempotent means sending it twice has the same effect as sending it once, so a client or proxy may retry it after a timeout (APIs, API design).

MethodUse it toSafe / idempotentLesson
GETRead a resource or a list; never change stateSafe, idempotent, cacheableAPI design
HEADSame as GET without the body (check existence or size)Safe, idempotentAPIs
POSTCreate a resource, or run an action such as /orders/123/cancelNeither; add an idempotency key to make retries safeAPI design
PUTReplace a resource with the full body sentIdempotentAPI design
PATCHChange some fields of a resourceNot guaranteed idempotent (for example "add 1 to quantity")API design
DELETERemove a resourceIdempotent; a second call may return 404, but the state is the sameAPI design
OPTIONSAsk what is allowed; browsers send it as the CORS preflightSafe, idempotentAuth and sessions

Status codes worth knowing

The first digit tells the client what to do: 2xx done, 3xx look elsewhere, 4xx the client must change the request, 5xx the server failed and a retry may help. Never return 200 with an error inside the body (APIs).

CodeMeaningReturn it whenLesson
200 OKSuccess with a bodyGET, or an update that returns the resourceAPIs
201 CreatedA new resource existsPOST created it; add a Location header with its URLAPI design
202 AcceptedTaken, not finishedWork went to a queue; return a job id or status URLJobs and retries
204 No ContentSuccess, no bodyDELETE, or an update with nothing to returnAPI design
301 / 308Moved permanentlyA URL changed for good; 308 keeps the method and bodyAPIs
302 / 307Moved temporarilyShort-term redirect; 307 keeps the method and bodyAPIs
304 Not ModifiedYour cached copy is still goodThe client sent If-None-Match and the ETag matchesCaching
400 Bad RequestMalformed requestBroken JSON, wrong types, missing required fieldsAPI design
401 UnauthorizedNot authenticatedNo token, expired token, bad signatureAuth and limits
403 ForbiddenAuthenticated, not allowedA store manager for S-014 asks for S-102's salesAccess control
404 Not FoundNo such resource (for this caller)Also when hiding that something exists from a caller who may not see itAccess control
405 Method Not AllowedWrong method for this URLDELETE on a read-only collection; send an Allow headerAPIs
409 ConflictClashes with current stateDuplicate unique value, or the same idempotency key with a different bodyPrisma data
412 Precondition FailedThe version you based your change on is oldIf-Match ETag no longer matches (optimistic locking)Transactions
415 Unsupported Media TypeWrong body formatClient sent XML or form data where JSON is requiredAPIs
422 Unprocessable ContentWell-formed but breaks a business ruleQuantity of 0, delivery slot in the past. Some teams use 400 for all validation; pick one and document itAPI design
429 Too Many RequestsRate limit hitAdd Retry-After with seconds to waitRate limiting
500 Internal Server ErrorUnexpected failureA bug; log it with a request id, show no stack traceTests and docs
502 Bad GatewayA proxy got a bad answer upstreamThe load balancer could not get a valid response from your appLoad balancers
503 Service UnavailableTemporarily overloaded or downMaintenance or load shedding; add Retry-AfterTimeouts and retries
504 Gateway TimeoutUpstream took too longThe proxy gave up waiting; check slow queries and timeoutsTimeouts and retries

Headers you will set or read

Header names are case-insensitive. Custom headers no longer need an X- prefix (APIs).

HeaderWhat it doesLesson
Content-Type: application/jsonSays what format the body is inAPIs
AcceptSays which formats the client can readAPIs
Authorization: Bearer <token>Carries an access tokenAuth and limits
Idempotency-KeyLets the server spot a retried POST (common convention, not yet a final standard)Jobs and retries
ETag, If-None-Match, If-MatchVersion of a resource for caching (304) and safe updates (412)Caching
Cache-Controlno-store for private data, public, max-age=60 for sharedCaching
LocationURL of the created resource (201) or redirect target (3xx)API design
Retry-AfterSeconds (or a date) to wait before trying again, with 429 or 503Rate limiting
X-Request-Id or traceparentTies logs and traces of one request together across servicesObservability
Access-Control-Allow-OriginCORS: which web origins may call the API from a browser; never * with credentialsAuth and sessions

Resource design rules

  • Nouns in the path, plural collections: GET /stores/S-014/orders, GET /orders/ord_81f2. The method is the verb.
  • Actions that are not simple CRUD get a sub-resource: POST /orders/ord_81f2/cancel.
  • Nest only one level, for ownership. Filter with query parameters: GET /orders?storeId=S-014&status=packed.
  • Money in integer cents with a currency: { "totalCents": 1299, "currency": "USD" }. Never floats.
  • Times in UTC ISO 8601: "2026-03-01T09:30:00Z". Dates without time as "2026-03-01".
  • Ids are opaque strings to clients. Do not expose auto-increment ids if guessing them leaks volume or data.
  • Pick one casing for JSON fields (camelCase is common in Node) and keep it everywhere.
  • Return the created or updated resource so the client does not need a second call.

See API design and Data and API.

Pagination: offset or cursor

Always paginate lists and cap the page size (for example 100). The Partner app's order history once returned every order a shop ever placed in one response (API design, Everyday SQL).

Offset (?page=3&limit=50)Cursor (?after=eyJpZCI6...&limit=50)Lesson
SQLLIMIT 50 OFFSET 100WHERE (created_at, id) < ($1, $2) ORDER BY created_at DESC, id DESC LIMIT 50Indexes
Deep pagesSlow: the database still reads and skips every earlier rowFast with an index on the sort columnsQuery plans
Rows added while pagingItems repeat or are skippedStableAPI design
Jump to page 40YesNo; only next and previousAPI design
Use forSmall admin tables in the Back officeFeeds, sync, infinite scroll, exportsAPI design

One error format everywhere

Clients should handle every error the same way. RFC 9457 "Problem Details" (which replaced RFC 7807) is a ready-made shape with the media type application/problem+json. Keep a stable machine-readable code, a human message, and a request id the support team can search for (API design, Tests and docs).

JSON
{
  "type": "https://api.mangrove.example/errors/slot-full",
  "title": "Delivery slot is full",
  "status": 409,
  "detail": "The 18:00-19:00 slot at store S-014 has no capacity left.",
  "code": "SLOT_FULL",
  "requestId": "req_7Hq2c",
  "errors": [{ "field": "slotId", "message": "Choose another slot" }]
}

Auth: which one?

Authentication proves who is calling; authorization decides what they may do. Check authorization on every request, on the server, for the specific record (Auth and limits, Access control, Auth and sessions).

CallerUseKey pointsLesson
Your own web app (Back office)Server session in an HttpOnly, Secure, SameSite cookieEasy to revoke; protect state-changing requests against CSRFAuth and sessions
Mobile app (Mangrove app)OAuth 2.0 authorization code flow with PKCE; short access token plus refresh tokenTokens in the device's secure storage; rotate refresh tokensAuth and limits
Single-page app on another domainAuthorization code with PKCE, or a backend-for-frontend that keeps tokens server-sideThe implicit flow is deprecated; avoid tokens in localStorageAuth and sessions
Service to serviceOAuth 2.0 client credentials, or mutual TLSOne identity per service, least privilegeAuth and limits
Partner or integration (Lagoon Digital's Partner app backend)API key per client, sent in a headerStore only a hash; allow rotation with two active keys; scope each keyAuth and limits
"Sign in with Google" and similarOpenID Connect on top of OAuth 2.0Validate the ID token's issuer, audience, expiry and signatureIdentity

JWT rules

  • A JWT is signed, not encrypted: anyone can read its payload. Never put secrets or personal data in it.
  • Verify the signature with a fixed, allowed algorithm list. Reject alg: none.
  • Check exp, iss and aud on every request.
  • Keep access tokens short (minutes); you cannot easily revoke a JWT before it expires.
  • Load roles and store access from the database for sensitive actions instead of trusting old claims.

Rate limits, idempotency and retries

Rate limit per API key or user, return 429 with Retry-After. Clients retry only idempotent requests, or POSTs with an idempotency key, with exponential backoff and jitter and a retry cap (Rate limiting, Timeouts and retries, Jobs and retries).

StepIdempotency key on POST /ordersLesson
1Client makes a random key (UUID) per user action, not per HTTP attempt, and resends the same key on retryJobs and retries
2Server inserts the key into a table with a unique constraint, in the same transaction as the orderTransactions
3If the key exists with the same body, return the stored response; with a different body, return 409 or 422Jobs and retries
4If the first request is still running, return 409 and let the client retry laterJobs and retries
5Expire keys after a set time, such as 24 hoursJobs and retries

Webhooks

Receiving them (a payment provider tells Mangrove a payment succeeded) and sending them (Mangrove tells the Partner app an order shipped) follow the same rules (No overselling, Jobs and retries).

  • Verify the signature (usually an HMAC of the raw body with a shared secret) with a constant-time compare, and reject old timestamps to stop replays.
  • Answer 2xx fast, then do the work in a queue. Slow handlers cause the sender to time out and retry.
  • Expect duplicates and out-of-order delivery: store the event id with a unique constraint, and check state before acting.
  • When sending: retry with backoff for hours, sign every request, include an event id and type, and give receivers a way to replay.
  • Run a reconciler that asks the provider about anything still pending; webhooks get lost.

Versioning and change

Mobile apps stay installed for months, so the Mangrove app version from last year still calls today's API (API design, Safe migrations).

ChangeBreaking?HowLesson
Add an optional request field or a response fieldNoShip it; clients must ignore unknown fieldsAPI design
Add a value to an enum in a responseOften yesTell clients to handle unknown values; announce firstAPI design
Rename or remove a field, change a typeYesAdd the new field, migrate clients, then remove the old oneSafe migrations
Make an optional field requiredYesNew version, or default it on the serverAPI design
A real redesignYesNew version in the path (/v2/orders) or a header; announce a sunset date for v1API design

NestJS and Prisma snippets

The Mangrove platform runs NestJS on Node with Prisma for Postgres. A controller with validation, a guard and a scoped query (NestJS service, Prisma data, Auth and limits):

TypeScript
// main.ts: reject unknown fields and convert types for every DTO
app.useGlobalPipes(new ValidationPipe({ whitelist: true, forbidNonWhitelisted: true, transform: true }));

// create-order.dto.ts
export class CreateOrderDto {
  @IsString() storeId: string;
  @IsArray() @ValidateNested({ each: true }) @Type(() => LineDto) lines: LineDto[];
}

// orders.controller.ts
@Controller('orders')
@UseGuards(AuthGuard)
export class OrdersController {
  constructor(private readonly orders: OrdersService, private readonly prisma: PrismaService) {}

  @Post()
  @HttpCode(201)
  create(@Body() dto: CreateOrderDto, @Headers('idempotency-key') key: string, @Req() req) {
    return this.orders.create(req.user, dto, key);
  }

  @Get(':id')
  async findOne(@Param('id') id: string, @Req() req) {
    const order = await this.prisma.order.findFirst({
      where: { id, storeId: { in: req.user.storeIds } },   // scope by the caller, not just by id
      select: { id: true, status: true, totalCents: true }, // never return whole rows by default
    });
    if (!order) throw new NotFoundException();
    return order;
  }
}
Prisma: I want to...Do thisLesson
Avoid N+1 when listing orders with linesinclude: { lines: true } or select the nested fieldsPrisma data
Change two tables together or not at allprisma.$transaction(async (tx) => { ... })Prisma data, Transactions
Decrement stock only if enough is leftupdateMany({ where: { sku, qty: { gte: n } }, data: { qty: { decrement: n } } }) and check the countNo overselling
Spot a duplicate unique valueCatch error code P2002 and return 409Prisma data
Change the schema in productionprisma migrate deploy in the pipeline; migrate dev only locallyPrisma data
Run raw SQL safelyThe $queryRaw tagged template, which parameterizes values; avoid the Unsafe variants with user inputInjection

Testing and documenting

Test at the HTTP level: send a real request to the app with a test database, and assert on status, body and side effects (Tests and docs, Unit and API tests, API testing).

  • For every endpoint test: happy path, validation error (400 or 422), no token (401), wrong store (403 or 404), not found, duplicate (409), and a retry with the same idempotency key.
  • NestJS with Supertest: await request(app.getHttpServer()).post('/orders').set('Authorization', `Bearer ${token}`).send(body).expect(201).
  • Generate an OpenAPI document from code (@nestjs/swagger) and publish it; generate client types from it.
  • Contract tests catch the day the Partner app and the API stop agreeing, before users do (Contract tests).
  • Load test the endpoints that face festival peaks, such as checkout and slot booking (Load testing).
  • Log one structured line per request: method, route, status, duration, user id and request id. Never log tokens, passwords or full card numbers.

Security checklist for every endpoint

  • Who may call this, and for which records? Check ownership in the query, not only in the UI (Access control).
  • Validate and allow-list input; parameterize every query (Injection).
  • Return only the fields the caller needs; never return password hashes or internal notes.
  • Set body size limits and timeouts; rate limit login, OTP and search (Auth and limits).
  • Secrets come from the environment or a secret manager, never from the repository (Security).
  • Check the OWASP API Security Top 10 before launch.

Glossary

Plain definitions of the API terms used across the backend courses.

Endpoint
One method plus path that the API answers, such as POST /orders. APIs
REST
A style of API built from resources addressed by URLs and changed with standard HTTP methods. API design
Resource
A thing the API exposes, such as an order or a store, with its own URL. API design
Idempotent
Doing it twice has the same effect as doing it once. Jobs and retries
Idempotency key
A unique value sent with a request so the server can recognise a retry and return the first result. Jobs and retries
DTO (data transfer object)
A class describing the shape of a request or response, used for validation. NestJS service
Guard
NestJS code that runs before a route handler and decides whether the request may continue. Auth and limits
Pipe
NestJS code that validates or transforms input before the handler sees it. NestJS service
ORM
A library that maps database rows to objects in code, such as Prisma. Prisma data
Migration
A versioned script that changes the database schema, run in order in every environment. Safe migrations
Cursor pagination
Paging by "give me items after this one" instead of a page number; stable and fast. API design
ETag
A version tag for a resource, used for conditional GETs and safe concurrent updates. Caching
CORS
Browser rules that decide which other web origins may call your API from JavaScript. Auth and sessions
CSRF
An attack where another site makes the user's browser send a request with their cookies; blocked with SameSite cookies and tokens. Auth and sessions
Session
Server-side record of a signed-in user, referenced by a random id in a cookie. Auth and sessions
JWT (JSON Web Token)
A signed, base64url-encoded set of claims used as a self-contained access token. Auth and limits
OAuth 2.0
A framework for letting an app get limited access tokens for a user or service. Auth and limits
PKCE
Proof Key for Code Exchange: a one-time secret that stops a stolen authorization code being used by another app. Auth and limits
OpenID Connect
An identity layer on OAuth 2.0 that adds an ID token saying who the user is. Identity
Refresh token
A long-lived token used only to get new short-lived access tokens. Auth and limits
Rate limit
A cap on requests per client per period, answered with 429 when exceeded. Rate limiting
Webhook
An HTTP call one system makes to another when something happens. No overselling
HMAC signature
A hash of the message with a shared secret, proving it came from the sender and was not changed. No overselling
OpenAPI
A standard machine-readable description of an HTTP API, used for docs, client generation and tests. Tests and docs
Contract test
A test that checks a provider still meets what its consumers expect. Contract tests
Problem Details
RFC 9457's standard JSON shape for HTTP API errors. API design

Courses on Kolom