Node.js REST API with Express: Complete Guide

Express is still the go-to framework for Node.js APIs. It’s minimal, flexible, and has a massive ecosystem. Here’s how to build a production-ready REST API from scratch.

Project Setup

mkdir my-api && cd my-api
npm init -y
npm install express cors helmet morgan dotenv
npm install -D nodemon

Basic Server

// server.js
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');

const app = express();

// Middleware
app.use(helmet()); // Security headers
app.use(cors());
app.use(morgan('dev')); // Logging
app.use(express.json());

// Routes
app.get('/api/health', (req, res) => {
  res.json({ status: 'ok', timestamp: new Date() });
});

// Error handler
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).json({ error: 'Something went wrong' });
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

Organizing Routes

// routes/users.js
const express = require('express');
const router = express.Router();

const users = [];

router.get('/', (req, res) => {
  res.json(users);
});

router.get('/:id', (req, res) => {
  const user = users.find(u => u.id === req.params.id);
  if (!user) return res.status(404).json({ error: 'Not found' });
  res.json(user);
});

router.post('/', (req, res) => {
  const user = { id: Date.now().toString(), ...req.body };
  users.push(user);
  res.status(201).json(user);
});

module.exports = router;

// server.js
const usersRouter = require('./routes/users');
app.use('/api/users', usersRouter);

Input Validation

Never trust user input. Use express-validator:

const { body, validationResult } = require('express-validator');

router.post('/',
  body('email').isEmail(),
  body('name').notEmpty().trim(),
  (req, res) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      return res.status(400).json({ errors: errors.array() });
    }
    // Create user...
  }
);

Async Error Handling

// Wrapper for async routes
const asyncHandler = fn => (req, res, next) =>
  Promise.resolve(fn(req, res, next)).catch(next);

router.get('/:id', asyncHandler(async (req, res) => {
  const user = await db.findUser(req.params.id);
  if (!user) throw new NotFoundError('User not found');
  res.json(user);
}));

References


Discover more from C4: Container, Code, Cloud & Context

Subscribe to get the latest posts sent to your email.

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.