REST (REpresentational State Transfer) continues to be the dominant way to provide a standard method for data access in a web environment. There are a lot of discussions on what makes a good RESTful API, but examples are sometimes hard to come by. How do you structure your code to enable REST but also ensure that you can update and maintain the code over the long haul?
It this talk I will walk through some of the things that I have learned in implementing a RESTful API. We will discuss some of the following topics:
* Landmines - what are some things not to do when developing your REST stack
* Pure data vs hypermedia (HATEOAS anyone?)
* Layering the stack to enable automated testing at all tiers
* Securing your endpoints
* Testing -- tools to explore and validate your endpoints
3. What is REST
• REpresentational State Transfer
– An application should be able to “browse” your api by knowing
a starting place (a bookmark)
– When data is returned from a particular call, available
“transfers” are included in the response (REpresentation) as
links with data types
• Roy Fielding’s Doctoral Dissertation, primarily chapter 5
http://www.ics.uci.edu/~fielding/pubs/dissertation/rest_a
rch_style.htm
4. Data + Hypermedia
• HATEOAS - Hypermedia As The Engine Of Application State
– One of the driving forces behind REST.
– The “Transfer” part of REST
5. Structuring your code
• Layer your code into
– Endpoints
– Services
– Data
• If the language you are using supports interfaces, use
them to allow you to swap in different implementations
for different situations
– testing
6. Structuring your code?page=2
DB
Data Layer (ORM)
Service Layer (Business logic)
REST Layer (endpoint)
Test Automation
Test Automation
Test Automation
7. Testing
• Unit testing
– Only test the method under test for logic errors.
• Functional testing
– Test through multiple layers
• Integration testing
– Test across multiple services
8. Data formats
• Content-Type and Accept headers
– Content-Type: what is the format of the data are you
expecting for input
– Accept: what is the format of the data will you be returning
• Typical data types
– JSON - Pretty much the current standard, especially for
consumption by JavaScript
– XML - Earlier standard. More restrictive format and verbosity
– CSV - Useful for data import and export
9. Security
• Closed by default
– Build it in at the beginning, don’t tack it on at the end
– Allows for “safer”, more reliable releases. If authentication
and authorization are built in you can provide releases to
authorized users without exposing sensitive data
10. Documentation
• Interactive documentation is very helpful
• If it can be generated from/by the code it is more likely to
stay up to date
• Try and use a standard like the OpenAPI Specification
– See http://swagger.io/
– You can define the contract for your api with its associated
documentation, generate the base endpoints for different
languages and generate test cases to validate your
implementation
– Good tutorial at https://github.com/tylerdave/OpenAPI-Tutorial/
11. Resource naming
• Two primary URLs for a given resource
– /resources - a collection of resources
– /resources/:id - a specific resource
• :id can be any identifier but it has to be unique
• uuid (Universally Unique IDentifier) is a good option
• Use plural nouns for resource names
– employees vs employee
• Use HTTP Verbs for processing
– Create = POST
– Read = GET/HEAD
– Update = PUT/PATCH
– Delete = DELETE
12. Versioning your API
• In the URL
• With request parameters
• Using custom headers
• Using Accept header
13. Versioning your API?page=2
• Depending on whom you ask, any of those ways are wrong
– https://www.troyhunt.com/your-api-versioning-is-wrong
-which-is/ - a somewhat tongue in cheek article about
this
• It’s your API
– You are defining the contract. You get to choose but make a
choice and stick with it
– What are the practical reasons for your choice?
– How do you support it in your back-end language/framework?
– How are you expecting your API to be consumed?
14. Collections and Pagination
• Implement pagination as early as possible. It will save
future headaches
• Use standard defaults throughout your API
– Default number of entries between 10 and 1000. Ask your
consumers (if you can) what works best for them
– You should probably have an upper limit (1000?) per request
• You want to allow for batching but too large of a dataset in a request
can slow down applications, particularly JavaScript
• Page number and number of entries are query
parameters
15. Collections and Pagination?page=2
• https://server.com/api/resources?page=2&items=100
• In the returned links, include links for the next,
previous, first and last pages
16. Caching responses
• Speeds up follow up requests for the same resource
– Some frameworks support this directly or have libraries that
support it
– Proxy servers can be configured to support it but it helps to
understand the tradeoffs
• Web standards
– ETags
• Cache busting - updating the cache when a resource
is updated
17. Things to avoid
• Doing everything in one method (handle inputs, query
database, transform results, handle errors, etc.)
• Raw SQL
– More of a strong guideline but raw SQL can become brittle
– Take advantage of the ORM and data models
• NOT writing tests
– When you have to change a schema/model or refactor your
code, how do you know things still work without tests?
18. Exercising your API
• Postman - https://www.getpostman.com/
• Allows you to test all of the different verbs
• Allows you to create collections of related requests
• You can set headers and body data for requests
• Allows you to define different environments to
parameterize requests