Alternate Title:
I Read Roy Fielding's dissertation
So you wouldn't have to
pro tip: start with chapter 5
What we'll cover
- Principles of REST and what that means for your app
- The API design process
- General HTTP API design principles
- URL Structure
- Versioning
- Status Codes
- Headers
- Authentication + Security
So...what's this REST thing?
- Codified by Roy Fielding in his doctoral dissertation
- An architectural (not code implementation!) style
- A set of constraints that make interacting with a web-based system more predictable
- Describes information structure on the Web, not just APIs
Constraint #1: Client-Server
Interfaces, not implementations
GET /tacos Accept: video/mp4 {other headers here} HTTP/1.1 200 OK Cache-Control: public, max-age: 3600 Content-Type: video/mp4 Date: Fri, 21 Feb 2019 19:35:00 GMT Vary: Accept-Encoding Content-Length: 20190505 Connection: keep-alive X-Powered-By: Scala on Scales
Constraint #2: Stateless
- No concept of a "session" (with preferences etc. baked in)
- Current user ID shouldn't be in URL (impacts caching!)
- Cookies are an antipattern*
- WebSockets aren't REST...and that's okay!
* They may be the best way to pass auth back and forth
Form follows function
An architecture for the web must therefore be designed with the context of communicating large-grain data objects across high-latency networks and multiple trust boundaries.
- Roy Fielding
Why large-grain data objects?
- Amortizes communication overhead
- More consistent format allows for more caching
Constraint #3: Cacheable
An interesting observation is that the most efficient network request is one that doesn't use the network.
- Roy Fielding
HTTP/1.1 304 Not Modified Cache-Control: public, max-age: 3600 ETag: a1234b1234 Content-Type: application/json Date: Fri, 21 Feb 2019 19:44:00 GMT Vary: Accept-Encoding Content-Length: 42 Connection: keep-alive X-Powered-By: Python on a Plane 2.999
If-Modified-Since If-None-Match Vary
Software X NETWORK
- Software architecture - design patterns without looking at impact on system behavior
- Network analysis - protocols without looking at application design changes that could result in more of a performance difference than protocol efficiency enhancements alone could provide
Constraint #4: Uniform Interface
Since REST is specifically targeted at distributed information systems, it views an application as a cohesive structure of information and control alternatives through which a user can perform a desired task.
- Roy Fielding
Hypermedia as the engine of application state
- Inline discoverability
- Choose your own adventure!
Still an inexact science
Representations vs. Resources
- Resource
- An identifier
- Defined by URI
- e.g. /users/5 or /events/upcoming or /talks?user_id=10
- REST != pretty URLs
- Representation
- Multiple may be available for a given resource
- Use content-type negotiation
- e.g. a QR code vs. a text URI for 2FA
Accept
Content-Type
POST /tokens HTTP/1.1 201 Created Cache-Control: no-cache, no-store Content-Type: application/hal+json Date: Fri, 21 Feb 2019 19:52:00 GMT Content-Length: 337 X-Powered-By: COBOL ON COGS 1.0.0RC3
POST /tokens Accept: image/png HTTP/1.1 201 Created Cache-Control: no-cache, no-store Content-Type: image/png Date: Fri, 21 Feb 2019 19:52:25 GMT Content-Length: 1337 X-Powered-By: Python on a Plane 2.999
POST /tokens Accept: text/xml HTTP/1.1 406 Not Acceptable Cache-Control: no-cache, no-store Date: Fri, 21 Feb 2019 19:52:45 GMT Content-Length: 1337 X-Powered-By: NodeLiveScript 0.8
Constraint #5: Layered System
- Abstraction && Encapsulation
- REST is not a query language
- GraphQL is...and GraphQL isn't REST
- REST expects caches/proxies to exist inline
- Browser cache
- CDN
Constraint #6: Code on Demand
- Optional
- Allows for shifting processing effort to the client side
- Downside: language-specific
- An SPA or SDK served from the API qualifies here
- e.g. codegen'd example via (the former) Sensio Melody
Core constraints of REST
- Client-server
- Stateless
- Cacheable
- Uniform Interface (HATEOS)
- Layered System
- Code On Demand (optional)
SometimeS full REST isn't the answer
Mix and match (RPC, GraphQL)
...but know why you're picking a tool for the job
'cuz Buzzword Driven Development is considered harmful
DOn't let your API happen by accident
- Figure out what workflows your users need to perform
- Build an API spec (e.g. OpenAPI), matching workflows to a series of API endpoints
- Build documentation/mock APIs explaining how to use the API to perform the workflows, see how users react
- Build only the API that you've spec'd
- Get feedback
- Update the API, keeping the API in sync
- Repeat
Write the docs!
- Your spec by default isn't enough
- Outdated docs may be worse than no docs
- Focus on user flows first
- Figure out how you're going to provide support
Remember the FIve Minute Rule
...and now for some API-Pinions
URL structure: Plural, not singular
- Good: /users, /users/1
- Bad: /user, /user/1
- Break this rule if you're 100% sure you'll never have more than one of a resource, e.g. /me
URL structure: Nouns, not verbs
- Good: /users/1/messages
- Bad: /users/1/sendMessage
- Also bad: /getUser?id=1
- Use RPC if you must...
- ...but try to model interactions as transferring representations of resources first (e.g. creating an event)
URL structure: Don't nest too deeply
- Good: /poems?tag=bird,lenore¢ury=19&author=poe
- Bad: /poems/19/poe/bird
- Pattern: /:resource/:id/:relation (links to top level IDs)
HTTP Methods, REST verbs
- GET: Find a thing (or a collection of things), idempotent
- POST: Create a thing (or, if you must, do an RPC)
- PATCH: Update a part of a thing
- PUT
- Update an entire thing (zero out non-provided fields)
- Create a thing with a client-specified ID (return a 201!)
- DELETE: Delete a thing (return a 204 or 205)
- OPTIONS: Which methods are allowed for this endpoint?
- Use HTTP 405 (Method Not Allowed) as applicable
HTTP Status Codes
- Clients should assume x00 if they don't understand the exact code returned
- 1xx - Provisional
- 100 - Continue (streamed output)
- 101 - Upgrade (e.g. to WebSockets, HTTP/2)
- 2xx - Success
- 200 - OK (don't put an error here)
- 201 - Created (can include Location header)
- 202 - Accepted (for async processing)
- 203 - Non-authoritative information (caching)
- 204 - No Content
- 205 - Reset Content
HTTP Status Codes - 3xx, redirects
- Location header (except for 304)
- 300 - Multiple Choices
- 301/308 - Moved Permanently
- 302/307 - Found
- 303 -
DONTTRUSTMESee Other - 304 - Not Modified
HTTP Status Codes - 4xx, client errors
- 400/422 - Your request format is bad
- 401 - Who are you?
- 402 - Pay me.
- 403 - I know who you are. You aren't allowed here.
- 404 - Nothing here.
- 405 - Wrong Method.
- 406 - I can't provide that content type.
- 408 - Timeout
- 409 - Conflict (e.g. when updating)
- 410 - Something was here but isn't here now.
- 418 - I'm a Teapot
- 429 - Too Many Requests
- 451 - Unavailable for Legal Reasons
HTTP Status Codes - 5xx, Server errors
- 500 - Generic server error
- 501 - Not implemented
- 502 - The server behind this one is broken
- 503 - Service Unavailable
- 504 - The server behind this one timed out
Versioning
- Clients shouldn't care about semver-minor versions; don't use
- URL prefix based versioning to allow a safety valve for backwards incompatible resource changes
- Content-Type based versioning (application/hal+json.v1) for backwards incompatible representation changes
- Custom header if you want to do some mix of the above
- Are you going to require a version in headers? If not, what do you default to?
Auth
- HTTP Basic/API Keys
- OAuth (we'll talk about OAuth 2)
- Access Tokens
- Refresh Tokens
- Grant Types
- Client Credentials
- Auth Code
- Implicit
- Password
Thanks! Questions?
- https://phpc.social/@ian - me!
- https://twitter.com/iansltx - also me
- https://github.com/iansltx - my code!
API Design Patterns for the REST of US - MergePHP May 2023
By Ian Littman
API Design Patterns for the REST of US - MergePHP May 2023
If you've spent long enough writing web applications, you've had firsthand experience with an API, whether internal or external, that leaves you scratching your head at best and banging your head against the keyboard at worst. To be fair, it takes more than exposing your application's database via a CRUD interface to get an API that's truly a joy to work with. We'll look at patterns, affecting everything from HTTP methods to versioning, from authentication to response codes, that'll make your API happily boring rather than uniquely frustrating. Including when to use REST, and when to pick a different design pattern that better suits the task at hand.
- 336