1. Courses
  2. System Design
  3. How the Web Works
  4. Stop 4

35 minBeginner

APIs done right

An API is a promise between the app team and the server team. A good one is easy to use correctly and hard to use wrongly: clear resources, honest status codes, one error format, pagination that does not lose rows, and POST requests that are safe to retry.

By the end you can

  • Design REST endpoints with the right methods and status codes
  • Explain why offset pagination loses or repeats rows, and build cursor pagination with Prisma
  • Stop duplicate records from double taps and network retries with an idempotency key
  • Validate input with DTOs and publish an OpenAPI contract from NestJS
  • Say when GraphQL, gRPC or webhooks fit better than REST

The everyday picture

At a busy restaurant, nobody walks into the kitchen to explain what they want. There is a menu. Each dish has a number, a name and a price. You tell the waiter "number 23, two plates", he writes it on a ticket, and the kitchen cooks it. The menu is the contract. The kitchen can change its cooks, its stoves and its suppliers, and as long as number 23 still arrives as promised, you do not care.

A good waiter also handles problems in a fixed way. If you order a dish that is not on the menu, he tells you at once, before the kitchen starts. If you wave at him twice because he did not seem to hear, he does not bring the dish twice. He remembers your table and your order. An API should behave the same: a clear menu, clear "no" answers, and no double orders.

Resources and methods

In a API, the URL names a , a thing, and the HTTP method says what to do with it. Use nouns in paths, not verbs: POST /grns, not POST /createGrn. The running example is Mangrove's Store app, which records every delivery a store receives as a (GRN).

RequestMeaningSafe to repeat?
GET /v1/grns?siteId=12List GRNs of store 12Yes, it only reads
GET /v1/grns/501Read GRN 501Yes
POST /v1/grnsCreate a new GRNNo, each call creates one more
PUT /v1/sites/12/prices/4711Set the price to this full valueYes, same result each time
PATCH /v1/grns/501Change some fieldsDepends: "set note" yes, "add 5 to qty" no
DELETE /v1/grns/501Remove or cancel GRN 501Yes, gone stays gone
POST /v1/grns/501/approveAn action that changes stateOnly if you make it so

The last column is about being : doing it twice has the same effect as doing it once. This matters because networks fail and clients retry. Retrying a GET or a PUT is harmless. Retrying a POST can create a second GRN, a second order or a second payment. We will fix that below.

Some actions do not map neatly to create, read, update or delete. Approving a GRN is a state change with its own rules. A sub-resource like POST /grns/501/approve is a common and readable answer. Do not bend it into a PATCH that sets status: "APPROVED" if approval also checks permissions and writes stock.

Quick check

A scanner's request times out and the app cannot tell whether it worked. Which request can it retry with no risk of doing the work twice?

Loading the rest of the lesson…