Skip to main content

Express Application

Express.js is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications. Understanding how to create and structure an Express application is fundamental for building backend services.

What is an Express Application?

An Express application is essentially a series of middleware function calls. It's an instance of the express module that provides methods to:

  • Set up middleware
  • Define routes
  • Configure template engines
  • Set up error handling
  • Create and start an HTTP server

Creating Your First Express Application

Let's start by creating a basic Express application:

Step 1: Set Up Your Project

First, create a new directory for your project and initialize a Node.js application:

bash
mkdir my-express-app
cd my-express-app
npm init -y
npm install express

Step 2: Create the Main File

Create a file named app.js (or index.js) in your project directory:

javascript
// Import the express module
const express = require('express');

// Create an Express application
const app = express();

// Define a route for the home page
app.get('/', (req, res) => {
res.send('Hello World! Welcome to my Express application.');
});

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

Step 3: Run Your Application

Run your application using Node.js:

bash
node app.js

Output:

Server running on port 3000

Now, if you visit http://localhost:3000 in your browser, you'll see the text "Hello World! Welcome to my Express application."

Application Methods

The Express application object provides several methods that are essential for configuring your application:

app.use()

This method is used to mount middleware functions. Middleware functions are functions that have access to the request object (req), the response object (res), and the next middleware function in the application's request-response cycle.

javascript
// Middleware to log all requests
app.use((req, res, next) => {
console.log(`${new Date().toISOString()} - ${req.method} ${req.url}`);
next(); // Don't forget to call next() to pass control to the next middleware
});

// Serving static files
app.use(express.static('public'));

// Parsing JSON request bodies
app.use(express.json());

app.get(), app.post(), app.put(), app.delete(), etc.

These methods define routes for different HTTP methods:

javascript
app.get('/users', (req, res) => {
// Handle GET request for /users
res.json([{ id: 1, name: 'John' }, { id: 2, name: 'Jane' }]);
});

app.post('/users', (req, res) => {
// Handle POST request for /users
const newUser = req.body;
// Process the new user data...
res.status(201).json({ id: 3, ...newUser });
});

app.set()

This method is used to set various application settings:

javascript
// Set the view engine to EJS
app.set('view engine', 'ejs');

// Set views directory
app.set('views', './views');

// Set application variable
app.set('appName', 'My Express App');

Application Configuration

A properly configured Express application should include several key components:

Error Handling

Error handling middleware is defined with four arguments instead of the usual three (err, req, res, next):

javascript
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Something went wrong!');
});

Environment Configuration

You should configure your application differently based on the environment:

javascript
if (process.env.NODE_ENV === 'development') {
// Development-specific settings
app.use(require('morgan')('dev')); // Logging middleware
} else {
// Production-specific settings
app.set('trust proxy', 1); // Trust first proxy
}

Security Middleware

It's important to add security features to your Express application:

javascript
const helmet = require('helmet');
app.use(helmet()); // Adds various HTTP headers for security

const cors = require('cors');
app.use(cors()); // Enable Cross-Origin Resource Sharing

Structuring an Express Application

As your application grows, it's important to structure it properly:

Simple Structure

For small applications, a simple structure might look like this:

my-express-app/
├── node_modules/
├── public/ # Static files
│ ├── css/
│ ├── js/
│ └── images/
├── views/ # Template files
│ └── index.ejs
├── routes/ # Route handlers
│ ├── index.js
│ └── users.js
├── app.js # Main application file
└── package.json

MVC Structure

For larger applications, an MVC (Model-View-Controller) structure is often used:

my-express-app/
├── node_modules/
├── public/
├── app/
│ ├── models/ # Data models
│ ├── views/ # Templates
│ ├── controllers/ # Request handlers
│ └── middleware/ # Custom middleware
├── config/ # Configuration files
├── routes/ # Route definitions
├── app.js
└── package.json

Real-World Example: Building a RESTful API

Let's create a simple RESTful API for managing a list of books:

javascript
const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;

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

