5. RESTful CRUD API
Last updated
Last updated
You now have the skills to build a simple server application. But it can only perform one of the four CRUD capabilities.
In this lesson, you will learn how to add crate, update, and delete endpoints and best practices for creating a "RESTful API".
Table of Contents:
RESTful API — an API that conforms to the design principles of representational state transfer (REST).
express.json()
Middleware — parses JSON data from the request and adds it to req.body
in the controller.
Route Parameters — named URL segments that are used to capture the values specified at their position in the URL. The captured values are populated in the req.params
object.
HTTP Response Codes:
201
— Success: Created
204
— Success: No Content
400
— Client Error: Invalid Request
404
— Client Error: Not Found
500
— Server Error: Internal Server Error
503
— Server Error: Unavailable
Clone down the provided repository and cd
into the starter folder. There, you will see a frontend
and server
provided for you. Run both applications and you'll see it is a simple application that shows a list of fellows and their unique IDs.
This application represents an essential pattern that we've seen a few times now:
The server sends the client a frontend application
The frontend application provides the user with buttons or forms that send requests back to the server
The server responds with data
The frontend renders that data
This back and forth pattern between a server application and the frontend application (both from the same origin) underlies virtually every web application that you've ever used!
This example application is quite limited in its capabilities. It only interact with its server to read data using GET
HTTP requests.
Take a look at the frontend adapters and the server endpoints/controllers and you'll see familiar code:
But think about an application like Google Calendar where you can create an account, create new events, edit events, and delete them too! Applications like these perform all of the CRUD operations.
That is, both their server and frontend are set up in such a way that enable users to create, read, update, and delete data via HTTP requests:
Create — POST
HTTP requests
Read — GET
HTTP requests
Update — PATCH
/UPDATE
HTTP requests
Delete — DELETE
HTTP requests
Let's look at how we can add create, update, and delete capabilities to our application!
To enable our frontend application to create data, we will add POST
endpoints on our server and send POST
requests from the frontend, often triggered by a form.
Since POST
requests are requests to create data, we need to send data in the body
of the request. On the frontend, this means including an options
object with our fetch
.
With the server controllers built on the backend and the adapter built on the frontend, we can easily test our code by hard-coding a call to the adapter:
With this code fully tested from the frontend and backend, we can now incorporate it into our React components! See if you can figure out how to trigger the createFellow
adapter within the Home
component.
We now have an application that can create new fellows and read the full list of fellows that have been created! Try clicking on a fellow in the list and, thanks to React Router, we are redirected to the FellowDetails
page.
Now, we now want to add the following features to the FellowDetails
page
View the details (name and id) of the chosen fellow
Update the name of the chosen fellow
Delete the chosen fellow from the database
Before we go into the code for adding these features, we need to talk about designing our endpoints according to REST (Representational State Transfer).
Pop Quiz: Guess the action that each of these requests will perform!
If you guessed these correctly, there is a good reason why! The endpoints are intuitive to understand thanks to the best practices encouraged by REST.
REST is an acronym for REpresentational State Transfer and an architectural style for distributed hypermedia systems. Roy Fielding first presented it in 2000 in his famous dissertation. Since then, it has become one of the most widely used approaches for building web-based APIs (Application Programming Interfaces).
REST is not a protocol or a standard, it is an architectural style. During the development phase, API developers can implement REST in a variety of ways.
Like the other architectural styles, REST also has its guiding principles and constraints. These principles must be satisfied if a service interface is to be referred to as RESTful.
The guiding principles of REST can be found in detail in the website above. Here, we've provided a few easy ways to ensure you are building a RESTful API:
Requests Should be Statelessness: Each request should contain all the information needed by the client for the current. The server doesn't store the current client state.
For example, if a user selects a filter that is applied to a GET
request, that filter must be provided with every request. The server should not be expected to remember the client's state.
Endpoints Describe Resources, Not Actions: Use plural nouns (e.g., /api/users
, /api/posts
) instead of verbs (e.g., /api/getUser
, /api/createPost
) for endpoint URLs.
A common exception to this rule is /api/login
and /api/logout
HTTP Methods Matter:
GET
– Retrieve data
POST
– Create data
PUT / PATCH
– Update data
DELETE
– Remove data
Endpoints Should Use Proper Status Codes (e.g., 200 OK, 201 Created, 400 Bad Request, 404 Not Found, 500 Internal Server Error)
URLs Should Indicate a Clear Hierarchy of Resources:
Use IDs to get individual resources: /api/users/123
Nest resources to indicate ownership: /api/users/123/posts/456
Avoid deep nesting that becomes hard to manage.
These principles help our API become "RESTful". But keep in mind that these are just guidelines that help make an API more intuitive and predictable for the client. Providing clear documentation will always be the best way to ensure your API is used properly.
In the request GET /api/fellows/3
, the value 3
indicates that I want to get the fellow with the ID 3
. To generalize the id
value in this request endpoint, we define the endpoint like so:
In this more generalized endpoint URL, the :id
portion is called a route parameter—a placeholder in the endpoint for a value provided by the client.
With route parameters, we can now add these endpoints to our server
GET /api/fellows/:id
using app.get()
PATCH /api/fellows/:id
using app.patch()
DELETE /api/fellows/:id
using app.delete()
In each of the corresponding controllers, we can access the id
route parameter's value via the req.params
object.
When fetching a single fellow, we use the id
route parameter to find the fellow with the matching id
in our "database":
In our frontend, we can create adapters for each endpoint:
Sending a GET /api/fellows:id
request is straightforward!
puts it best: