Skip to main content

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

  1. Install node.js and npm
mkdir myproject
cd myproject
  1. Install NPM
npm init
  1. Install typescript, ts-node and nodemon
npm install typescript ts-node @types/node nodemon --save-dev
  1. install tsconfig.json
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"outDir": "dist",
"strict": true,
"esModuleInterop": true
},
"include": ["src/**/*.ts"]
}
  1. Create src directory with index.ts

console.log('Hello, world!');

  1. Add a start script to your package.json file:
{
"scripts": {
"start": "nodemon --exec ts-node src/index.ts"
}
}

  1. Run the TypeScript compiler to compile your code:
npx tsc

  1. 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);
});
}
));
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

  1. Create a new directory for your project and navigate into it.
mkdir my-project && cd my-project
  1. Initialize a new Node.js project using npm init and install the required dependencies:
npm init -y
npm install express --save
  1. 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');
});

  1. 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"]
  1. Build the Docker image by running the following command in your project directory:
docker build -t my-project .
  1. 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
});