Skip to content

Instantly share code, notes, and snippets.

@eriknguyen
Last active August 7, 2017 04:17
Show Gist options
  • Save eriknguyen/f94b1c46faf1ee7d8fae1b4162dd5fb1 to your computer and use it in GitHub Desktop.
Save eriknguyen/f94b1c46faf1ee7d8fae1b4162dd5fb1 to your computer and use it in GitHub Desktop.
API Best Practices

API Design Best Practices

Curated list of best practices for creating API

Web API Design

Following Apigee api design guide

1. Use nouns with HTTP verbs

  • There should be only 2 base URLs per resource
    • First is for collection, eg. /dogs
    • Second is for element, eg. /dogs/1234
  • Keep verbs out of base URLs
  • Use HTTP verbs to operate: post, get, put, delete for Create, Read, Update, Delete (CRUD)
  • Example:
    • /dogs
      • POST create new dog
      • GET get list of dogs
      • PUT bulk update dogs
      • DELETE bulk delete all dogs
    • /dogs/1234
      • POST error
      • GET get 1 dog 1234
      • PUT update dog 1234 if existed, else error
      • DELETE delete dog 1234

2. Plural nouns & concrete names

  • Use plural nouns, avoid mixed model when using both plural & singular
  • Concrete names > abstract name, eg. /products, /blogs are better than /items, /assets => more compelling & useful
  • Keep the number of resources between 12-24

3. Simplify associations - sweep complexity under the ?

  • Get all dogs belong to owner 5678: don't use GET /owners/5678/dogs
  • Should not need more than this: /resource/identifier/resource
  • Use ? for URL: GET /dogs?color=red&state=running&location=park

4. Handling errors

  • Examples:

    • Facebook: always get code 200, error code in message, no info about #803 error or how to react
       HTTP Status Code: 200
       {
       	"type": "OauthException",
       	"message": "(#803) Some of the aliases you requested do not exist: foo.bar"
       }
      
    • Twilio: nice to give a link to doc
       HTTP Status Code: 401
       {
       	"status": "401",
       	"message": "Authenticate",
       	"code": 20003,
       	"more info": "http://www.twilio.com/docs/errors/20003"
       }
      
    • SimpleGeo: bad -> only error code, no additional info in the message
       HTTP Status Code: 401
       {
       	"code": 401,
       	"message": "Authentication Required"
       }
      
  • Use HTTP status codes

    • Use a subset of common codes
    • Start from 3:
      • All good: Success - 200 OK
      • App side: Client error - 400 Bad Request
      • API side: Server error - Internal Server Error
    • If need more -> add upto 8, try picking from:
      • 201 - Created
      • 304 - Not Modified
      • 404 - Not Found
      • 401 - Unauthorized
      • 403 - Forbidden
    • HTTP Status Codes
  • Make messages verbose, add more hints, use URL for more info

     {
     	"developerMessage" : "Verbose, plain language description of the problem for the app developer with hints about how to fix it.", 
     	"userMessage":"Pass this message on to the app user if needed.", 
     	"errorCode" : 12345, 
     	"more info": "http://dev.teachdogrest.com/errors/12345"
     }
    

5. Versioning

  • Never release an API without a version
  • Can use v3 prefix in URL, don't use v3.1because it implies that the interface might be changing more frequently than it should
  • Maintain at least 1 version back, around 6 months to 2 years
  • Version in URLs or headers?
    • Using headers is more correct
      • Leverage existing HTTP standards
      • Intellectually consistent with Fielding's vision
      • Solve some hard real-world problems related to inter-dependent APIs
    • Rules:
      • If it changes the logic you write to handle response, need to see easily -> URL
      • Else (eg. OAuth information) -> header

6. Pagination & Partial Response

  • Partial Response -> give developers just the info they need
    • /dogs/?fields=name,color,location
    • Google: ?fields=title,media:group(media:thumbnail) -> sub-objexts to pull in other info
  • Pagitation
    • Facebook, LinkedIn use offset & limit: ?offset=50&limit=25 or ?start=50&count=25 -> recommend
      • More common
      • Well understood in leading databases
      • Easy for developers
    • Twitter uses page & rpp: ?page=3&rpp=25 (rpp = records per page)
    • Return metadata: total number of records

7. Don't involve resources

  • Use verbs, nout nouns. Eg. /convert?from=EUR&to=CNY&amount=100
  • Separate these "non-resource" cases in API docs

8. Support multiple formats

  • Use params like ?alt=json, ?type=json or /dogs.json
  • type param overrides the Accept header -> Accept: application/json

9. Attribute names

  • Use JSON as default
  • Follow JS naming conventions
    • Camel Case: createdAt instead of created_at or DateTime
    • Use uppercase or lowercase depending on type of object

10. Tips for search

//TODO

11. Consolidate API requests in sub-domain

  • All requests go under one API subdomain -> api.domain.com
  • Do web redirects

12. Exceptional behavior

//TODO

13. Authentication

  • Use OAuth 2.0 ->
    • Web or mobile apps that expose APIs don't have to share passwords
    • Allows the API provider to revoke tokens for an individual user, for an entire app without requiring the user to change their original password

14. Making requests on your API

15. Chatty APIs

16. Complement with an SDK

17. The API Facade Pattern

  • A virtual layer between the interface on top & API implementation on the bottom
  • Create a facade - a comprehensive view of what the API should be, importantly from the API consumer perspective
  • API facade isolates the developer/application and the API
  • Use the façade pattern when you want to provide a simple interface to a complex subsystem. Subsystems often get more complex as they evolve.
    (Gang of Four, Design Patterns - Elements of Reusable Object-Oriented Software)

  • Implementing -> 3 basic steps:
    1. Design the ideal API -> design URLs, request params & responses, payloads, headers, query params, etc -> should be self-consistent
    2. Implement the design with data stubs -> can use API and give feedback even before API is connected to internal systems
    3. Mediate/ integrate between the facade & the systems
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment