🛠️ Building Our First Express.js Project – Tea Manager CRUD App PART 1


Now that we've understood the architecture and why we're using Express.js and Node.js, it's time to get our hands dirty with real-world code.
We’re building a fun, beginner-friendly project:
A Tea Manager backend app ☕ – where you can create, list, and manage different types of tea.
📁 Step 1: Initialize the Node Project
We start by creating a fresh folder:
mkdir 02-express
cd 02-express
npm init
You’ll be prompted with a few setup questions. Here’s what I used:
Package name:
tea-manager
Entry point:
index.js
Description: A simple manager for different teas
Author: Santwan
Once complete, you'll get a package.json
— the brain of your Node project.
📦 Step 2: Install Express.js
npm install express
Also, since we’ll use modern ES Modules syntax (import
instead of require
), add this to your package.json
:
"type": "module"
🧪 Step 3: Basic Express Server
Create index.js
:
import express from "express";
const app = express();
const port = 3000;
app.listen(port, () => {
console.log(`Server is running at port ${port}...`);
});
Run it:
npm run start
You’ll see:
Server is running at port 3000...
Boom — your first Express server is up!
✨ Step 4: Add Your First Route
Now for the magic! A server that's running is great, but it doesn't do anything until we tell it how to handle incoming requests. This is where routes come in. Think of a route as a receptionist for your application; it listens for a specific request at a specific URL and directs it to the right block of code to handle it.
Let's create our very first route. We'll start with the most fundamental one: the root route (/
). This is the entry point of your application, what a user accesses when they go to your main URL (like https://www.google.com
or, in our case, http://127.0.0.1:3000
). We'll tell our app to listen for a GET
request, which is the most common type of HTTP request, typically used for retrieving data—it's what your browser performs every time you type in a web address.
Code with Comments
// This sets up a GET route for the home page ('/') of our application.
// 'app.get' tells the server to listen for GET requests at a specific path.
app.get("/", (req, res) => {
// The function we provide is a "callback." It only runs when a request
// matching the method (GET) and path ("/") is received.
// It receives two crucial objects: 'req' (request) and 'res' (response).
// 'req' holds all the information about the incoming request from the client.
// 'res' is our toolkit for building and sending a response back to that client.
// We use 'res.send()' to send a simple text response back to the browser.
res.send("Hello from Santwan and his tea ☕");
});
Expected Output
When you test this endpoint, you are simulating a browser or client making a GET
request to your server's root URL. The server, following the instructions in your route handler, sends back the defined string.
Method:
GET
Server Response:
Hello from Santwan and his tea ☕
🔁 Step 5: Add Multiple Routes
Excellent, our digital tea shop now has a front door! But a real shop has different sections—one for hot tea, another for iced tea, maybe a contact page. In web development, we create these "sections" by defining more routes. Each route handles a different path, allowing us to build out the features of our application in an organized and scalable way.
Let's add two new routes. One will be for customers interested in ice tea (/ice-tea
), and another will be a link to a Twitter profile (/twitter
). This demonstrates how you can create distinct endpoints that serve completely different purposes, all within the same application.
Code with Comments
// Here we define another GET route. This one listens on the '/ice-tea' path.
// When a user makes a GET request to yoursite.com/ice-tea, this specific
// function will execute, and the other routes will be ignored for this request.
app.get("/ice-tea", (req, res) => {
res.send("What ice tea would you prefer?");
});
// And a third route for the '/twitter' path. This shows how versatile
// routes are. They can serve content, redirect to other sites, or return
// structured data like JSON. Here, we're just sending a simple string.
app.get("/twitter", (req, res) => {
res.send("santwan.com");
});
Expected Output
Now you can test each endpoint individually. Notice how changing the path in the URL results in a completely different response from the server, because it's triggering a different route handler.
1. Ice Tea Route
Method:
GET
Server Response:
What ice tea would you prefer?
2. Twitter Route
Method:
GET
Server Response:
Plaintext
santwan.com
🧙♂️ Step 6: Use nodemon
for Dev Mode
So far, every time you've made a change to your index.js
file, you've had to follow a repetitive, manual process:
Go to your terminal.
Stop the server (usually with
Ctrl + C
).Start it again (
node index.js
).Go back to your browser or Postman to test the change.
This works, but it's slow and tedious. It breaks your focus and adds unnecessary friction to the creative process of coding. In development, speed and a smooth workflow are everything. This is where a fantastic tool called nodemon
comes to the rescue.
nodemon
is a utility that wraps your Node application, watches for any file changes in the directory, and automatically restarts the server for you. Think of it as a vigilant assistant that reloads your app the instant you save a file.
Installing nodemon
First, let's install it. We'll install it as a "development dependency" because it's a tool for you, the developer, and not something the final application needs to run in production. We use the -D
(or --save-dev
) flag to tell npm to record it this way.
Bash
npm install -D nodemon
Configuring package.json
Next, we need to create a convenient shortcut to run our server using nodemon
. We do this by adding a new command to the scripts
section of our package.json
file. This section allows us to define custom project commands.
"scripts": {
"start": "node index.js",
"dev": "nodemon index.js"
}
By adding this, we now have two ways to run our app:
npm start
: This uses the standardnode
command. You might use this for your final, production version.npm run dev
: This usesnodemon
to run our app in a "development mode," with auto-restarting enabled.
Running in Development Mode
Now, instead of node index.js
, you can simply run this command in your terminal:
Bash
npm run dev
You'll see a slightly different output in your console, letting you know that nodemon
is running and watching your files.
Go ahead and try it! With the server running via npm run dev
, make a small change to any of the res.send()
messages in your index.js
file and hit save. You'll see the server instantly restart in your terminal. Now you can just refresh your browser to see the change immediately. No more manual restarts!
🔄 Step 7: Build a CRUD API (Tea Manager)
So far, our server can only respond with predefined, static strings. To build a real application, our server needs to manage data—create it, read it, update it, and delete it (an acronym known as CRUD).
For this tutorial, we'll use in-memory storage. This means we'll store our data in a simple JavaScript array directly in our server's memory.
// This array will act as our temporary, in-memory database.
let teaData = [];
// A simple counter to generate unique IDs for our tea objects.
let nextId = 1;
➕ POST /teas – Add New Tea
The first CRUD operation is "Create." We need a way for a client to send us new data to be saved. For this, we use the POST
HTTP method. While a GET
request is for retrieving data, a POST
request is for submitting new data to the server to create a new resource.
Before we can handle the data, we need to tell Express how to receive it. Clients typically send data in a format called JSON. Our Express server doesn't understand raw JSON out of the box. We need to use middleware—a piece of code that runs between the request and our handler—to translate it. The express.json()
middleware does exactly this: it parses incoming JSON requests and puts the parsed data onto the req.body
object for us to use.
Code with Comments
// This line adds the express.json() middleware to our application.
// It MUST be placed before any routes that need to handle JSON data.
app.use(express.json());
// C: Create - Add a new tea
app.post("/teas", (req, res) => {
// Extract the 'name' and 'price' from the request body.
const { name, price } = req.body;
// Create a new tea object with a unique ID.
const newTea = {
id: nextId++, // Use the current nextId, then increment it for the next one
name,
price,
};
// Add the new tea object to our in-memory data array.
teaData.push(newTea);
// Send a response back to the client.
// 201 Created is the standard status code for a successful POST request.
res.status(201).send(newTea);
});
📬 Testing with Postman
To test this, you must send data in the body of the request.
Method:
POST
Body Tab → raw → JSON
{
"name": "Ginger Tea",
"price": "₹100"
}
If successful, the server will respond with the object that was just created, now including its new id
.
Server Response:
{
"id": 1,
"name": "Ginger Tea",
"price": "₹100"
}
📋 GET /teas – List All Teas
Now that we can add teas, we need a way to see all the teas in our collection. This is the "Read" part of CRUD. A simple GET
request is perfect for this, as we're just asking the server to retrieve existing data.
This endpoint will return the entire teaData
array.
Code with Comments
JavaScript
// R: Read - Get all teas
app.get("/teas", (req, res) => {
// Send the full array of tea data with a 200 OK status.
// Express automatically converts the JavaScript array to JSON format.
res.status(200).send(teaData);
});
📬 Testing with Postman
This is a simple GET
request with no body.
Method:
GET
Server Response: If you first created the "Ginger Tea" and then created a "Masala Tea," your response would look like this. The endpoint will return whatever is currently in the teaData
array.
[
{
"id": 1,
"name": "Ginger Tea",
"price": "₹100"
},
{
"id": 2,
"name": "Masala Tea",
"price": "₹120"
}
]
✅ Recap of What We Built
✔ Initialized a Node + Express project
✔ Installed Express and nodemon
✔ Created multiple GET routes
✔ Built a POST API to add new tea data
✔ Built a GET API to list all teas
🔜 Coming Next…
In the next part, we’ll:
Build
GET /teas/:id
to fetch a single teaAdd
PUT /teas/:id
to update tea infoAdd
DELETE /teas/:id
to remove teaStart exploring Mongoose and MongoDB for persistent storage
Subscribe to my newsletter
Read articles from Santwan Pathak directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Santwan Pathak
Santwan Pathak
"A beginner in tech with big aspirations. Passionate about web development, AI, and creating impactful solutions. Always learning, always growing."