API Design: REST, RPC, GraphQL, and Contracts
Learn to design stable service interfaces, choose communication styles, version APIs, and handle errors safely.
REST Resource Modeling and HTTP Methods
REST-style APIs model resources and expose representations through predictable URLs. Prefer /users/{userId}/orders to action-heavy paths when the resource model is clear, but do not force every operation into CRUD if a domain command is more expressive.
Use HTTP methods consistently: GET retrieves, POST commonly creates or invokes a command, PUT replaces a resource representation, PATCH applies partial changes, and DELETE requests removal. Define pagination, sorting, filtering, field selection, and error formats before clients depend on them.
Cursor pagination is often better than offset pagination for large, changing datasets. An offset query such as skip=100000 may require expensive work and can produce duplicates or gaps when rows are inserted during traversal. A cursor based on a stable ordering key provides more predictable continuation.
GET /v1/orders?limit=25&after=eyJjcmVhdGVkQXQiOi... HTTP/1.1
Accept: application/json- Use consistent resource and method semantics.
- Document pagination and errors as part of the contract.
- Prefer cursor pagination for large mutable collections when appropriate.
Which approach is most suitable for preventing duplicate payment creation after a client timeout and retry?
REST vs. RPC vs. GraphQL vs. Events
REST works well for resource-oriented public APIs and benefits from familiar HTTP semantics. RPC exposes operations such as ReserveInventory; it can express domain commands clearly and may use generated contracts. GraphQL lets clients request a shaped graph of data and can reduce over-fetching, but needs query-cost controls, authorization at field boundaries, and caching strategy.
Asynchronous event interfaces decouple producers from consumers in time. A producer publishes an event such as OrderPlaced; consumers process it independently. This improves isolation and absorbs bursts, but introduces eventual consistency, duplicate delivery, ordering questions, and operational complexity.
Choose an interface based on the interaction. A synchronous checkout command may need an immediate acceptance result, while sending an email can be asynchronous. Mixing styles is normal when each boundary has a clear purpose.
Resources and HTTP semanticsExplicit operationsClient-selected response shapeAsynchronous facts or commands- No API style is best for every interaction.
- Asynchronous messaging is not a free performance upgrade.
- Authorization and observability must cross every interface.
Versioning, Validation, and Error Design
A stable contract makes compatibility explicit. Prefer additive changes when possible: adding an optional response field is often safer than renaming a field that existing clients depend on. Removing fields, changing meanings, or tightening validation can be breaking changes even when the URL stays the same.
Validate untrusted input at the boundary: types, lengths, ranges, allowed enum values, object shape, and ownership. Validation prevents malformed data from reaching deeper layers, but authorization must still check whether the caller may access the referenced object.
Return consistent machine-readable errors with a stable code, safe message, and correlation identifier. Do not expose stack traces, database errors, secrets, or internal topology to public clients. Maintain an API deprecation policy and measure usage before removing old versions.
- Compatibility includes behavior and semantics, not just JSON shape.
- Validate input and separately authorize access.
- Errors should help clients recover without revealing sensitive internals.