๐ŸŸข
Websites

Building a REST API with Node.js and Express: a practical guide

02.02.2026
โ† All articles

Modern web applications almost always consist of two parts: the frontend that users see and the backend that manages data and business logic. The bridge connecting these two layers is precisely the REST API. Node.js and the Express framework built on top of it have become one of the most popular choices for backend development today, thanks to their simplicity, performance, and enormous ecosystem of ready-made packages. In this article we will build a fully working REST API from scratch, explore routes and middleware, the CRUD operations, error handling, validation, and authentication, all illustrated with practical code examples.

What Express is and why you need it

Express is a minimal and flexible web framework for Node.js that greatly simplifies the process of receiving HTTP requests and sending responses. Node.js on its own can also create a web server, but in that case you would have to manually parse every request, compare URLs, and configure response headers yourself. Express hides this complexity and gives you convenient tools in the form of routes, middleware, and helper methods. This is exactly why thousands of companies and startups build their APIs on Express, because it lets you achieve more results with less code.

Setting up the project and the first server

To get started, you need Node.js installed on your computer. We then create a new project folder, initialize it using the npm package manager, and install the Express library. The following commands prepare the basic project structure and lay the foundation for writing your first working server.

mkdir my-api && cd my-api
npm init -y
npm install express

Now we create an index.js file in the project root and write the simplest possible server. This server responds to just one address, but it serves as the foundation of our entire future API. Notice that the server listens on a specific port and prints a message to the console on startup so that we know everything is working correctly.

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

app.use(express.json());

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

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

Routes and CRUD endpoints

The core idea of a REST API is working with resources, such as users or articles. Four basic operations are performed on each resource: create, read, update, and delete, known together as CRUD. These operations are expressed through the HTTP methods POST, GET, PUT, and DELETE respectively. In the example below we look at a complete set of CRUD endpoints that work with a temporary data store in the form of a simple array.

let users = [{ id: 1, name: 'Alice' }];

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

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

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

app.delete('/users/:id', (req, res) => {
  users = users.filter(u => u.id !== Number(req.params.id));
  res.status(204).end();
});

In this code we read the dynamic parts of the URL through req.params and the JSON data from the request body through req.body. When sending a response it is crucial to choose the correct HTTP status codes: 201 when a new resource is created, 204 on a successful deletion, and 404 when a resource is not found. These codes clearly communicate to API clients exactly what happened with their request.

The middleware concept

Middleware consists of functions that run in the interval between a request arriving at the server and a response being sent back. Through them you can centrally handle tasks such as logging requests, checking authentication, pre-processing data, or catching errors. The express.json() we used above is in fact also middleware that automatically converts an incoming JSON body into an object. Writing your own middleware is also quite straightforward.

function logger(req, res, next) {
  console.log(`${req.method} ${req.url}`);
  next();
}

app.use(logger);

Note that you must call the next() function at the end of every middleware, otherwise the request will not move on to the next stage and will simply hang. This mechanism makes Express extremely powerful, because it lets you break complex logic into small, reusable pieces.

Error handling and validation

When building a reliable API, one of the most overlooked yet most important aspects is proper error handling. A user may send invalid data, request a non-existent resource, or encounter an unexpected problem on the server. In such situations the API must return a clear error message and the correct status code. In Express, a dedicated error-handling middleware accepts four arguments and is placed after all the routes.

app.post('/users', (req, res, next) => {
  if (!req.body.name) {
    return res.status(400).json({ error: 'Name is required' });
  }
  next();
});

app.use((err, req, res, next) => {
  console.error(err);
  res.status(500).json({ error: 'Server error' });
});

Validation, meaning the checking of incoming data, is also extremely important from a security standpoint. In practice many developers, instead of writing this logic by hand, use specialized libraries such as Joi or express-validator, which helps keep the code clean and reliable.

Database and authentication

In real projects data is stored not in an array but in a database. Node.js connects easily to MongoDB through Mongoose or to PostgreSQL through tools like Prisma. The connection is usually established once when the application starts, and then queries are executed through that connection inside the routes. Authentication is most often implemented using JWT, the JSON Web Token technology: when a user logs in they receive a special token, and that token is used in subsequent requests to confirm their identity.

const jwt = require('jsonwebtoken');

function auth(req, res, next) {
  const token = req.headers.authorization?.split(' ')[1];
  if (!token) return res.status(401).json({ error: 'No token' });
  try {
    req.user = jwt.verify(token, process.env.SECRET);
    next();
  } catch {
    res.status(403).json({ error: 'Invalid token' });
  }
}

Best practices

To create a professional-grade API, it is worth following several important rules. First, separate your code into logical layers: keeping routes, business logic, and data access in separate files greatly simplifies maintenance as the project grows. Second, never write secret data such as database passwords and token keys directly into the code; store them in environment variables instead. Third, introduce API versioning and consistently apply security measures such as rate limiting. By following these practices you will end up with an API that is not merely functional but genuinely reliable and capable of withstanding real-world load.

Related articles

๐ŸŒพ Agriculture and Agribusiness Website: Product Catalog and B2B Sales โค๏ธ Charity Foundation Website: Transparent Fundraising and Donor Trust ๐ŸŽ‰ Wedding Venue and Banquet Hall Website: Event Planning and Online Booking ๐Ÿš™ Car Rental Website: Vehicle Catalog, Price Calculator, and Online Booking
๐ŸŒ Language
๐Ÿ‡บ๐Ÿ‡ฟ O'zbek ๐Ÿ‡บ๐Ÿ‡ฟ ะŽะทะฑะตะบ ๐Ÿ‡ท๐Ÿ‡บ ะ ัƒััะบะธะน ๐Ÿ‡ฌ๐Ÿ‡ง English โœ“