Ever wondered how popular websites like Netflix or Uber handle millions of requests every second? A big part of the answer lies in efficient server-side programming. If you’re building applications with Node.js, then Express.js is your go-to framework for crafting robust and scalable web servers and APIs. Think of Express.js as the fast lane for your Node.js web applications – it simplifies the complex parts, letting you focus on building amazing features.
In this lesson, we’ll embark on an exciting journey to set up your very first Express.js web server. We’ll cover everything from installation to handling basic web requests, giving you a solid foundation for your full-stack development career.
Node.js allows you to run JavaScript on the server side, which is fantastic for full-stack development. However, building a web server from scratch with just Node.js can be quite verbose. This is where Express.js steps in. It’s a minimalist, unopinionated web framework for Node.js, meaning it provides a lean set of features for web and mobile applications, giving you the flexibility to choose other tools as needed.
Key Benefits of Express.js:
Express.js is a fundamental component of the MERN (MongoDB, Express, React, Node.js) and MEAN (MongoDB, Express, Angular, Node.js) stacks, making it an essential skill for any aspiring full-stack developer.
Before we write any code, let’s get your environment ready. You’ll need Node.js and npm (Node Package Manager) installed on your system. If you haven’t already, please install them from the official Node.js website.
First, create a new directory for your project and navigate into it using your terminal:
mkdir my-express-app
cd my-express-app
Now, initialize a new Node.js project. This command creates a package.json file, which manages your project’s metadata and dependencies.
npm init -y
The -y flag answers "yes" to all prompts, creating a default package.json file instantly. You can open this file to see basic information about your project.
Next, we need to install the Express.js package as a dependency for your project:
npm install express
This command downloads Express.js and its dependencies and adds them to a node_modules folder in your project directory. It also updates your package.json file to list Express.js as a dependency.
Now that Express.js is installed, let’s create a simple server that responds with a "Hello, FullStackDost!" message when you visit it in your browser.
In your my-express-app directory, create a new file named index.js (or app.js, it’s a common convention).
Open index.js and paste the following code:
// 1. Import the Express module
const express = require('express');
// 2. Create an Express application instance
const app = express();
// 3. Define the port number for your server
const PORT = 3000; // A common port for development
// 4. Define a route handler for the root URL ('/')
// When a GET request comes to '/', execute this function
app.get('/', (req, res) => {
res.send('Hello, FullStackDost! Welcome to Express.js!');
});
// 5. Start the server and listen for incoming requests
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
console.log('Press Ctrl+C to stop the server.');
});
Save the index.js file. Now, go back to your terminal and run your Node.js application:
node index.js
You should see the message: Server is running on http://localhost:3000.
Open your web browser and navigate to http://localhost:3000. You should see the message "Hello, FullStackDost! Welcome to Express.js!" displayed in your browser.
Congratulations! You’ve just created and run your very first Express.js web server!
Routing is how your Express.js application determines how to respond to a client request to a particular endpoint (URL) and an HTTP method (GET, POST, etc.).
A route is essentially a combination of:
/, /users, /products/:id)In our "Hello, FullStackDost!" example, app.get('/', ...) defines a route that handles HTTP GET requests to the root path (/).
Let’s expand our index.js to handle requests for different URLs. Replace the previous index.js content with this updated version:
const express = require('express');
const app = express();
const PORT = 3000;
// Root route
app.get('/', (req, res) => {
res.send('Welcome to the Homepage!');
});
// About page route
app.get('/about', (req, res) => {
res.send('This is the About page of our FullStackDost app.');
});
// Contact page route
app.get('/contact', (req, res) => {
res.send('You can reach us at contact@fullstackdost.com');
});
// Route with a URL parameter
app.get('/greet/:name', (req, res) => {
const name = req.params.name; // Accessing the URL parameter
res.send(`Hello, ${name}! Glad to see you.`);
});
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});
Now, restart your server (Ctrl+C to stop, then node index.js again) and try visiting these URLs in your browser:
http://localhost:3000/http://localhost:3000/abouthttp://localhost:3000/contacthttp://localhost:3000/greet/Dost (try replacing ‘Dost’ with your name!)Notice how req.params.name allows you to capture dynamic parts of the URL. This is incredibly powerful for building RESTful APIs!
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. They can modify the request and response objects, end the request-response cycle, or call the next middleware function.
Imagine a clean diagram here depicting a request entering Express.js, passing through several "gates" (middleware functions) that perform tasks like logging, authentication, or data parsing, before finally reaching its destination route handler.
Let’s add a simple middleware that logs every incoming request’s URL and timestamp to your console:
const express = require('express');
const app = express();
const PORT = 3000;
// --- START: Our custom logging middleware ---
app.use((req, res, next) => {
const timestamp = new Date().toISOString();
console.log(`[${timestamp}] ${req.method} request to ${req.url}`);
next(); // IMPORTANT: Call next() to pass control to the next middleware/route handler
});
// --- END: Our custom logging middleware ---
// Root route
app.get('/', (req, res) => {
res.send('Welcome to the Homepage!');
});
// About page route
app.get('/about', (req, res) => {
res.send('This is the About page of our FullStackDost app.');
});
// Contact page route
app.get('/contact', (req, res) => {
res.send('You can reach us at contact@fullstackdost.com');
});
// Route with a URL parameter
app.get('/greet/:name', (req, res) => {
const name = req.params.name;
res.send(`Hello, ${name}! Glad to see you.`);
});
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});
Restart your server. Now, every time you visit any of your routes in the browser, you’ll see a log message in your terminal indicating the request method and URL. The app.use() method applies this middleware to *all* incoming requests.
The next() function is crucial! It tells Express.js to pass control to the next middleware function or route handler in the stack. Without it, your request would get stuck in the middleware, and your browser would just keep spinning.
Time to get your hands dirty and apply what you’ve learned! Modify your index.js file to complete the following tasks:
/dashboard.app.use().let requestCount = 0; outside the middleware function).requestCount.requestCount to the console (e.g., console.log(`Total requests: ${requestCount}`);).next()!After implementing these, restart your server and test your new dashboard route and observe the request counter in your terminal!
Today, you’ve taken significant steps into the world of server-side development with Express.js. You’ve learned:
This is just the beginning! Express.js offers a vast array of features for building complex web applications and APIs. Keep practicing, keep experimenting, and you’ll soon be building robust backends with confidence. In our next lessons, we’ll dive deeper into more advanced routing, handling different HTTP methods, and working with request bodies.