Home/Coding & Tech Skills

Step-by-Step: Build a REST API with Express and Node.js in 2026

coding-tech-skills · Coding & Tech Skills

Laptop showing terminal with npm init and VS Code folder structure

I spent last weekend rebuilding a tiny inventory API for a friend's pop-up shop — the same kind of project I've done half a dozen times before — and I still hit a snag that reminded me why getting the fundamentals right matters, especially in 2026. Express and Node.js have been around long enough that some developers dismiss them as old news, but after that Saturday debugging session, I'm more convinced than ever: if you want a REST API that's fast to build, easy to maintain, and flexible enough to grow with you, this stack is still the sweet spot. Below is the exact step-by-step process I used, including the mistakes I made so you don't have to.

Why Build a REST API with Express and Node.js in 2026?

You might wonder: aren't there newer, shinier frameworks like Fastify or Hono? Sure, they have their place, but Express still boasts the largest middleware ecosystem on npm — over 80,000 packages as of early 2026. That means if you need authentication, logging, rate limiting, or file uploads, there's almost certainly a well-tested solution ready to plug in. Node.js itself is faster than ever after the 22.x LTS release, with improved HTTP handling that makes Express feel snappy even under moderate load.

But the real reason I keep coming back to Express is its simplicity. When I started coding that pop-up shop API, I didn't want to learn a new paradigm or fight with a framework's opinionated structure. I wanted to write routes, connect a database, and ship. Express gets out of your way. And because it's been around since 2010, the community knowledge is immense — if you hit a wall, you're never more than a search away from someone who solved the same problem a decade ago.

In 2026, building a REST API with Express and Node.js also means you're investing in skills that transfer directly to other Node.js frameworks (like NestJS, which wraps Express under the hood). It's not just relevant — it's foundational.

Prerequisites: What You Need Before You Start

Before we write a single line of code, let's make sure your machine is ready. Here's what I had installed when I started:

  • Node.js (version 22.x LTS or later) — check with node -v.
  • npm (comes with Node.js, but I always update it: npm install -g npm@latest).
  • A code editor — I used VS Code with the ESLint extension, but anything works.
  • Basic JavaScript familiarity — you should know functions, objects, arrays, and arrow syntax.

If you're missing Node.js, head to the official website and grab the LTS installer. Don't overthink the version — LTS is stable and supported for years.

Step 1: Initialize Your Node.js Project and Install Express

Open your terminal, navigate to where you want the project to live, and run:

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

The -y flag accepts all defaults. You'll get a package.json file. Now install Express:

npm install express

I also like to create a sensible folder structure from the start. Here's what I used for my pop-up shop API:

my-api/
  ├── controllers/
  ├── models/
  ├── routes/
  ├── middleware/
  └── server.js

This keeps concerns separated. Controllers handle business logic, models define data shapes, routes map URLs to controllers, and middleware holds reusable code like authentication. Even for a small project, this pattern pays off when you add a second resource (like adding 'orders' to my 'items' API).

Step 2: Create the Basic Express Server

Create a file called server.js in the root. Here's the minimal server I started with:

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

// Middleware to parse JSON bodies
app.use(express.json());

// A test route
app.get('/', (req, res) => {
  res.send('API is running');
});

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

Run it with node server.js and visit http://localhost:3000 in your browser. You should see 'API is running'. That's your first endpoint.

The express.json() middleware is critical — without it, req.body would be undefined when you send JSON in a POST request. I forgot this once and spent 20 minutes wondering why my data wasn't reaching the server. Don't be me.

Step 3: Define Your RESTful Routes and Controllers

Now let's build something real. I'll use a 'items' resource — like the inventory items in my friend's shop. First, create routes/items.js:

const express = require('express');
const router = express.Router();
const { getItems, createItem } = require('../controllers/itemsController');

router.get('/', getItems);
router.post('/', createItem);

module.exports = router;

Then create controllers/itemsController.js with placeholder logic (no database yet):

let items = [
  { id: 1, name: 'Tote bag', price: 25 },
  { id: 2, name: 'Sticker pack', price: 5 }
];

exports.getItems = (req, res) => {
  res.json(items);
};

exports.createItem = (req, res) => {
  const newItem = { id: items.length + 1, ...req.body };
  items.push(newItem);
  res.status(201).json(newItem);
};

Finally, wire the routes into server.js:

app.use('/api/items', require('./routes/items'));

Now restart your server and test with curl: curl http://localhost:3000/api/items. You'll get the array back. A POST with curl -X POST -H "Content-Type: application/json" -d '{"name":"Mug","price":15}' http://localhost:3000/api/items should add it.

This in-memory approach is fine for prototyping, but we need persistence for anything real. That's next.

Step 4: Connect to a Database (MongoDB Example)

For the pop-up shop, I used MongoDB because the inventory data was unstructured (some items had colors, others had sizes). Install Mongoose:

npm install mongoose

Create a models/Item.js:

const mongoose = require('mongoose');

const itemSchema = new mongoose.Schema({
  name: { type: String, required: true },
  price: { type: Number, required: true },
  inStock: { type: Boolean, default: true }
});

module.exports = mongoose.model('Item', itemSchema);

In server.js, add the connection (use environment variables for the URI):

const mongoose = require('mongoose');
mongoose.connect(process.env.MONGO_URI || 'mongodb://localhost:27017/shop');

Update your controller to use the model:

const Item = require('../models/Item');

exports.getItems = async (req, res) => {
  const items = await Item.find();
  res.json(items);
};

exports.createItem = async (req, res) => {
  const item = new Item(req.body);
  await item.save();
  res.status(201).json(item);
};

Notice I used async/await — essential for database calls. I also added a .env file (using npm install dotenv) to keep my MongoDB URI out of the code. Never hardcode credentials.

Step 5: Add Error Handling and Validation

My first version of the API had no error handling — when I sent a POST with an invalid price, it crashed the server. Here's how to fix that.

First, validation using express-validator:

npm install express-validator

In your route file:

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

router.post('/', [
  body('name').notEmpty().withMessage('Name is required'),
  body('price').isFloat({ min: 0 }).withMessage('Price must be a positive number')
], (req, res) => {
  const errors = validationResult(req);
  if (!errors.isEmpty()) {
    return res.status(400).json({ errors: errors.array() });
  }
  createItem(req, res);
});

Second, centralized error handling middleware. Add this near the end of server.js:

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

This catches any unhandled errors and returns a consistent response — much better than a crash.

Step 6: Test Your API Locally and Next Steps

I tested my endpoints with Postman, but you can also use curl or the built-in VS Code REST Client extension. Create a test.http file with:

GET http://localhost:3000/api/items
###
POST http://localhost:3000/api/items
Content-Type: application/json

{"name":"Hat","price":20}

Click 'Send Request' and watch the responses roll in.

Now that your API works locally, what's next? In my own project, I added JWT authentication (using jsonwebtoken and bcrypt for password hashing) and rate limiting with express-rate-limit. For deployment, I pushed to GitHub and used Render's free tier — it took about 15 minutes.

One surprising insight I had: don't skip writing tests. I thought my API was solid until I added Jest and Supertest and found a bug where the DELETE route returned a 500 instead of a 404 for missing items. Tests save you from embarrassing bugs.

Building a REST API with Express and Node.js in 2026 is still one of the fastest ways to turn an idea into a working service. The stack is mature, the community is huge, and you now have a solid foundation to build on. Worth bookmarking before your next API project.