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

Santwan PathakSantwan Pathak
9 min read

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.


🔁 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

2. Twitter Route


🧙‍♂️ 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:

  1. Go to your terminal.

  2. Stop the server (usually with Ctrl + C).

  3. Start it again (node index.js).

  4. 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 standard node command. You might use this for your final, production version.

  • npm run dev: This uses nodemon 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;
💡
A quick note: In-memory storage is perfect for learning and prototyping. However, because it's stored in the server's active memory, all data will be lost if the server restarts. Real-world applications use a dedicated database (like MongoDB, PostgreSQL, etc.) to store data permanently.

➕ 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.

{
  "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.

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 tea

  • Add PUT /teas/:id to update tea info

  • Add DELETE /teas/:id to remove tea

  • Start exploring Mongoose and MongoDB for persistent storage

0
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."