Building RESTful APIs with Express
So, in this section, you learned that:
- REST(Representational State Transfer) defines a set of conventions for creating
HTTP services (CRUD operations):
- POST: to create a resource
- PUT: to update it
- GET: to read it
- DELETE: to delete it
- Express is a simple, minimalistic and lightweight framework for building web
servers.
Startup & installation
mkdir express-demo
npm init -y
npm i express
// Build a web server
const express = require(‘express’);
const app = express();
Ex 1:
const express = require("express");
const app = express();
/* http methods
app.get()
app.put()
app.post()
app.delete()
*/
app.get("/", (req, res) => {
// res.download('index.js') - to download a file from server
res.send("hello world");
});
app.get("/games/cricket", (req, res)=>{
res.send([1,2,3])
})
app.listen(3000, () => console.log("Listening on port 3000...."));
// Listen on port 3000
app.listen(3000, () => console.log(‘Listening…’));- We use Nodemon to watch for
changes in files and automatically restart the node process.
Nodemon – node monitor
npm i -g nodemon
nodemon index.js
// Reading the port from an environment variable
- We can use environment variables to store various settings for an application. To
read an environment variable, we use process.env.
const port = process.env.PORT || 3000;
app.listen(port);
const port = process.env.PORT || 3000;
app.listen(port, () => console.log(`Listening on port ${port}....`));
to set port on local machine: open cmd > set PORT=5000 > nodemon index.js
Route Parameters:
// ex: http://localhost:3000/api/courses/11
app.get("/api/courses/:id", (req, res) => {
res.send(req.params.id);
});
// ex: http://localhost:3000/api/courses/2018/feb
app.get("/api/courses/:year/:month", (req, res) => {
res.send(req.params); // params object
});
// ex: http://localhost:3000/api/courses?sortBy=name
app.get("/api/courses", (req, res) => {
res.send(req.query); // query string
});
app.get("/users", (req, res) => {
res.send(req.originalUrl);
});
// http://localhost:3000/users?name=decdier
app.get("/users", (req, res) => {
res.send(req.query.name);
});
Handling HTTP GET Request
const courses = [
{ id: 1, name: "course1" },
{ id: 2, name: "course2" },
{ id: 3, name: "course3" },
];
app.get("/api/courses", (req, res) => {
res.send(courses);
});
app.get("/api/courses/:id", (req, res) => {
const course = courses.find((c) => c.id === parseInt(req.params.id));
if (!course)
res.status(404).send("The course you are searching was not found");
res.send(course);
});
// Getting all the courses
app.get(‘/api/courses’, (req, res) => {
// To read query string parameters (?sortBy=name)
const sortBy = req.query.sortBy;
// Return the courses res.send(courses);
});
// Getting a single course
app.get(‘/api/courses/:id’, (req, res) => {
const courseId = req.params.id;
// Lookup the course
// If not found, return 404
res.status(404).send(‘Course not found.’);
// Else, return the course object
res.send(course);
});
Handling HTTP POST Request
// run postman => new => select post and paste url => body => raw => {"name": "new
course" } => then send
app.use(express.json()); // express.json() returns middleware and the app.use will
uses it
app.post("/api/courses", (req, res) => {
const course = {
id: courses.length + 1,
name: req.body.name,
};
courses.push(course);
res.send(courses);
});
// Creating a course
app.post(‘/api/courses’, (req, res) => {
// Create the course and return the course object
resn.send(course);
});
Input Validation
app.post("/api/courses", (req, res) => {
if (!req.body.name || req.body.name.length < 3) {
res.status(400).send("There was an error in the name property");
}
const course = {
id: courses.length + 1,
name: req.body.name,
};
courses.push(course);
res.send(courses);
});
Using joi package
- You should never trust data sent by the client. Always validate! Use Joi package
to perform input validation.
// npm i joi
const Joi = require("joi");
app.post("/api/courses", (req, res) => {
const schema = Joi.object({
name: Joi.string().min(3).required(),
});
const result = schema.validate(req.body);
if (result.error) {
res.status(400).send(result.error.details[0].message);
}
const course = {
id: courses.length + 1,
name: req.body.name,
};
courses.push(course);
res.send(courses);
});
Handling HTTP PUT Request
app.put("/api/courses/:id", (req, res) => {
const schema = Joi.object({
name: Joi.string().min(3).required(),
});
const { error } = schema.validate(req.body);
if (error) {
res.status(400).send(error.details[0].message);
return;
}
const course = courses.find((c) => c.id === parseInt(req.params.id));
if (!course) {
res.status(404).send("The course you are searching was not found");
}
course.name = req.body.name;
res.send(courses);
});
// Updating a course
app.put(‘/api/courses/:id’, (req, res) => {
// If course not found, return 404, otherwise update it
// and return the updated object.
});
Handling HTTP DELETE Request
app.delete("/api/courses/:id", (req, res) => {
const course = courses.find((c) => c.id === parseInt(req.params.id));
if (!course)
return res.status(404).send("The course you are searching was not found");
const index = courses.indexOf(course)
courses.splice(index, 1)
res.send(courses);
});
// Deleting a course
app.delete(‘/api/courses/:id’, (req, res) => {
// If course not found, return 404, otherwise delete it
// and return the deleted object.
});
// Writing routes in a separate folder using express.Router()
// router.route method:
Express: Advanced Topics
So, in this section, you learned that:
- A middleware function is a function that takes a request object and either
terminates the request/response cycle or passes control to another middleware
function.
In the server, REQUEST PROCESSING PIPELINE
=>Request=>midlleware functions: [json() => route()] => response
Ex1: here middleware function hadle the “/” route
app.get(
"/",
// => route handler function
(req, res) => {
res.send("welcome to vidly demo");
}
);
Ex2:
app.use(express.json());
// the express.json() will return middleware function
// it will parse the req, if there was a body in the req , then it will set
req.body
Ex3:
Ex4:
- You can create custom middleware for cross-cutting concerns, such as logging,
authentication, etc.
// Custom middleware (applied on all routes)
app.use(function(req, res, next)) {
// …
next();
}
app.use(function (req, res, next) {
console.log("Logging.....");
next();
});
app.use(function (req, res, next) {
console.log("Authentication");
next();
});
Ex 3: router.param middleware
Express has a few built-in middleware functions:
- json(): to parse the body of requests with a JSON payload
- urlencoded(): to parse the body of requests with URL-encoded payload
express.urlencoded({ extended: true }) is a middleware in Express.js used for
parsing data from HTML forms submitted via the POST method.
Use: It extracts form data from the request body, making it accessible in req.body.
This is essential when handling user inputs, such as login credentials or form
submissions, in an Express.js application.
- static(): to serve static files, like to view html pages,..
app.use(express.json());
app.use(express.urlencoded({ extended: true })); // to access body of the req
app.use(express.static("public")); // http://localhost:3000/readme.txt
Third party middleware functions:
https://expressjs.com/en/resources/middleware.html
app.use(helmet());
app.use(morgan("tiny"));
// Custom middleware (applied on routes starting with /api/admin)
app.use(‘/api/admin’, function(req, res, next)) {
// …
next();
}
Environment:
- We can detect the environment in which our Node application is running
(development, production, etc) using process.env.NODE_ENV and
app.get(‘env’). - The config package gives us an elegant way to store
configuration settings for
our applications.
console.log(process.env.NODE_ENV); // undefined
console.log(app.get("env")); // by default development
- We can use the debug package to add debugging information to an application.
Prefer this approach to console.log() statements.
/*
set NODE_ENV=production
*/
if (app.get("env") === "development") {
app.use(morgan("tiny"));
console.log("morgan enabled...");
}
Configuration:
// npm i config
const config = require("config");
Folder: config > default.json:
{
"name": "My Express App"
}
Congif > development.json:
{
"name": "My Express App - Development",
"mail" : {
"host" : "dev-ser-mail"
}
}
Config > production.json:
{
"name": "My Express App - Production",
"mail": {
"host": "pro-ser-mail"
}
}
Config > custom-environment-variables.json:
{
"mail": {
"password": "app_password"
}
}
Index.js
console.log("App name: " + config.get("name"));
console.log("mail: " + config.get("mail.host"));
console.log("App Password: " + config.get("mail.password"));
// set app_password=1234
Debugging:
// npm i debug
const startupDebbuger = require("debug")("app:startup");
const dbDebugger = require("debug")("app:db");
const morgan = require("morgan");
const express = require("express");
const app = express();
if (app.get("env") === "development") {
app.use(morgan("tiny"));
startupDebbuger("morgan enabled...");
}
// some db work...
dbDebugger("Connected to db");
const port = process.env.PORT || 3000;
app.listen(port, () => console.log(`Listening on port ${port}....`));
// set DEBUG=app:startup
// set DEBUG=app:db
// set DEBUG=app:*
Templating Engine:
- To return HTML markup to the client, use a templating engine. There are various
templating engines available out there. Pug, EJS and Mustache are the most
popular ones.
Ex1: pug
Views > index.pug
html
head
title= title
body
h1= message
index.js
// npm i pug
const morgan = require("morgan");
const express = require("express");
const app = express();
if (app.get("env") === "development") {
app.use(morgan("tiny"));
}
app.set("view engine", "pug");
app.set("views", "./views");
app.get("/", (req, res) => {
res.render("index", { title: "My Express App", message: "Hello" });
});
const port = process.env.PORT || 3000;
app.listen(port, () => console.log(`Listening on port ${port}....`));
Ex2: ejs
index.js
Database Integration:
https://expressjs.com/en/guide/database-integration.html
Structuring Express Application:
Middleware> logger.js
function log(req, res, next) {
console.log("Logging.....");
next();
}
module.exports = log;
routes > genres.js
const express = require('express')
const router = express.Router()
const Joi = require("joi");
const genres = [
{ id: 1, genre: "Action" },
{ id: 2, genre: "Horror" },
{ id: 3, genre: "comedy" },
{ id: 4, genre: "sci-fi" },
];
router.use(express.json());
router.get("/", (req, res) => {
res.send(genres);
});
router.post("/", (req, res) => {
const schema = Joi.object({
genre: Joi.string().min(3).required(),
});
const result = schema.validate(req.body);
if (result.error)
return res.status(400).send(result.error.details[0].message);
genres.push({
id: genres.length + 1,
genre: result.value.genre,
});
res.send(genres);
});
router.put("/:id", (req, res) => {
const { id } = req.params;
const genreIndex = genres.findIndex((g) => g.id === parseInt(id));
if (genreIndex === -1)
return res.status(404).send("The genre ID you are searching is not found");
const schema = Joi.object({
genre: Joi.string().min(3).required(),
});
const result = schema.validate(req.body);
if (result.error)
return res.status(400).send(result.error.details[0].message);
genres[genreIndex].genre = result.value.genre;
res.send(genres);
});
router.delete("/:id", (req, res) => {
const { id } = req.params;
const genreById = genres.findIndex((g) => g.id === parseInt(id));
if (genreById === -1)
return res.status(404).send("The genre ID you are searching is not found");
genres.splice(genreById, 1);
res.send(genres);
});
router.get("/:id", (req, res) => {
const { id } = req.params;
const genreById = genres.find((g) => g.id === parseInt(id));
if (!genreById)
return res.status(404).send("The genre ID you are searching is not found");
res.send(genreById);
});
module.exports = router
routes > home.js
const express = require("express");
const home = express.Router();
home.get("/", (req, res) => {
res.send("welcome to vidly demo");
});
module.exports = home
index.js
const express = require("express");
const app = express();
const genres = require("./routes/genres");
const home = require("./routes/home");
const log = require("./middleware/logger");
app.use(log);
app.use("/", home);
app.use("/api/genres", genres);
const port = process.env.PORT || 3000;
app.listen(port, () => console.log(`The connection is running on ${port}`));