Node.js REST Examples
Review REST Methods
- GET - Retrieve resource(s) from the server
- POST - Create a new resource on the server
- PUT - Update an existing resource on the server
- DELETE - Delete a resource on the server
- HEAD - Retrieve metadata from the server
- OPTIONS - Retrieve the supported HTTP methods for a resource
- PATCH - Partially update an existing resource on the server
Basic Express REST API
GET example
npm init -y
npm install express body-parser --save
const express = require('express');
// import * as express from 'express';// Typescript
const bodyParser = require('body-parser');
const app = express();
// parse application/json
app.use(bodyParser.json());
// define your routes here
app.get('/', (req, res) => {
res.send('Hello World!');
});
// start the server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
Typescript setup
- Install node.js and npm
mkdir myproject
cd myproject
- Install NPM
npm init
- Install typescript, ts-node and nodemon
npm install typescript ts-node @types/node nodemon --save-dev
- install tsconfig.json
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"outDir": "dist",
"strict": true,
"esModuleInterop": true
},
"include": ["src/**/*.ts"]
}
- Create src directory with index.ts
console.log('Hello, world!');
- Add a start script to your package.json file:
{
"scripts": {
"start": "nodemon --exec ts-node src/index.ts"
}
}
- Run the TypeScript compiler to compile your code:
npx tsc
- Run server with npm start
npm start
GET example in Typescript
npm install express @types/express --save
npm install typescript --save-dev
npx tsc
import express, { Request, Response } from 'express';
const app = express();
// Dummy data to simulate a database
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Charlie' },
];
// GET /users - Returns all users
app.get('/users', (req: Request, res: Response) => {
res.json(users);
});
// Start the server
const port = process.env.PORT || 3000;
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
node dist/index.js
Or use ts-node to run directly
npm install ts-node --save-dev
npx ts-node src/index.ts
POST example in Typescript
import express, { Request, Response } from 'express';
import bodyParser from 'body-parser';
const app = express();
app.use(bodyParser.json());
// Dummy data to simulate a database
const products = [
{ id: 1, name: 'Product 1', price: 10 },
{ id: 2, name: 'Product 2', price: 20 },
{ id: 3, name: 'Product 3', price: 30 },
];
// GET /products - Returns all products
app.get('/products', (req: Request, res: Response) => {
res.json(products);
});
// POST /products - Adds a new product
app.post('/products', (req: Request, res: Response) => {
// Parse the request body
const { name, price } = req.body;
// Add the new product to the array
const newProduct = { id: products.length + 1, name, price };
products.push(newProduct);
// Return the new product with a 201 status code
res.status(201).json(newProduct);
});
// Start the server
const port = process.env.PORT || 3000;
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
Node.js Express middleware
- Body-parser
- This middleware parses the request body and makes it available in the req.body object. It supports JSON, URL-encoded, and multipart form data.
npm install body-parser --save
var express = require('express')
var bodyParser = require('body-parser')
var app = express()
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }))
// parse application/json
app.use(bodyParser.json())
app.use(function (req, res) {
res.setHeader('Content-Type', 'text/plain')
res.write('you posted:\n')
res.end(JSON.stringify(req.body, null, 2))
})
var express = require('express')
var bodyParser = require('body-parser')
var app = express()
// create application/json parser
var jsonParser = bodyParser.json()
// create application/x-www-form-urlencoded parser
var urlencodedParser = bodyParser.urlencoded({ extended: false })
// POST /login gets urlencoded bodies
app.post('/login', urlencodedParser, function (req, res) {
res.send('welcome, ' + req.body.username)
})
// POST /api/users gets JSON bodies
app.post('/api/users', jsonParser, function (req, res) {
// create user in req.body
})
- CORS
- This middleware adds Cross-Origin Resource Sharing (CORS) headers to responses, allowing the API to be accessed from other domains.
- https://www.npmjs.com/package/cors
- Enable CORS for a Single Route
- Configuring CORS
- Configuring CORS Asynchronously
- Enabling CORS Pre-Flight
- Configuring CORS w/ Dynamic Origin
npm install cors --save
var express = require('express')
var cors = require('cors')
var app = express()
app.use(cors())
app.get('/products/:id', function (req, res, next) {
res.json({msg: 'This is CORS-enabled for all origins!'})
})
app.listen(80, function () {
console.log('CORS-enabled web server listening on port 80')
})
var express = require('express')
var cors = require('cors')
var app = express()
app.get('/products/:id', cors(), function (req, res, next) {
res.json({msg: 'This is CORS-enabled for a Single Route'})
})
app.listen(80, function () {
console.log('CORS-enabled web server listening on port 80')
})
- Compression
- Compresses responses using gzip or deflate, reducing the amount of data sent over the network and improving performance.
npm install compression --save
- Helmet
- Security-related HTTP headers to responses, helping to protect against common web application vulnerabilities like cross-site scripting (XSS) and cross-site request forgery (CSRF).
- Content-Security-Policy
- Cross-Origin-Embedder-Policy
- Cross-Origin-Opener-Policy
- Cross-Origin-Resource-Policy
- Origin-Agent-Cluster
- Referrer-Policy
- Strict-Transport-Security
- X-Content-Type-Options
- X-DNS-Prefetch-Control
- X-Download-Options
- X-Frame-Options
- X-Permitted-Cross-Domain-Policies
- X-Powered-By
- X-XSS-Protection
npm install helmet --save
import express from "express";
import helmet from "helmet";
const app = express();
// Use Helmet!
app.use(helmet());
app.get("/", (req, res) => {
res.send("Hello world!");
});
- Morgan
- Logs HTTP requests and responses to the console or a file, making it easier to debug and monitor the application.
- https://www.npmjs.com/package/morgan
- log file rotation
- use custom token formats
- split / dual logging
npm install morgan --save
var express = require('express')
var morgan = require('morgan')
var app = express()
app.use(morgan('combined'))
app.get('/', function (req, res) {
res.send('hello, world!')
})
var express = require('express')
var fs = require('fs')
var morgan = require('morgan')
var path = require('path')
var app = express()
// create a write stream (in append mode)
var accessLogStream = fs.createWriteStream(path.join(__dirname, 'access.log'), { flags: 'a' })
// setup the logger
app.use(morgan('combined', { stream: accessLogStream }))
app.get('/', function (req, res) {
res.send('hello, world!')
})
- Rate-limiting
- Limits the rate at which clients can make requests to the API, helping to prevent denial-of-service (DoS) attacks and other types of abuse.
- https://www.npmjs.com/package/express-rate-limit
- others:
- rate-limiter-flexible
- express-brute
- rate-limiter
npm install express-rate-limit --save
import rateLimit from 'express-rate-limit'
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // Limit each IP to 100 requests per `window` (here, per 15 minutes)
standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
legacyHeaders: false, // Disable the `X-RateLimit-*` headers
})
// Apply the rate limiting middleware to all requests
app.use(limiter)
- Authentication and authorization
- Passport, JSON Web Tokens (JWT), and OAuth.
npm install passport-oauth2 --save
passport.use(new OAuth2Strategy({
authorizationURL: 'https://www.example.com/oauth2/authorize',
tokenURL: 'https://www.example.com/oauth2/token',
clientID: EXAMPLE_CLIENT_ID,
clientSecret: EXAMPLE_CLIENT_SECRET,
callbackURL: "http://localhost:3000/auth/example/callback"
},
function(accessToken, refreshToken, profile, cb) {
User.findOrCreate({ exampleId: profile.id }, function (err, user) {
return cb(err, user);
});
}
));
- Validation
- Express-validator
- https://express-validator.github.io/docs
npm install express-validator --save
import * as express from 'express';
import { query, validationResult } from 'express-validator';
const app = express();
app.use(express.json());
app.get('/hello', query('person').notEmpty(), (req, res) => {
const result = validationResult(req);
if (result.isEmpty()) {
return res.send(`Hello, ${req.query.person}!`);
}
res.send({ errors: result.array() });
});
app.listen(3000);
- Error handling
- express-async-errors
npm install express-async-errors --save
- CSRF protection
- csurf
npm install csurf --save
- Request logging
- express-request-logger
npm install express-request-logger --save
- Session management
- express-session
npm install express-session --save
- Upload files ** **
npm install multer --save
const express = require('express')
const multer = require('multer')
const upload = multer({ dest: 'uploads/' })
const app = express()
app.post('/profile', upload.single('avatar'), function (req, res, next) {
// req.file is the `avatar` file
// req.body will hold the text fields, if there were any
})
app.post('/photos/upload', upload.array('photos', 12), function (req, res, next) {
// req.files is array of `photos` files
// req.body will contain the text fields, if there were any
})
- Compression
npm install compression --save
- Cross-Site Scripting (XSS) prevention
- xss-clean
npm install xss-clean --save
- Content Security Policy (CSP) enforcement
- xss-clean
npm install xss-clean --save
Node.js REST server + Docker
- Create a new directory for your project and navigate into it.
mkdir my-project && cd my-project
- Initialize a new Node.js project using npm init and install the required dependencies:
npm init -y
npm install express --save
- Create a new file named index.js in your project directory, and add the following code to it:
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.send('Hello, world!');
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});
- Create a new file named Dockerfile in your project directory, and add the following code to it:
FROM node:16-alpine
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm install --production
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
- Build the Docker image by running the following command in your project directory:
docker build -t my-project .
- Run the Docker container by running the following command:
docker run -p 3000:3000 my-project
- Starts a new Docker container and
- Maps port 3000 from the container to port 3000 on the host machine.
- Navigate to http://localhost:3000/.
next()
const express = require('express');
const app = express();
// Middleware function that adds a timestamp to the request object
app.use((req, res, next) => {
req.timestamp = Date.now();
next(); // pass control to the next middleware function
});
// Middleware function that logs the request URL and timestamp
app.use((req, res, next) => {
console.log(`Request made to ${req.originalUrl} at ${new Date(req.timestamp).toISOString()}`);
next(); // pass control to the next middleware function
});
// Route handler that sends a JSON response
app.get('/data', (req, res) => {
const data = {
name: 'John Doe',
age: 30,
email: 'johndoe@example.com'
};
res.json(data);
});
app.listen(3000, () => {
console.log('Server started on port 3000');
});
debug library
npm install debug --save
const debug = require('debug')('myapp:server');
debug('Server listening on port ' + server.address().port);
DEBUG=myapp:* node server.js
node server.js
Alternate Node.js API solutions
Nest.js
Nest.js is a TypeScript-based server-side framework for building scalable and maintainable applications. It's built on top of Node.js and provides a modular and structured approach to building web applications.
Nest.js is heavily influenced by Angular, and it borrows many concepts and techniques from the Angular framework. It uses a similar syntax for defining controllers, services, and modules, and it provides features like dependency injection, middleware, and decorators to make building complex applications easier.
Key features of Nest.js include:
- A modular architecture based on Angular-like modules
- Built-in support for TypeScript and the latest ECMAScript features
- CLI tool for generating code and managing project structure
- Built-in modules for handling common tasks like authentication, logging, and configuration
- Support for various web frameworks like Express and Fastify
- Focus on maintainability, scalability, and testability
- Nest.js is a popular choice for building enterprise-level applications and microservices.
- It has a large and active developer community.
Nest.js + Docker
Using Nest.js with docker is a way to add several important benefits:
- Consistent environment: Docker allows you to create a consistent development and deployment environment for your Nest.js application.
- Portability: Docker containers can be run on any machine that has Docker installed, regardless of the underlying operating system.
- Scalability: Docker makes it easy to scale your application horizontally by running multiple instances of your application in separate containers.
- Simplified/Faster deployment: Docker allows you to package your application and its dependencies into a single container, making it easier to deploy your application to production.
Nest.js + Docker + services
Database server
- MySQL, PostgreSQL, or MongoDB on a separate container to store your application data.
Caching server
- Redis or Memcached to improve the performance of your application by caching frequently accessed data.
Load balancer
- NGINX or HAProxy to distribute traffic across multiple instances of your application running in separate containers.
Reverse proxy
- like NGINX or Apache to handle SSL termination, load balancing, and other HTTP-related tasks for your application.
Monitoring and logging tools
- Prometheus, Grafana, or ELK stack to monitor the health and performance of your application and generate logs for debugging purposes.
Task scheduler
- Cron or Celery to schedule periodic tasks or background jobs.
Message broker
- Message broker like RabbitMQ or Apache Kafka to handle asynchronous messaging between different parts of your application.
Node.js Queries/Promises
Creating a new promise:
const promise = new Promise((resolve, reject) => {
// Asynchronous operation here
if (/* operation successful */) {
resolve(result);
} else {
reject(error);
}
});
Handling a resolved promise with .then():
promise.then(result => {
// Handle the result here
}).catch(error => {
// Handle the error here
});
Chaining promises with .then():
promise.then(result1 => {
// Handle the first result here
return result1 + 1;
}).then(result2 => {
// Handle the second result here
}).catch(error => {
// Handle any errors here
});
Using Promise.all() to run multiple promises at the same time:
Promise.all([
promise1(),
promise2(),
promise3()
]).then(results => {
// Handle all of the results here
}).catch(error => {
// Handle any errors here
});
Using Promise.race() to wait for the first promise to resolve or reject:
Promise.race([
promise1(),
promise2(),
promise3()
]).then(result => {
// Handle the first result here
}).catch(error => {
// Handle any errors here
});
Wrapping a callback function with a promise:
const promise = new Promise((resolve, reject) => {
callbackFunction((error, result) => {
if (error) {
reject(error);
} else {
resolve(result);
}
});
});
Using Promise.resolve() to create a resolved promise:
const promise = Promise.resolve(result);
Using Promise.reject() to create a rejected promise:
const promise = Promise.reject(error);
Using async/await with promises:
async function myFunction() {
try {
const result = await promise;
// Handle the result here
} catch (error) {
// Handle any errors here
}
}
Using Promise.finally() to run code after a promise is resolved or rejected:
promise.finally(() => {
// Run this code after the promise is resolved or rejected
});
Other related Node.js
Local Library website written in NodeJS/Express; example for the MDN server-side development NodeJS module https://github.com/mdn/express-locallibrary-tutorial
node-express-server-rest-api https://github.com/rwieruch/node-express-server-rest-api/tree/master/src
GraphQL with Apollo, Express and PostgreSQL boilerplate project. https://github.com/the-road-to-graphql/fullstack-apollo-express-postgresql-boilerplate
Building a Node.js/TypeScript REST API, Part 1: Express.js https://www.toptal.com/express-js/nodejs-typescript-rest-api-pt-1
Building a Node.js/TypeScript REST API, Part 2: Models, Middleware, and Services https://www.toptal.com/express-js/nodejs-typescript-rest-api-pt-2
Building a Node.js/TypeScript REST API, Part 3: MongoDB, Authentication, and Automated Tests https://www.toptal.com/express-js/nodejs-typescript-rest-api-pt-3
Express promises example https://github.com/vssenko/express-promises-example
Using Express.js Routes for Promise-based Error Handling https://www.toptal.com/express-js/routes-js-promises-error-handling
The Top 10 Most Common Mistakes That Node.js Developers Make https://www.toptal.com/nodejs/top-10-common-nodejs-developer-mistakes
How to Use JWT and Node.js for Better App Security https://www.toptal.com/json/jwt-nodejs-security
Creating a Secure REST API in Node.js https://www.toptal.com/nodejs/secure-rest-api-in-nodejs#anatomy-of-a-rest-api
Build a REST API with Node.js, Express, and MySQL https://blog.logrocket.com/build-rest-api-node-express-mysql/