Express crash course
Updated:
✒ Reference:Express crash course by Traversy Media
Express API Reference
What is Express?
A fast, unopinionated, and minimalist back-end framework for Node.js. It can be combined with client-side frameworks like React, Angular & Vue to build full stack applications.
You can build an API with Express so that it takes request from the front-end and serves back data in JSON format.
- Unopinionated: not a high level framework, does not require any fixed design, you have full control of handling the requrest to the server.
Why use Express?
- Light, fast, free, and popular
- Makes building web apps with Node.js much easier: full control of request and response
- Used for both APIs with JSON data and back-end applications
- It’s all JavaScript; great to use with client side frameworks
Basic server syntax
const express = require('express');
// Init express
const app = express();
// Create your endpoints/route handlers
app.get('/', function(req, res) {
res.send('Hello World!');
})
// Listen on a port
app.listen(5000);
Basic route handling
What can you do within your route?
- Fetch data from a database (MongoDB, MySQL, etc)
- Load pages
- Return JSON data
- Full access to request and response
req
object represents the HTTP request properties for things like URL parameters, query strings, any data sent within the body, HTTP headers, etc.res
object represents the HTTP response, and you can use this object to send back JSON data, return template, redirect, etc.
- Parse any incoming data with the Body Parser
- Routs can be stored in separate files and exported, since Express has a router.
Using nodemon
Once the following code is added to package.json
, you can run the file in developer mode via npm run dev
. (npm run start
is used for deployment)
"scripts": {
"start": "node index",
"dev": "nodemon index"
}
Creating a static server with Express
Postman is used: an HTTP client to make requests to the server (get, put, post, delete, request, etc.)
const express = require('express');
const app = express();
// Look for the environment variable, and set it to PORT
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server started on ${PORT}`));
localhost:5000
will show Cannot GET /
, which means that it cannot find a route handler for /
(index page).
const express = require('express');
const app = express();
// Create a route to go to a web page
app.get('/', (req, res) => {
res.send('<h1>Hello World!</h1>');
});
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server started on ${PORT}`));
Instead of adding a single h1, you can add a whole HTML file.
// Create a route to go to a web page
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'index.html'));
});
However, this is not ideal since you have to put a route manually for every single page. Express has functionality to make a certain folder as the static folder.
// Create a route to go to a web page by setting a static folder
// .use() is a method used to include a middleware.
app.use(express.static(path.join(__dirname, 'public')));
Any file within the public folder can be open without manually adding the route. i.e. localhost:5000/about.html
will show about.html
saved within the public folder. Instead of handling the content types and paths, you can just put the files in the public folder!
However, creating a static server that creates regular html files is not what you will usually use Express for. You will mostly create JSON APIs to connect from a front-end or render templates where you can insert dynamic data.
Middleware functions
A middleware is a stack of functions that executes whenever a request is made to a server. These functions have access to the request and response objects. Express has its own middleware, just like how 3rd party packages do. It takes three arguments: req
, res
, and next
. next()
calls the next function in the stack.
Middlewares can:
- Execute any code
- Make changes to the request/response objects
- End response cycle
- Call next middleware in the stack (with the next function)
const moment = require("moment");
const app = express();
// Create a middleware that prints the url
const logger = (req, res, next) => {
console.log(
`${req.protocol}://${req.get("host")}${
req.originalUrl
}: ${moment().format()}`
);
next();
};
Everytime a request is made, the logger is executed. If a GET request is made (i.e. GET http://localhost:5000/api/members
), it will print something like http://localhost:5000/api/members: 2021-12-15T19:40:33+09:00
to the console.
GET request
Creating a JSON API
Data used:
// Members.js const members = [
{
id: 1,
name: 'John Doe',
email: 'john@gmail.com',
status: 'active'
},
{
id: 2,
name: 'Bob Williams',
email: 'bob@gmail.com',
status: 'inactive'
},
{
id: 3,
name: 'Shannon Jackson',
email: 'shannon@gmail.com',
status: 'active'
},
];
module.exports = members;
Getting the members list
The code below gets all members, by returning the members as a JSON file. res.json(members)
will return a JSON file without using JSON.stringify
. You can send a GET request on http://localhost:5000/api/members
on Postman, and retrieve the members list in a JSON file.
// index.js
const express = require('express');
const path = require('path');
const members = require('./members');
// Gets all members
app.get('/api/members', (req, res) => res.json(members));
// Set static folder
app.use(express.static(path.join(__dirname, 'public')));
// Look for the environment variable, and set it to PORT
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server started on ${PORT}`));
If you send a GET request on http://localhost:5000/api/members
using nodemon, you can retrieve the members in a JSON file.
// Get single member
app.get("/api/members/:id", (req, res) => {
// :id represents an input of any id.
// req.params.id returns the id of the request as a string.
const found = members.some(member => member.id === parseInt(req.params.id));
if (found) {
// If the id exists, return the JSON representation of the member.
res.json(members.filter(member => member.id === parseInt(req.params.id)));
} else {
// If it doesn't, change the status and send a message
res.status(400).json({ msg: `No member with the id of ${req.params.id}` })
}
})
Using a router
You can separate the router to a different file.
// index.js
const express = require("express");
const path = require("path");
const app = express();
// Set static folder
app.use(express.static(path.join(__dirname, "public")));
// Members API Routes
// first param: route, second param: file
app.use('/api/members', require('./routes/api/members'));
// Look for the environment variable, and set it to PORT
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server started on ${PORT}`));
Getting a single member
// members.js in routes/api
const express = require('express');
const router = express.Router();
const members = require("../../members");
// Gets all members
router.get("/", (req, res) => res.json(members));
// Get single member
router.get("/:id", (req, res) => {
const found = members.some(member => member.id === parseInt(req.params.id));
if (found) {
res.json(members.filter(member => member.id === parseInt(req.params.id)));
} else {
res.status(400).json({ msg: `No member with the id of ${req.params.id}` })
}
})
module.exports = router;
POST request
Using a body parser
// members.js
// Send the given data to response
router.post('/', (req, res) => {
res.send(req.body);
});
The code above sends the body of the request to the response.
Sending a POST request using postman:
You need to use a body parser to parse the data sent into the body. You can initialize the parser as a middleware.
// index.js
// Body Parser Middleware
app.use(express.json()); // handle raw json
app.use(express.urlencoded({ extended: false })); // handle form submissions
Adding a member
// Create member
router.post('/', (req, res) => {
// Add a new member
const newMember = {
id: uuid.v4(),
name: req.body.name,
email: req.body.email,
status: 'active'
}
// Bad request if name or email nonexisting
if (!newMember.name || !newMember.email) {
return res.status(400).json( { msg: 'Please include a name and email' });
}
// Add the new member to the array
members.push(newMember);
// Return the entire array
res.json(members);
});
When the email is not provided: When both name and email are provided:
When dealing with new API, you deal with real database instead of a file with your data. In that case, you install a package to connect to the database (i.e. Mongoose for MongoDB). Rather than pushing the new member to the array, you use a method such as members.save(newMember)
.
PUT request
Updating a member
// Update member
router.put("/:id", (req, res) => {
// Check to see if the member exists.
const found = members.some(member => member.id === parseInt(req.params.id));
if (found) {
// If the id exists,
const updMember = req.body;
members.forEach(member => {
// If the member has the same id as the given id, then change the updated parameter.
if (member.id === parseInt(req.params.id)) {
member.name = updMember.name ? updMember.name : member.name;
member.email = updMember.email ? updMember.email : member.email;
res.json( {msg: 'Member updated', member });
}
});
} else {
// If it doesn't, change the status and send a message
res.status(400).json({ msg: `No member with the id of ${req.params.id}` })
}
})
Sending a PUT request using postman:
DELETE request
// Delete member
router.delete("/:id", (req, res) => {
const found = members.some((member) => member.id === parseInt(req.params.id));
if (found) {
// If the id exists, filter out the id and
// return all the members except the one that was deleted
res.json({
msg: "Member deleted",
members: members.filter(
(member) => member.id !== parseInt(req.params.id)
),
});
} else {
// If it doesn't, change the status and send a message
res.status(400).json({ msg: `No member with the id of ${req.params.id}` });
}
});
Sending a DELETE request using postman:
Server-rendered templates
You usually build (1) an API to connect to the frontend or (2) a complete server-side app where you use templates. The following parts explain the latter.
You can use the handlebars
module (installed by npm install express-handlebars
) and the Bootstrap template (the CSS link inserted in main.handlebars
).
Rendering the members
First, initiate the handlebars middleware.
// In index.js
var express = require('express');
var exphbs = require('express-handlebars');
var app = express();
app.engine('handlebars', exphbs());
app.set('view engine', 'handlebars');
app.get('/', function (req, res) {
res.render('index', {
res.render('index', {
title: 'Member App',
members
});
});
app.listen(3000);
Create main.handlebars
file in views/layouts
folder.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0"><!-- CSS only -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<title>Members App</title>
</head>
<body>
<!-- Triple curly braces around 'body'
wherever you want to output the rest of your views with handlebars-->
<!-- mt-4 is a bootstrap class -->
<div class="container mt-4">
}
</div>
</body>
</html>
Create index.handlebars
file in views
folder.
<!-- To use variables in handlebars, use double curly brackets -->
<h1 class="text-center mb-3"></h1>
<h4>Members</h4>
<ul class="list-group">
<li class="list-group-item">: </li>
</ul>
Now the members are loaded at localhost:3000
.
Creating a form
You can make a request to API from the form.
<!-- index.handlebars -->
<h1 class="text-center mb-3"></h1>
<!-- You can send the JSON from a form instead of Postman. -->
<form action="/api/members" method="POST" class="mb4">
<div class="form-group">
<label for="name">Name</label>
<input type="text" name="name" class="form-control">
</div>
<div class="form-group">
<label for="email">Email</label>
<input type="email" name="email" class="form-control">
</div>
<input type="submit" value="Add Member" class="btn btn-primary btn-block">
</form>
<h4>Members</h4>
<ul class="list-group">
<li class="list-group-item">: </li>
</ul>
Now you have a form on your page.
Once a form is submitted, the relevant data will be sent to routes/api/members.js
to hit router.post('/', (req, res) => {...}
. However, if you are using server rendered views, you will redirect back to the index page instead of returning a JSON.
// Static folder: Return the entire array
res.json(members);
// Server-rendered view:
res.redirect('/');
You can also use Passport.js as your template engine. (passport-local for server-side app)
Leave a comment