Using REST API with Express
TIL 2021.02.24
Day 76: Treehouse Full Stack JavaScript Techdegree
REST API INTRO RECAP:
INTRO TO REST APIs
Traditional Web Applications
- Handles both server side and client side concerns
- Example: Favorite Recipes Web App
- Click on a URL and the browser makes a request to the server and the server responds by finding the recipe data in a database, assembling the data into HTML templates and sending that HTML back to the browser to be displayed.
- But what if we want to do the same with a mobile app? This is where REST API comes in
When we request information from a RESTful Application, the application only responds with the recipe data — typically in the JSON format. Sending JSON data rather than HTML means that the backend only has to be built once. While any number of front end applications can consume and display the data
If we have a REST API for recipes we could create a recipe website, but we could also use the same data to create a small meal planning app, calorie tracking app, a virtual cookbook and so much more. Making use of the REST API makes everything more flexible in a way that we are only sending out the data.
Another example is the Twitter app on your phone, the twitter’s REST API to get the tweets before you see them — the JSON from twitter’s API is formatted and displayed on your phone. But the Twitter API allows the same tweet data to be accessed and used by a web browser, smart tv, etc.
REST APIs can provide data and content for rich web applications, mobile apps and other server side applications.
Rest API
- Lets you retrieve data and present that data in any way you want, providing more flexibility.
WHAT IS A REST API?
How information is transferred to the web:
Client (application requesting information from a server)
REST (Representational State Transfer): A set of ideas about how a server should respond to a request from a client — A traditional web application responds with HTML while a REST API responds with data and it is up to the framework on how to display the data from the API
Example: Random User Generator / Employee Directory Built in the Earlier Section of the FSJS
The flexibility of REST API is that once we receive the data — we can do whatever we want from it
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
HTTP METHODS AND CRUD OPERATIONS
Web applications we use everyday like Facebook, instagram, and twitter follow a common pattern: CRUD (Create, Read, Update and Delete)
REST APIs allow a client to manipulate data using actions that map closely to the idea of CRUD — GET POST PUT DELETE which are known as HTTP VERBS OR HTTP METHODS
Information in a REST API is typically organized using nouns that describe what the data represents, nouns like users, posts, movies, score, games — which are Resources
Using HTTP methods and API nouns, a client can tell a REST API what information it wants and what it wants to do with that information.
If a client sends a GET request to the resource/movies, it means that i expects to read or view data representing the collection of movies
POST request means that it wants to create a new movie
PUT means it wants to update an existing movie
DELETE means it wants to delete an existing movie
It takes in requests and response with JSON objects
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
A SIMPLE API
A RESTful API responds with data — most of the time being a JSON object
The express function adds a bunch of methods to Node’s HTTP server to make it easier for us to receive and respond to requests. These methods include GET, POST, PUT and DELETE.
First let’s do a GET method — it takes two arguments, a route as the first argument and a callback function as a second argument (This is the callback function we want to run when this request comes in and how we want to respond.)
The callback function takes two arguments
- Incoming request from the client (req)
- Outgoing response from the server (res)
Usually we would do res.render(“template_name”) but we don’t want to do that because we are building a REST API. So inside the callback function we want to send back JSON. And to do that we can use the JSON method available on the response object. We can pass the JSON method an object and it will convert that object into JSON format and send it to the client.
We can see that our API is sending JSON when we make a request.
The browser by default sends a GET request — by visiting /greetings we’re sending a GET request to the route which we’ve programmed in our Express application to send back a JSON object containing a greeting
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
PLANNING OUR REST API
We want the client to be able to send a GET request to /quotes to read a list of quotes
//Send a GET request to /quotes to READ a list of quotes
//Send a GET request to /quotes/:id to READ(View) a Quote
//Send a POST request to /quotes CREATE a new Quote
//Send a PUT request to /quotes/:id UPDATE an existing Quote
//Send a DELETE request to /quotes/:id to DELETE an existing Quote
//Send a GET request to /quotes/quote/random to READ(View) a random Quote
Notice that these endpoints are mostly the same, this is because each endpoint can have multiple HTTP methods — the HTTP method, the action, will tell Express what to do and how to respond to the request
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
GET A QUOTE, GET ALL QUOTES
Now let’s get a single quote
One way to do it:
Or we can use the find method
Using the browser we’re sending a GET request to our Express server with the ID number of the quote we’re looking for
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
MANAGING REST API DATA
Depending on the database you choose, you would likely choose some kind of library known as an ORM (Object Relational Mapping) to facilitate the communication between your Express App and your Database.
An ORM makes it easier to retrieve, manipulate, save and validate the data in your database
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
REFACTOR THE GET ALL ROUTES QUOTE
On records.js
We’re calling records.getQuotes() and saving the result to a variable called quotes — records.getQuotes() reaches into our data store, gets all the quotes and returns them to us. Once we have all those quotes, we send them back to the client as JSON
Note: all the functions in the records.js use asynchronous functions to closely mimic the behavior of an actual database
The getQuotes function returns a promise which uses node’s readFile method to retrieve the data from the data.json file then resolves the promise with that data
We used async await to tell JavaScript to wait until we get all the code or data we need from the getQuotes function then execute the next line of code which is to return the response as a JSON
await tells JavaScript to expect the method to return something, so it should wait until the method to return something to execute the next line of code
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
REFACTOR THE GET ONE QUOTE ROUTE
records.js
routes
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
USING POSTMAN TO TEST ROUTES
Postman is an application that will allow us to send a variety or requests to our API so that we can write endpoints to create, update and delete quotes. And then make sure our application is working as we expect
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
CREATE A NEW QUOTE
Notice that we have the same route /quotes for the get and post request
When the get request is sent, it will execute that code. When a post request is sent, it will execute that code. Even if they have the same route, once called, they perform or render different things.
POST request
And now once we use a get request to get all the quotes, we have what we created from the post request earlier
OR
Similar to how req.params work, express takes information from the client and makes it available to us on the request object on a property called body
Express takes information from the client and makes it available on the request object. But it doesn’t do it automatically
To be able to access values on req.body we would need some middleware.
So on top of the file
When a request comes in, it’ll be sent through this function before it hits any of the routes
This middleware tells express that we’re expecting requests to come in as JSON. That way express can take the JSON we’re sent, via the request body, and make it available to us on the request object on the property called body
On Postman
And once we hit send
And it is now on our data.json
Review:
We are making a POST request containing JSON for a new quote with postman, our client. We are then accessing that information through express with req.body and we’re setting those values to a new object that we’re passing to createQuote() function. Then we are storing our newly created quote to a quote variable and sending it back to the client.
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
USING TRY/CATCH WITH ASYNC/AWAIT
We need to find a way to catch any errors that happens in our await function
Let’s throw a fake error
It works, but notice that the status code is still 200
We will tackle this on the next video about status codes
Next let’s add a try/catch block on our get single quote route
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
HTTP STATUS CODES
HTTP status codes gives the client important information about the request and response cycle
200 range: everything went as expected
400 range: something went wrong on the client side — something went wrong with the request, like a missing resource or the wrong information was sent
500 range: something went wrong on the server side — something unexpected happened that prevented the server from fulfilling the request
Common Status Codes
- 200 — OK (The request was successful)
- 201 — Created (A new resource has been created)
- 400 — Bad Request (The request was malformed or missing information)
- 404 — Not Found (The resource does not exist)
- 500 — Internal Server Error
Even though there is an error, you can see that the status code is 200 — this means that everything went find, there are no problems — this is the code that express sends by default but we have the power to choose which status codes we send in case of an error or to give consumers of our API useful information about the requests.
We can send a different status code by using the status method
res.status(code)
And now on postman, if we try and make another post request, knowing that there will automatically be an error because of what we set
It now displays the 500 status code for internal server error
Note we can also chain these by doing res.status(500).json()
Also we can send a better status code than 200 if the POST request was successful and that would be the 201 status code for created
And now we will get this
We now have the 201 created status code — or a new resource was created
There is some error handling we could do on our single quote route
We are doing this because if we, for example, try and get a single quote with a fake id parameter, it will return no json as well as a status code of 200 (since this is the default status code express provides). So we are basically handling the error that if the quote exists we will return the json response and if not we will set the status code as well as the json res as an error message.
A status code of 400 means bad request — something is wrong with the syntax of the request, information was missing or some sort of information validation has failed
Without the error handling above, if we request a quote with an id that does not exist — we get back nothing, a blank screen and a 200 status code
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
EDIT A QUOTE
A PUT request indicates to our application that a change is going to be made on an existing resource.
For a PUT request, it is a convention to send a status of 204 — which means no content. This generally means that everything went okay, but there’s nothing to send back.
Also, it is convention not to respond with anything when doing PUT requests.
The datastore simply gets updated and we send back a status code indicating that the update went as expected.
We need another way to end the request, or the server will hang indefinitely. We can end the request with the Express end method — which simply tells express that we’re done.
Let’s edit the quote with the ID of 8721
Prior to updating
We’ll edit it by adding a “jr” to the author’s name
And once we send it
We receive this as a response — a status code of 204 No Content(Per the convention of using the PUT method) and a body of blank which is also a convention when using the PUT request.
And we can now see that it is updated.
So we are finding the quote by ID, taking information the client has sent us and using it to update the quote. Passing the new quote to the updateQuote method to be saved. In a real world situation, we would want to validate the information that was sent to us like has the client sent back data for all the required properties — we do this in ORM
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
DELETE A QUOTE
Similarly to using the PUT route, we will follow the convention of the status 204 and no content for the body
We want to delete the quote with the id 2295
And it is now deleted!
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
WRITING A GLOBAL ERROR HANDLER
There are two types of errors that we’re dealing with
Request Error 404
Or
Server Error 500
For our API we are going to create our own custom error handler so that we can send the error message as a JSON object
Now let’s try and create an error on our delete request
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
REFACTOR WITH EXPRESS MIDDLEWARE
We can use a piece of middleware to prevent using the try/catch block on every single route
We are using an asyncHandler
It takes in a function, wraps it in a try/catch block and passes any errors to the global error handler we wrote
We can use this middleware function in every route instead of using the try/catch block every single time
The asyncHandler will be our callback function, when a request is made, the home route will run the asyncHandler.
We have to wrap this around the asyncHandler function
Inside the asyncHandler function we’ll pass the anonymous function
We don’t have to do the try/catch block anymore since the asyncHandler is the one performing the try/catch block
This is exactly the same code we had before, only the asyncHandler is acting as the middleman here. We passed it the anonymous function containing the code that we want to run when the request is made and it is going to call that function in the try/catch blocks and catch errors for us.
And because we want to await the creation of the record inside of the anonymous function, the anonymous function also needs to be asynchronous
For the PUT request
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
STRUCTURING YOUR REST API
Notice that when we access information through an API, we request information from a /api route.
We can set up our API the same way within an express router. Which is created using a method called express.router
Doing it this way will be beneficial,
We can map each of our routes to an endpoint that starts with /api without having to repeat /api for each route.
It will keep our express files more modular
- Create a routes.js file
- Move all routes except the error handler
2. Require express
- const express = require(“express”);
3. And back on app.js: recall that we can use app.use to tell express that we want to add a piece of middleware. This time we are going to specify middleware only to be used if the request route starts with a certain path
- app.use(“/api”, routes);
- Here we are saying when a request starts with a path /api, use the routes inside of the routes.js file
4. Import routes.js file on app.js
- const routes = require(“./routes.js”);
5. Back on routes.js we will set up a new router
- The router method allows us to route everything to /api without having to specify it on every single route
- const router = express.Router();
6. Replace each route that says app to router
7. Export the router at the bottom of the file
- module.exports = router;
Created a new module named routes
Moved all our quote routes to it
We’re using the express router method which allows us to map these routes to a specified path, in this case /api
We’re assigning express router to a variable called router and using it handle our various types of requests
Finally, we exported the router through module.exports = router
And on App.js we are importing the file we just made and saying we want to use the routes in that file whenever a request comes in that starts with /api
Now let’s try it out
We get an error if we try to make a GET request to our /quotes route
Let’s add /api at the start
And it works!
This is a standard way to structure and modularize an API in express that will allow our app to easily grow
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
GET RANDOM QUOTE
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —