πŸ› οΈ Building Our First Express.js Project – Tea Manager CRUD App PART 2

Santwan PathakSantwan Pathak
7 min read

Introduction

Welcome back to our series on building a backend API with Node.js and Express!

In the first part, we laid the essential groundwork for our Tea Shop API. We went from an empty folder to a running Express server, learned how to use nodemon for a smooth development workflow, and built our first two endpoints: one to create new teas (POST /teas) and another to list all the teas in our collection (GET /teas).

Now, it's time to complete our API's functionality and make it truly robust. In this second part, we will implement the remaining core features of a CRUD (Create, Read, Update, Delete) application. You'll learn how to target specific items on our menu to read, update, and delete them.

Finally, we'll take the most significant step in our journey: graduating from temporary, in-memory storage to a permanent database. We'll connect our application to MongoDB using the Mongoose library, transforming our prototype into a real-world application where data persists permanently.

Let's dive in and finish what we started!

πŸ” Step 8: GET /teas/:id – Fetch a Single Tea

Our API can now list the entire tea menu, which is great. But what if a user wants to see the details of just one specific tea? Asking for the entire list just to find one item is inefficient and slow, especially if our menu grows to hundreds of teas.

To solve this, we'll create an endpoint that retrieves a single tea using its unique identifier. We'll achieve this using a route parameter. A route parameter acts as a variable in our URL, allowing clients to specify which resource they want. We define it by placing a colon (:) before its name in the route path, like this: /teas/:id.

When a request comes in to a URL like /teas/1, Express will automatically capture the value 1 and make it available to us in our route handler.

Code with Comments

// R: Read - Get a single tea by its ID
app.get("/teas/:id", (req, res) => {
  // 'req.params' is an object containing route parameters.
  // Express captures the value from the URL (:id) and stores it here.
  // URL parameters always come in as strings, so we use parseInt()
  // to convert the ID to an integer for a strict comparison (===).
  const teaId = parseInt(req.params.id);

  // We use the JavaScript array 'find' method. It iterates over 'teaData'
  // and returns the very first element that satisfies the condition (t.id === teaId).
  const tea = teaData.find(t => t.id === teaId);

  // Error handling: What if the tea doesn't exist?
  // If 'find' doesn't find a matching tea, it returns 'undefined'.
  if (!tea) {
    // We send back a 404 Not Found status code and a clear error message.
    // It's crucial to 'return' here to stop further execution of the function.
    return res.status(404).send({ message: "Tea not found" });
  }

  // If the tea is found, send it back with a 200 OK status.
  res.status(200).send(tea);
});

πŸ“¬ Testing with Postman

To test this, you'll make a GET request, but this time you'll append the ID of the tea you want to the end of the URL. Let's assume you've already created a tea and its ID is 1.

Successful Response (Tea Found):

If a tea with ID 1 exists, the server will respond with just that single object.

JSON

{
  "id": 1,
  "name": "Ginger Tea",
  "price": "β‚Ή100"
}

Error Response (Tea Not Found):

If you request an ID that doesn't exist (e.g., /teas/99), your error handling logic will kick in, and you'll get this response:


✏ Step 9: PUT /teas/:id – Update Tea Details

Our API can now create and read data, but what happens when information changes? A tea might get a new name or a price update. We need a way to modify an existing resource, and for this, we use the PUT HTTP method.

The PUT method is typically used to update a resource at a specific URL. Similar to GET /teas/:id, our update route will use a route parameter to identify exactly which tea to modify.

For this operation, instead of using find() to get the tea object, we'll use findIndex(). This is because to update an element in an array, we need to know its position (its index). findIndex() returns this position for us, or -1 if no match is found.

Code with Comments

// U: Update - Modify an existing tea
app.put("/teas/:id", (req, res) => {
  // Find the index of the tea to update in the teaData array.
  const teaIndex = teaData.findIndex(t => t.id === parseInt(req.params.id));

  // If findIndex returns -1, the tea was not found.
  if (teaIndex === -1) {
    return res.status(404).send({ message: "Tea not found" });
  }

  // Get the new name and price from the request body.
  const { name, price } = req.body;

  // This is the core update logic.
  // We access the tea object by its index and create a new object for it.
  const updatedTea = {
    ...teaData[teaIndex], // 1. Copy all existing properties of the tea.
    name: name || teaData[teaIndex].name, // 2. If a new name exists, use it; otherwise, keep the old one.
    price: price || teaData[teaIndex].price, // 3. If a new price exists, use it; otherwise, keep the old one.
  };

  // Replace the old object with our new updatedTea object.
  teaData[teaIndex] = updatedTea;

  // Send back the updated tea with a 200 OK status.
  res.status(200).send(updatedTea);
});

πŸ“¬ Testing with Postman

To test this, you'll make a PUT request to the specific tea's URL. In the request body, you can include one or both of the fields you want to update.

Successful Response:

The server will respond with the complete, updated object.

{
  "id": 1,
  "name": "Premium Ginger Tea",
  "price": "β‚Ή150"
}

If you were to then make a GET request to /teas, you would see this updated information reflected in the full list.


πŸ—‘ Step 10: DELETE /teas/:id – Remove a Tea

We've reached the final letter in CRUD: "D" for Delete. An API isn't complete without the ability to remove resources. For this, we use the DELETE HTTP method, which is universally understood to mean "remove the resource at this specific URL."

The logic will be similar to our PUT route. We first need to find the tea we want to delete. Once we have its position (index) in our array, we can remove it. For this task, we'll use JavaScript's versatile splice() method.

Code with Comments

// D: Delete - Remove a tea from the collection
app.delete("/teas/:id", (req, res) => {
  // First, find the index of the tea to be deleted.
  const teaIndex = teaData.findIndex(t => t.id === parseInt(req.params.id));

  // If the tea is not found, return a 404 error.
  if (teaIndex === -1) {
    return res.status(404).send({ message: "Tea not found" });
  }

  // The splice() method changes the contents of an array by removing or
  // replacing existing elements.
  // Here, we go to the 'teaIndex' position and remove 1 element.
  teaData.splice(teaIndex, 1);

  // Send back a confirmation message.
  res.status(200).send({ message: "Tea deleted successfully" });
});

Pro Tip: While a 200 OK with a message is perfectly fine, many APIs use the status code 204 No Content for a successful deletion. This code tells the client "The request was successful, but I have nothing to send back to you," which is often true after deleting something.

πŸ“¬ Testing with Postman

To test this, you just need to send a DELETE request to the URL of the resource you want to remove. No request body is needed.

Successful Response:

You'll receive the confirmation message you defined in your code.

{
  "message": "Tea deleted successfully"
}

To fully confirm the deletion, you can now make a GET request to /teas again. You'll see that the tea with ID 1 is no longer in the list. With this, you have now built a complete, fully functional CRUD API!


πŸ“Œ Recap – Our Complete CRUD Tea API

At this point, our Tea Manager backend supports:

MethodEndpointPurpose
POST/teasAdd a new tea
GET/teasList all teas
GET/teas/:idFetch a specific tea
PUT/teas/:idUpdate tea details
DELETE/teas/:idDelete a tea

All data is still stored in memory, meaning it will reset when the server restarts β€” perfect for learning API basics, but not for production.


πŸ”œ Coming Up Next…

In the next section, we’ll:

  • Connect to MongoDB using Mongoose

  • Save and fetch teas from a real database

  • Understand schema and models in Mongoose

  • Deploy our API to DigitalOcean or another cloud platform

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