It's time to build a backend. We'll do so by using a NPM package named Express.
Express is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications.
With a myriad of HTTP utility methods and middleware at your disposal, creating a robust API is quick and easy.
Express provides a thin layer of fundamental web application features, without obscuring Node.js features that you know and love.
An API server will allow us to Create, Read, Update, Delete(C.R.U.D.)
a resource/entity/foo
in our database through a URL
.
Route Name | URL | HTTP Verb | Description |
Index |
|
| Get a list of |
Create |
|
| Create a new |
Show |
|
| Get a detailed |
Edit |
|
| Update a property/attribute/params of a |
Destroy |
|
| Delete/Destroy/Remove an individual |
The URLs can also "listen" for query parameters
and produce dynamic results/output/data
.
Hint: try console.log the req object and checkout body, params, query
We must also handle the following URLs.
Route Name | URL | HTTP Verb | Description |
Index |
|
| Get some |
Search for spam |
|
| Find a foo where it's title/name/description contains |
English Foos |
|
| Get some |
English Foos sorted |
|
| Get some |
const express = require("express");
const path = require("path");
const cookieParser = require("cookie-parser");
const logger = require("morgan");
const cors = require("cors");
const indexRouter = require("./routes/index");
const app = express();
app.use(cors());
app.use(logger("dev"));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, "public")));
app.use("/news", indexRouter);
CORS is a node.js package for providing a Connect / Express middleware that can be used to enable CORS with various options It help us disable CORS error. Readmore here
Logger is a technique in programming to keepin log of how an program running. It contain many errors such as process, success and error.
Morgan is an HTTP request logger middleware for node.js Create a new morgan logger middleware function using the given format and options. The format argument may be a string of a predefined name (see below for the names), a string of a format string, or a function that will produce a log entry.
The format function will be called with three arguments tokens, req, and res, where tokens is an object with all defined tokens, req is the HTTP request and res is the HTTP response. The function is expected to return a string that will be the log line, or undefined / null to skip logging. Readmore here
In Node.js, the path module is an inbuilt module which deals with path and directories. Every Operating System has its own way of managing paths and the operations related to it.
For examples different OS will have different syntax to navigate in the file system. Path module help Node to adapt on the native OS syntax
Readmore here
This module help expose the cookies send along with client request. Which may use to help authorization , cache , ... Parse Cookie header and populate req.cookies with an object keyed by the cookie names. Optionally you may enable signed cookie support by passing a secret string, which assigns req.secret so it may be used by other middleware. Readmore here
In practices an api server may have multiple endpoints. We tend to group endpoint with the same nature together. For examples, if we have the same amount of routes for each feature Create, Read, Update, Delete like the exercise. Then apply that for:
Since each endpoints listed above serving 1 type of data. It is recommended that we group them in separated file
Commonly ruotes folder will contain all endpoints and index will connected to each one and act as a mini router in our backend.
|- ruotes/
|- index.js
|- users.api.js
|- authors.api.js
|- movies.api.js
|- news.api.js
Since we are working with multiple files in our codebase, it is very important that we understand how each line and file relate to eachother. A request from client will always go through its specific route to a result reponsding (success or error)
Here is A flow chart on how express file work that may help us to visuallize the process of the files
next()
So we have explore req
res
the only thing left in the Express Router callback arguments is next()
In short, next()
is a method, when called, start executing/calling/running the next middlewares
in the line.
const express = require("express");
const app = express();
app.get("/same", function (req, res, next) {
console.log("before request handler");
next();
});
app.get("/same", function (req, res, next) {
console.log("handling request");
res.sendStatus(200);
next();
});
app.get("/same", function (req, res, next) {
console.log("after request handler");
next();
});
app.listen(3000, function () {
console.log("Example app listening on port 3000!");
});
http://localhost:3000/user/123
before request handler handling request after request handler
Try comment out the call to next() in the middle handler like this:
app.get("/user/:id", function (req, res, next) {
console.log("handling request");
res.sendStatus(200);
//next();
});
before request handler handling request
Notice that the last handler (the one that prints after request handler) does not run. That's because you are no longer telling express to run the next handler.
So it doesn't really matter if your "main" handler (the one that returns 200) was successful or not, if you want the rest of the middlewares to run, you have to call next().
Remember that node is async so it can't know when the first handler's callback has finished. You have to tell it by calling next().
Again here is A flow chart on how express file work that may help us to visuallize the process of the files
The final puzzle in our Express code is Error handling middleware, which help us to handle in the event of error happen in our server code
app.use((err, req, res, next) => {
console.log("ERROR", err.message);
return res.send(err.message);
});
An error handling middleware of express will have 4 arguments. Notice the err
argument, is carrying the Error
object passed by method before it.
We will once again using this flow chart on how express file work to undestand the logic of Error handling
We doing a task with our server, no one could make sure that it will always success. So in the case of failure , we must be able to identify and handle it. Recall try-catch
in previous weeks, wrapping tasks inside of try-catch
help us control when "thing go wrong, do this instead of crashing my precious server".
try {
//dosomethiing that may be success or fail
} catch (err) {
//dosomething when it fail and have err object
next(err); // calling next-in-line-error-handling-middleware
}
Which is this middlewares in app.js
app.use("/api",ABCXYZ) // see how this middlewares and it handler ABCXYZ run in prior
...
app.use((err, req, res, next) => {
console.log("ERROR", err.message);
return res.send(err.message);
});
So sometime we want to make an error just for fun we could use
throw new Error("error message");
and inside a try-catch
your new error will be catch by the error catch handler.
try {
//dosomethiing that may be success or fail
//but too lazy to do anything
throw new Error("too lazy");
} catch (err) {
//dosomething when it fail and have err object
console.log(err.message); // too lazy
next(err); // calling next-in-line-error-handling-middleware
}
Hey there, travelers! if you want to learn everything about Express and RestfulAPI that has not been covered (lots of it), check out the links below to have better understanding of the concepts.