Crosspost of my article here
REST (Representational State Transfer) APIs are the backbone of modern web development. This article will break down how to create and use modern REST APIs, what design decisions to take into consideration when making one, and the theory being the foundation of REST.
Practical Guide
This section dives into using REST APIs with HTTP covering endpoints, methods, requests, and responses. You'll find everything you need to start making API calls and applying REST in your projects.
How to Structure Your URIs
In general there are two main ways you can treat a URI:
- As an action
- As a resource
Consider the two following URIs:
-
https://example.com/getUserData?id=1
(action) -
https://example.com/users/1
(resource)
Both examples show us retrieving user data of a user with an id
of 1
. The difference being that in the first example the route /getUserData
is performing an action while in the second example the route /users/1
is the location of an asset, it does not indicate what action is being performed. We could say that this type of URI is acting as a noun (as it is a thing instead of an action i.e. a verb).
The REST pattern dictates that we strictly use URIs like the second example. We want our URIs to be nouns so that we can use HTTP Methods as our verbs to perform actions upon those nouns. For example, we can use the HTTP method GET
to retrieve information about /users/1
, but we could use PUT
to update that corresponding user's information, or DELETE
to delete the user entirely.
The last thing to note about URIs is that, as with the example above, when referencing an individual resource (e.g. a single user in this case) the URI should end with the unique identifier for that resource. When referencing all resources in a given category, the unique identifier should be omitted.
-
https://example.com/users/1
- References a particular user with anid
of1
-
https://example.com/users
- References all users regardless ofid
What Actions to Support
There are 4 main actions to support in REST, we use the acronym CRUD to remember them: Create, Read, Update, Delete. Each of these actions maps to an HTTP Method that we can use to perform that action. The mapping is as follows:
Action | HTTP Method |
---|---|
Create |
POST |
Read |
GET |
Update |
PUT / PATCH |
Delete |
DELETE |
All Action + URI Combinations to Support
Every REST API is really just (at a minimum) 5-6 routes. In our example, the base endpoint will be /users
and we'll pretend to host it on https://example.com
.
-
GET https://example.com/users
- Action: Return all user assets (each asset is one user)
- Request Body: Empty
- Response Body: List of user assets (as a JSON array)
-
GET https://example.com/users/[id]
([id]
is a variable)- Action: Returns just the singular requested user asset
- Request Body: Empty
-
Response Body: Just the user asset with the matching
id
(as JSON)
-
POST https://example.com/users
- Action: Adds one user asset to the collection
- Request Body: All data needed to create the new user asset (no specific format required, JSON recommended)
- Response Body: The newly created asset that was inserted + a unique ID (as JSON)
-
PUT https://example.com/users/[id]
([id]
is a variable)- Action: Fully replaces just one existing user's data with the given data
-
Request Body: All data needed to replace an existing user's data, whether or not it has changed (minus the
id
- no specific format required, JSON recommended) -
Response Body: The newly updated asset with the matching
id
(as JSON)
-
(Optional)
PATCH https://example.com/users/[id]
([id]
is a variable)- Action: Partially replaces just one existing user's data with the given data
- Request Body: Only the data that needs updating (minus the id - no specific format required, JSON recommended)
- Response Body: The newly updated asset with the matching id (as JSON)
-
DELETE https://example.com/users/[id]
([id]
is a variable)- Action: Deletes just one record from the users table
- Request Body: None
-
Response Body: None (just HTTP response code) OR The data from the asset that was just deleted with matching
id
(as JSON)
Design Considerations
Outside of what defines an endpoint as using the REST pattern or not, there are many things to take into consideration before you start building one. Is there a chance that you might want to update your endpoint in the future? Should your output give helpful hints to users? Is REST even the right pattern to use in your situation? Let's answer some of these questions.
Versioning Your Endpoint
It might be a good idea to start thinking about versioning of your API right from the start so that making changes is easier in the future. There are a few different methods for determining what API version your users are choosing to use:
- URI Versioning
- The version numbers are incorporated into a URL path, usually at the base
- Examples:
https://example.com/v1/users/1
https://example.com/v2/users/1
- Query Parameter
- The version number is appended as a query parameter in the API endpoint
- Examples:
https://example.com/users/1?apiVersion=1
https://example.com/users/1?apiVersion=2
- Header based
- The version number is a specific and unique header field
- Examples (Request Headers):
x-api-version: 1
x-api-version: 2
- Content Negotiation
- The version is determined based on the representational state or media type.
- In the example below, the server code would know that
firstName
is for the first version and that it was changed togivenName
in the next version. - Examples (Request Body):
{ firstName: 'Henry' }
{ givenName: 'Henry' }
Mocking a Quick REST API
Sometimes playing around with one is the best tool to learn how they work. One of my favorite libraries to demo REST is json-server. Setting it up is pretty simple - just a few steps required.
Install JSON Server
npm install json-server
Create a simple data store
{
"users": [
{ "id": "1", "username": "gorwell", "email": "gorwell@gmail.com" },
{ "id": "2", "username": "cdickens", "email": "cdickens@gmail.com" },
{ "id": "3", "username": "jausten", "email": "jausten@gmail.com" },
{ "id": "4", "username": "vwoolf", "email": "vwoolf@gmail.com" },
{ "id": "5", "username": "sking", "email": "sking@gmail.com" }
]
}
Start up the server
npx json-server db.json
Make an HTTP request against your local server
curl -X GET http://localhost:3000/users/1
An Easy CRUD Data Grid
A fully functioning REST endpoint can be hooked up to a data grid easily with ZingGrid, just point the base REST URI at the <zing-grid>
element's src
attribute like below
<zing-grid
src="http://localhost:3000/users"
editor-controls
></zing-grid>
Final Thoughts
REST APIs come in many shapes and sizes across the web, each tailored to meet specific needs. By structuring URIs thoughtfully, choosing the right actions, and keeping versioning in mind, you can create a straightforward, flexible API that developers will find a pleasure to work with. With these foundational steps, even a quick prototype can evolve into a robust, reliable API that stands the test of time.
Top comments (1)
Your articles on REST APIs are incredibly helpful. In my own projects, EchoAPI has been invaluable for simulating REST endpoints, letting me thoroughly test requests and responses.