Checkout
Goal: the user presses “Pay”. You must create an order, charge a card with a secret you cannot expose, and tell the user the result. Design:- The browser calls a route:
POST /orders/{id}/pay. - The route runs a flow that loads the order from the database (never trusting a price from the browser), charges the card through the provider’s API with a secret, marks the order paid, and returns
{ status, receiptUrl }. - The browser shows the result.
key lets the flow recognise a repeat of the same attempt and return the earlier result instead of charging twice. That is what makes idempotent: true safe. Without it, never set that flag on a payment.
Long-running work
Goal: “Export all my data” takes two minutes. A browser request should not wait that long. Design:- The browser calls
flows.run("start-export"). The flow creates anexportsrecord withstatus: "preparing", queues the real work in the background, and returns{ exportId }immediately. - The background work builds the file, uploads it to storage, and updates the record to
status: "ready"with the file’s key. - The browser watches that record (or a channel) and shows a download button when it is ready.
Safe retries
A network drop can leave you not knowing whether aPOST arrived. Decide, per feature:
The client already retries rate limits and 502/503/504 for you. The flag only matters for repeating a
POST after the connection broke.
Cancel a request
Give long calls anAbortSignal so the user can back out, and so a component that unmounts does not leave work behind.
Optimistic UI
For quick actions, change the screen first, call the server, and undo if it fails.Validate on the server, show on the client
The flow checks the input and answers withValidationError and a field name. The client shows it next to the right input:
Act for the user on a server
When your own backend calls Pawabase for someone who is signed in to your app, send their token, so the same access rules apply:Testing
- Unit-test the logic itself where it is written.
- Integration-test the contract from a script against a development environment: sign in as a test user, call the route, assert the result and the error codes.
- Test refusals as carefully as successes: call with no sign-in, with the wrong user, with bad input. The access rule is a feature.
Access rules
How a route or flow decides who may call it.
Errors
Every error type.