6 Topics
Chapter•40 Minutes

Build the Express Server and API Routing

Create the Express application, configure middleware, implement API routing, and understand request processing.

Create the Express Application

Create src/app.js.

This file will initialize the Express application and register global middleware and routes.

We will keep app initialization separate from server startup. This makes the application easier to test using Supertest without opening a network port.

src/app.js
import express from 'express';
import cors from 'cors';
import helmet from 'helmet';

const app = express();

app.use(helmet());
app.use(cors({
  origin: process.env.CLIENT_ORIGIN || 'http://localhost:5173'
}));

app.use(express.json({ limit: '100kb' }));
app.use(express.urlencoded({
  extended: true,
  limit: '100kb'
}));

app.get('/health', (req, res) => {
  res.status(200).json({
    success: true,
    message: 'API is running'
  });
});

export default app;

Creates an Express application, registers security headers, CORS, body parsing middleware, and a health endpoint.

Key Takeaways
  • Express applications are initialized using express().

  • Middleware processes requests before route handlers.

  • Separate application initialization from server startup.

Understand Express Middleware

Middleware functions are functions that execute during the request-response lifecycle.

A middleware function receives three important arguments:

(req, res, next)
  • req: Contains incoming request information.
  • res: Provides methods for sending the response.
  • next: Passes control to the next middleware.

Middleware can:

  • Read request information.
  • Modify request or response objects.
  • Perform authentication.
  • Validate input.
  • Log requests.
  • Handle errors.
  • Terminate requests by sending responses.

If middleware neither sends a response nor calls next(), the request may remain pending.

Middleware order matters because Express executes middleware in registration order.

Express Middleware Execution
Create a Request Logging Middleware
function requestLogger(req, res, next) {
  console.log({
    method: req.method,
    path: req.originalUrl,
    timestamp: new Date().toISOString()
  });

  next();
}

app.use(requestLogger);

Logs incoming request information and passes execution to the next middleware.

Key Takeaways
  • Middleware executes during the request-response lifecycle.

  • next() passes execution to the next middleware.

  • Middleware order determines execution order.

Create the Main API Router

Create src/routes/index.js.

Express Router allows you to organize endpoints into independent modules.

For example, user-related endpoints can live in user.routes.js, while authentication endpoints can live in auth.routes.js.

The main router combines these route modules under a common API prefix.

src/routes/index.js
import { Router } from 'express';
import userRoutes from './user.routes.js';
import authRoutes from './auth.routes.js';

const router = Router();

router.use('/users', userRoutes);
router.use('/auth', authRoutes);

export default router;

Combines user and authentication routes into a single router.

Key Takeaways
  • Express Router organizes API endpoints.

  • Separate routes by application resource.

  • A main router provides a central routing entry point.

Mount API Routes in Express

Update src/app.js to register the main API router.

We will expose all API endpoints under /api/v1.

Versioning helps introduce future API changes without immediately breaking existing clients.

Mount Versioned API Routes
import apiRoutes from './routes/index.js';

app.use('/api/v1', apiRoutes);

Registers all API endpoints under the /api/v1 prefix.

Key Takeaways
  • Version API endpoints using a consistent prefix.

  • Centralize route registration.

  • Separate API versions when breaking changes are introduced.

Create the Server Entry Point

Create src/server.js.

This file will be responsible for:

  1. Loading environment variables.
  2. Connecting to MongoDB.
  3. Importing the Express application.
  4. Starting the HTTP server.

The database connection must complete before the server begins accepting requests that depend on MongoDB.

src/server.js
import 'dotenv/config';
import app from './app.js';
import { connectDatabase } from './config/database.js';

const PORT = process.env.PORT || 3000;

async function startServer() {
  await connectDatabase();

  app.listen(PORT, () => {
    console.log(`REST API running at http://localhost:${PORT}`);
  });
}

startServer().catch((error) => {
  console.error('Application startup failed:', error);
  process.exit(1);
});

Loads configuration, connects to MongoDB, and starts the Express application only after the database connection succeeds.

Key Takeaways
  • The server entry point controls application startup.

  • Connect to the database before accepting requests.

  • Separate app.js from server.js for easier testing.

Test the Initial Express Server

Start the application:

npm run dev

Open your browser or API client and send a GET request:

GET http://localhost:3000/health

Expected response:

{
  "success": true,
  "message": "API is running"
}

If you receive the expected response, the Express application is running.

You should also see a successful MongoDB connection message in the terminal.

Start the Application
npm run dev

Starts the backend using the development script.

Key Takeaways
  • Verify the server using the health endpoint.

  • Confirm that MongoDB connects successfully.

  • Check terminal logs when startup fails.