Skip to content
On this page


A minimalist foundation for starting a web API in Javascript.
Installing Express

For debugging system configurations, it helps to spin up a known working server. "Hello world" is always a good place to start:

const express = require('express')
const app = express()
const port = 3000

app.get('/', (req, res) => res.send('Hello World!'))

app.listen(port, () => console.log(`Example app listening at http://localhost:${port}`))
Express "Hello World" example


When express receives a request, it parses the request and makes attributes available in the req value.

req.params contains route parameters (in the path portion of the URL)

req.query contains the URL query parameters (after the ? in the URL).

req.body contains anything in the request body. Typically this is used on PUT and POST requests.


Once a project grows beyond "Hello World", collect routes in various .js files.

In the app, these can be included with:

var birds = require('./birds')

// ...

app.use('/birds', birds)

and may look something like:

var express = require('express')
var router = express.Router()

// middleware that is specific to this router
router.use(function timeLog (req, res, next) {
  console.log('Time: ',
// define the home page route
router.get('/', function (req, res) {
  res.send('Birds home page')
// define the about route
router.get('/about', function (req, res) {
  res.send('About birds')

module.exports = router

It is acceptable to provide multiple functions in the route. There are different ways to format these, including as an array of functions to call:

See "Route handlers" on
Express routing
Express basic routing

Nested Routes

CRUD Example

Async/await can be used with Express.

In this case, it's good to enable:

So that error conditions are handled correctly by routes

npm install express-async-errors --save
const express = require('express')
var express = require("express");
var router = express.Router();
var common = require("./common");

const mongoose = require("mongoose");
var db = require("../db-mongo");
const Sample = mongoose.model("sample");

// Search vs Find
// Leaning toward `find` being a lightweight GET call to quickly filter a list of objects
// `search` is a parameterized POST method that returns more details

router.get("/find/:query", common.set_user, async (req, res, next) => {
  const filtered = common.escapeRegex(req.params.query);
  // do not include forward slashes in the string
  const regex = new RegExp(`${filtered}`, "gi");
  // console.log("using regex", regex);
  const matches = await Sample.find({ name: regex }).catch((err) => {
  // console.log("Search for", req.params.query, matches);
  // res.json(
  //{ name }) => {
  //     return { value: name };
  //   })
  // );

router.get("/:id", common.set_user, async (req, res, next) => {
  const match = await Sample.findOne({ _id: }).catch((err) => {

router.patch("/:id", common.check_role("admin"), async (req, res, next) => {
  let sample = await Sample.findOneAndUpdate({ _id: }, req.body, {
    returnOriginal: false,
  }).catch((err) => {
    return next(err);

  if (!sample) return res.status(200).json([]);

router.delete("/:id", common.check_role("admin"), async (req, res, next) => {
  await Sample.deleteOne({ _id: }).catch((err) => {
  res.json({ result: "success" });

router.get("/", common.set_user, async (req, res, next) => {
  const matches = await Sample.find({}).catch((err) => {
});"/", common.check_role("admin"), async (req, res, next) => {
  // console.log("CREATE NEW SAMPLE", req.body);
  const created = await Sample.create(req.body).catch((err) => {

module.exports = router;


When developing an API, it can be cumbersome to have to manually restart the server every time there is a code change. Want some type of watch mode.

Be sure to disable this when deploying the API to production.

Depending on what you use to manage the node processes, your options may vary


Nodemon works well here

npm install --save-dev nodemon 

then run with

npx nodemon

An example for docker-compose.yml

    # image: node:14
      context: ./api
      dockerfile: Dockerfile
    container_name: boilerplate_api
      - .:/srv/boilerplate/
      - boilerplate_api_modules:/srv/boilerplate/api/node_modules
    working_dir: /srv/boilerplate/api

    # development
    command: sh -c "npm install && npx nodemon boilerplate-api.js"

    # in production, no need to run with a watcher
    # command: sh -c "npm install && npm run start"
    # DEBUG=express:* node ./api/boilerplate-api.js
    # entrypoint: ["tail", "-f", "/dev/null"]


PM2 watch mode


To see all the internal logs used in Express, set the DEBUG environment variable to express:* when launching your app.

DEBUG=express:* node index.js

You can also use curl or wget to test specific routes that are available.


Morgan makes it easy to log all requests made to the service.
GitHub - expressjs/morgan: HTTP request logger middleware for node.js

Winston allows logging custom messages.
GitHub - winstonjs/winston: A logger for just about everything.
馃挙 morgan js logger at DuckDuckGo
馃挙 morgan - npm
馃挙 express morgan winston at DuckDuckGo
Combine Morgan & Winston @
Node.js - logging / Use morgan and winston - Stack Overflow
Advanced logging with NodeJs | Ugo Lattanzi's tech world
Better logs for ExpressJS using Winston and Morgan with Typescript - DEV Community


For more details see Auth

Session management in Express
node express session management - Google Search
How to Manage Session using Node.js and Express - Codeforgeek
How to create a REST API with Express.js in Node.js - RWieruch


Boilerplates are tricky -- it's rare to find one that sets things up just the way you want. However, they're a great way to learn about new tools and techniques, and to see where a community is headed.

This is a nice one that focuses on just the server / backend:
GitHub - sahat/hackathon-starter: A boilerplate for Node.js web applications
hackathon-starter/package.json at master 路 sahat/hackathon-starter 路 GitHub
hackathon-starter/user.js at master 路 sahat/hackathon-starter 路 GitHub

I like the structure of this one for both client and server (but not sure that I agree with all of the dependencies):
GitHub - icebob/vue-express-mongo-boilerplate: MEVN Full stack JS web app boilerplate with NodeJS, Express, Mongo and VueJS
vue-express-mongo-boilerplate/server at master 路 icebob/vue-express-mongo-boilerplate 路 GitHub
express backend boilerplate - Google Search
express passport boilerplate - Google Search
Nodejs Starter - Javascript Boilerplates to start fast - DEV
The Best Node.js Boilerplate to Speed Up Your Project Development
express-boilerplate 路 GitHub Topics 路 GitHub
GitHub - app-generator/nodejs-starter: Nodejs Starter - Open-Source Javascript Boilerplate | AppSeed
Express-js Boilerplate with User Authentication | Hacker Noon
NodeJS and Good Practices 鈥 The Miners
Four Layer Architecture
Clean Coder Blog