Build a Production API
One endpoint — `POST /checkout` — built in the order you would actually build it. At each step there is a question that decides whether the endpoint is correct or merely working. Think about it before you open the answer; the answer is much less useful if you have not first been wrong.
Checkout is the example because it has every hard property at once: money, an external dependency, a race on shared stock, a client that retries, a transaction, and an event other systems depend on. An endpoint that only reads a row can hide all six.
- 1Authenticate
Establish who is calling from the session or token.
The questionShould the tenant come from the request body, since the client already knows it?
- 2Validate
Check the request is well-formed and the cart is in a checkout-able state.
The questionThe cart was valid when we checked. Is it still valid when we charge?
- 3Check the idempotency key
Look up the client-supplied key; if this request was already processed, return the original result.
The questionWhere does this check belong — before the work, or after?
- 4Reserve inventory
Decrement available stock for the items in the cart.
The questionTwo customers buy the last unit at the same moment. Who gets it?
- 5Charge payment
Call the payment provider — a slow, external, failable dependency.
The questionShould this happen inside the database transaction?
- 6Handle a payment timeout
The provider does not respond before your deadline expires.
The questionDid the charge happen?
- 7Write the order
Persist the order and its line items.
The questionWhat belongs inside this transaction?
- 8Publish OrderCreated
Tell the rest of the system an order exists.
The questionThe commit succeeded and the publish failed. Now what?
- 9Respond
Serialize the result and return it.
The questionThe connection drops before the client reads the response. What does the client do?