A Beginner's Guide to Node.js, Express, and Middleware

Adarsh RajAdarsh Raj
6 min read

As part of my journey to become a better developer, I'm a huge believer in "learning in public." It keeps me accountable and, hopefully, helps others who are on the same path. This week, I dove deep into the fundamentals of backend development with Node.js and Express.

It was a whirlwind of new concepts, from how the internet fundamentally works to writing some pretty cool, practical code. Here’s a breakdown of what I learned and how you can get started with it too.

The Absolute Basics: How Does the Web Talk?

Before writing a single line of server code, I realized I needed to solidify my understanding of the basics of HTTP communication. It’s like learning the grammar of a language before you try to write a story.

  • Domain Name/IP & Port: Think of an IP address (127.0.0.1) as a building's street address and the Port (3000, 8080) as the specific apartment number. When your browser wants to get data from a server, it needs to know exactly where to send the request.

  • HTTP Methods: These are the verbs of the web. They tell the server what action you want to perform. The most common ones are:

    • GET: To retrieve data (e.g., loading a webpage).

    • POST: To submit new data (e.g., filling out a sign-up form).

    • PUT: To update existing data.

    • DELETE: To remove data.

  • Headers: This is metadata sent along with a request or response. It can include things like the content type (Content-Type: application/json), authentication tokens, and cache control.

  • Body: This contains the actual data you're sending. For a GET request, the body is usually empty. For a POST or PUT request, it might contain the JSON data for a new user or a blog post.

  • Status Codes: These are the server's short replies to a request. You've probably seen 404 Not Found, but there are many others:

    • 200 OK: Everything went perfectly.

    • 201 Created: The resource was successfully created (e.g., after a POST request).

    • 400 Bad Request: The server didn't understand your request.

    • 500 Internal Server Error: Something went wrong on the server's end.

Building My First Server (The Hard Way!)

To appreciate the power of frameworks, I started by building a web server using only Node.js's built-in http module.

My goal was simple: create a server that could do basic math. Specifically, it would take two numbers from the URL's query parameters (?a=5&b=10) and return their sum.

Here's the basic idea:

JavaScript

const http = require('http');
const url = require('url');

const server = http.createServer((req, res) => {
  const parsedUrl = url.parse(req.url, true);
  const { pathname, query } = parsedUrl;

  if (pathname === '/sum') {
    const a = parseInt(query.a);
    const b = parseInt(query.b);
    const sum = a + b;

    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end(`The sum is ${sum}`);
  } else {
    res.writeHead(404, { 'Content-Type': 'text/plain' });
    res.end('Not Found');
  }
});

server.listen(3000, () => {
  console.log('Server running on port 3000');
});

This taught me a lot about manually parsing URLs and handling different "routes" with if/else statements. It works, but it can get messy very quickly.

Enter Express: Making Life Easier

After building a server from scratch, I moved on to Express.js. It's a minimal and flexible Node.js web application framework that provides a robust set of features to develop web and mobile applications. It makes everything so much cleaner!

With Express, setting up routes is incredibly simple.

JavaScript

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

// A simple route for the homepage
app.get('/', (req, res) => {
  // Sending an HTML response
  res.send('<h1>Welcome to my server!</h1>');
});

// A route sending a JSON response
app.get('/user', (req, res) => {
  res.json({ name: 'Alex', role: 'Developer' });
});

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

Look at how much cleaner that is! Express handles the routing, so I don't need a giant if/else block. I can also easily send different types of responses like HTML, JSON, or plain text.

The Magic of Middleware

This was the biggest "aha!" moment for me this week. Middleware functions are functions that have access to the request object (req), the response object (res), and the next function in the application’s request-response cycle.

Think of them as "gatekeepers" or "helpers" that can run code, make changes to the request and response objects, and end the cycle or pass control to the next middleware.

I set myself two challenges based on my learning list:

1. Create a request logger middleware: I wanted to log the method, URL, and timestamp for every single request that comes into my server.

JavaScript

const loggerMiddleware = (req, res, next) => {
  const timestamp = new Date().toISOString();
  console.log(`${timestamp} - ${req.method} request to ${req.url}`);
  next(); // Super important! This passes control to the next function.
};

// To use it for all routes:
app.use(loggerMiddleware);

Now, every time a request hits my server, I see a neat log in my console. This is amazing for debugging!

2. Count the total number of requests: This was a fun little exercise to see how you can maintain state.

JavaScript

let requestCount = 0;

const requestCounterMiddleware = (req, res, next) => {
  requestCount++;
  console.log(`Total requests received: ${requestCount}`);
  next();
};

app.use(requestCounterMiddleware);

Commonly Used Middlewares You Should Know

Finally, I learned about some essential, pre-built middlewares that solve common problems:

  • express.json(): When a client sends data to your server in a POST request (often as JSON), you need a way to parse it. This middleware does exactly that. It takes the incoming JSON body and makes it available on req.body.

  • cors: This stands for Cross-Origin Resource Sharing. By default, browsers block requests from a web page to a different domain for security reasons. If your frontend (e.g., at http://my-awesome-site.com) wants to fetch data from your backend (e.g., at http://api.my-awesome-site.com), you need the cors middleware to tell the browser it's okay.

Tying it all together with Fetch

Finally, to see it all in action, I used the fetch API in my browser's console to talk to my new Express server.

JavaScript

// In the browser console
fetch('http://localhost:3000/')
  .then(response => response.text())
  .then(data => console.log(data));
// Logs: <h1>Welcome to my server!</h1>

Conclusion

What a week! I went from the theoretical concepts of HTTP to building a functional Express server with custom middleware. The key takeaway for me is how frameworks like Express abstract away the tedious boilerplate, allowing you to focus on building features. And middleware is the superpower that makes Express so flexible.

I'm excited to see what I can build with this new knowledge next week.

What are you learning this week? Drop a comment below!

#wevdevelopment #backend #nodejs #expressjs #learninginpublic #javascript

0
Subscribe to my newsletter

Read articles from Adarsh Raj directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Adarsh Raj
Adarsh Raj