Node.js logoNode.jsv5INTERMEDIATE

Express.js REST API

Express REST API cheat sheet covering routing, middleware, database integration, authentication, error handling, and testing patterns.

18 min read
expressnodejsrest-apimiddlewareroutingdatabasemongodbsqlerror-handlingtestingauthentication

Sign in to mark items as known and track your progress.

Sign in

Express.js Basic Setup

Initialize Express server and configure essential middleware

Express Server Setup

Create and configure a basic Express server with essential middleware

javascript
const express = require('express');
const app = express();

// Middleware
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// Basic route
app.get('/', (req, res) => {
  res.json({ message: 'Hello World!' });
});

app.listen(3000, () => {
  console.log('Server running on port 3000');
});
🟢 Essential - Every Express app starts with this setup
💡 Use dotenv for environment variables in production
⚡ Enable cors() for cross-origin requests from frontend
📌 Body parsers (json/urlencoded) required for POST requests
🔗 Related: helmet for security headers, compression for gzip

Middleware Functions

Create custom middleware for authentication, logging, and request processing

javascript
// Custom middleware
const auth = (req, res, next) => {
  const token = req.header('Authorization');
  if (!token) {
    return res.status(401).json({ error: 'Access denied' });
  }
  next();
};

// Apply middleware
app.use(auth);
app.get('/protected', auth, (req, res) => {
  res.json({ message: 'Protected route' });
});
🟢 Essential - Middleware is the heart of Express
💡 Order matters - middleware runs in sequence
⚠️ Don't forget next() or response will hang
📌 app.use() applies to all routes, router.use() to specific routes
⚡ Keep middleware focused on single responsibility

Routing & REST APIs

Define routes and implement RESTful API endpoints

CRUD Operations

Implement Create, Read, Update, Delete operations following REST conventions

javascript
// Basic CRUD routes
app.get('/api/users', (req, res) => {
  res.json(users);
});

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

app.post('/api/users', (req, res) => {
  const user = { id: Date.now(), ...req.body };
  users.push(user);
  res.status(201).json(user);
});
🟢 Essential - Standard REST API operations
💡 Use proper HTTP methods: GET (read), POST (create), PUT/PATCH (update), DELETE
📌 Return appropriate status codes: 200 OK, 201 Created, 404 Not Found
⚡ Use async/await for cleaner asynchronous code
⚠️ Always validate and sanitize user input
🔗 Related: express-validator for input validation

Router Organization

Organize routes into modular routers for scalable API structure

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

router.get('/', getAllUsers);
router.get('/:id', getUserById);
router.post('/', createUser);
router.put('/:id', updateUser);
router.delete('/:id', deleteUser);

module.exports = router;

// app.js
app.use('/api/users', require('./routes/users'));
app.use('/api/products', require('./routes/products'));
💡 Separate routes by resource or feature for maintainability
📌 Use router.route() to chain methods for same path
⚡ Mount routers with prefixes: app.use('/api/users', userRouter)
🟢 Essential for large applications - keeps code organized
🔗 Related: express.Router() for creating modular routes

Database Integration

Connect and interact with databases in Express

MongoDB with Mongoose

Connect Express to MongoDB using Mongoose ODM for data modeling

javascript
const mongoose = require('mongoose');

// Connect to MongoDB
mongoose.connect('mongodb://localhost:27017/mydb', {
  useNewUrlParser: true,
  useUnifiedTopology: true
});

// Define schema
const userSchema = new mongoose.Schema({
  name: { type: String, required: true },
  email: { type: String, required: true, unique: true },
  age: Number,
  createdAt: { type: Date, default: Date.now }
});

const User = mongoose.model('User', userSchema);
🟢 Essential for MongoDB - Mongoose simplifies database operations
💡 Define schemas for data validation and structure
⚡ Use connection pooling for better performance
📌 Handle connection errors and implement retry logic
⚠️ Never expose database credentials in code
🔗 Related: MongoDB Atlas for cloud hosting

SQL Database Integration

Connect Express to PostgreSQL/MySQL using query builders or ORMs

javascript
const mysql = require('mysql2/promise');

// Database connection
const pool = mysql.createPool({
  host: 'localhost',
  user: 'root',
  password: 'password',
  database: 'mydb',
  waitForConnections: true,
  connectionLimit: 10,
  queueLimit: 0
});

// Query examples
const getUsers = async () => {
  const [rows] = await pool.execute('SELECT * FROM users WHERE active = ?', [1]);
  return rows;
};
💡 Use Prisma or Sequelize for type-safe database access
📌 Knex.js for flexible SQL query building
⚡ Use prepared statements to prevent SQL injection
⚠️ Always use parameterized queries, never concatenate SQL
🟢 Essential - Choose ORM based on project needs
🔗 Related: pg for PostgreSQL, mysql2 for MySQL

Error Handling & Testing

Handle errors gracefully and test your API endpoints

Error Handling Best Practices

Implement centralized error handling and custom error responses

javascript
// Custom error classes
class AppError extends Error {
  constructor(message, statusCode) {
    super(message);
    this.statusCode = statusCode;
    this.isOperational = true;
    Error.captureStackTrace(this, this.constructor);
  }
}

// Global error handler
app.use((err, req, res, next) => {
  const { statusCode = 500, message } = err;
  res.status(statusCode).json({
    error: {
      message,
      ...(process.env.NODE_ENV === 'development' && { stack: err.stack })
    }
  });
});
🟢 Essential - Proper error handling improves user experience
💡 Use error middleware (4 parameters) for centralized handling
📌 Create custom error classes for different error types
⚠️ Never expose stack traces in production
⚡ Log errors but send user-friendly messages to client
🔗 Related: winston or pino for logging

Testing Express APIs

Write unit and integration tests for API endpoints

javascript
// Jest test example
const request = require('supertest');
const app = require('../app');

describe('User API', () => {
  test('GET /api/users should return users', async () => {
    const response = await request(app)
      .get('/api/users')
      .expect(200);
    
    expect(response.body).toHaveProperty('users');
    expect(Array.isArray(response.body.users)).toBe(true);
  });

  test('POST /api/users should create user', async () => {
    const userData = {
      name: 'John Doe',
      email: 'john@example.com',
      password: 'password123'
    };

    const response = await request(app)
      .post('/api/users')
      .send(userData)
      .expect(201);
    
    expect(response.body.user.name).toBe(userData.name);
  });
});
🟢 Essential - Tests ensure API reliability
💡 Use Jest + Supertest for API testing
📌 Test both success and error scenarios
⚡ Mock external dependencies for unit tests
🎯 Aim for 80%+ code coverage
🔗 Related: Postman for manual API testing