// Sample data
let books = [
{ id: 1, title: 'The Great Gatsby', author: 'F. Scott Fitzgerald', year: 1925 },
{ id: 2, title: 'To Kill a Mockingbird', author: 'Harper Lee', year: 1960 },
{ id: 3, title: '1984', author: 'George Orwell', year: 1949 }
];

// GET all books
app.get('/api/books', (req, res) => {
res.json(books);
});

// GET a specific book
app.get('/api/books/:id', (req, res) => {
const book = books.find(b => b.id === parseInt(req.params.id));
if (!book) return res.status(404).json({ message: 'Book not found' });
res.json(book);
});

// POST a new book
app.post('/api/books', (req, res) => {
const { title, author, year } = req.body;

if (!title || !author) {
return res.status(400).json({ message: 'Title and author are required' });
}

const newBook = {
id: books.length + 1,
title,
author,
year: year || 'Unknown'
};

books.push(newBook);
res.status(201).json(newBook);
});

// PUT (update) a book
app.put('/api/books/:id', (req, res) => {
const book = books.find(b => b.id === parseInt(req.params.id));
if (!book) return res.status(404).json({ message: 'Book not found' });

const { title, author, year } = req.body;

if (title) book.title = title;
if (author) book.author = author;
if (year) book.year = year;

res.json(book);
});

// DELETE a book
app.delete('/api/books/:id', (req, res) => {
const index = books.findIndex(b => b.id === parseInt(req.params.id));
if (index === -1) return res.status(404).json({ message: 'Book not found' });

const deletedBook = books[index];
books.splice(index, 1);

res.json(deletedBook);
});

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

With this API, you can:

  • Get all books: GET /api/books
  • Get a specific book: GET /api/books/1
  • Add a new book: POST /api/books with a JSON body
  • Update a book: PUT /api/books/1 with a JSON body
  • Delete a book: DELETE /api/books/1

Best Practices for Express Applications

  1. Use environment variables for configuration settings

    javascript
    const PORT = process.env.PORT || 3000;
    const MONGODB_URI = process.env.MONGODB_URI || 'mongodb://localhost/myapp';
  2. Handle errors properly

    javascript
    app.use((err, req, res, next) => {
    console.error(err.stack);
    res.status(500).send({ error: err.message || 'Server Error' });
    });
  3. Use async/await for asynchronous operations

    javascript
    app.get('/api/data', async (req, res, next) => {
    try {
    const data = await fetchDataFromDatabase();
    res.json(data);
    } catch (error) {
    next(error); // Pass errors to Express
    }
    });
  4. Use routers to modularize your application

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

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

    module.exports = router;

    // app.js
    const usersRouter = require('./routes/users');
    app.use('/users', usersRouter);
  5. Add validation for incoming data

    javascript
    app.post('/api/users', (req, res) => {
    const { name, email, age } = req.body;

    if (!name || !email) {
    return res.status(400).json({ message: 'Name and email are required' });
    }

    if (typeof age !== 'undefined' && (isNaN(age) || age < 0)) {
    return res.status(400).json({ message: 'Age must be a positive number' });
    }

    // Process valid data...
    });

Summary

Express.js provides a flexible framework for building web applications and APIs in Node.js. By understanding how to create and configure an Express application, you can build robust, scalable server-side applications.

Key points to remember:

  • An Express application is created with express() and configured with middleware
  • Routing methods like app.get(), app.post() define how your app responds to client requests
  • Middleware functions are the building blocks of Express applications
  • Proper application structure becomes important as your application grows
  • Error handling is critical for maintaining a stable application

Additional Resources

Exercises

  1. Create a basic Express application that serves an HTML page and a CSS file from a public directory.
  2. Extend the book API example to include validation and persistent storage using a JSON file.
  3. Create a middleware that logs all requests with their response time.
  4. Build a simple Express application with multiple routes and error handling.
  5. Create an Express application that renders dynamic content using a template engine like EJS or Pug.


If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)