🌐 Creating a Simple REST-like API with JSON using the Node.js http Module

Introduction

A REST-like API allows applications to communicate by exchanging data over HTTP using standard methods such as GET, POST, PUT, and DELETE. In this tutorial, you'll build a simple JSON API using Node.js's built-in http module without relying on external frameworks. The API will manage an in-memory collection of users and demonstrate the fundamentals of RESTful application development.

Information

This tutorial stores data in memory using a JavaScript array. All data is lost when the server restarts, making this approach suitable for learning and prototyping rather than production use.

Learning Objectives

  • Create an HTTP server using the http module.
  • Build REST-like endpoints using different HTTP methods.
  • Send and receive JSON data.
  • Implement CRUD (Create, Read, Update, Delete) operations.
  • Return meaningful HTTP status codes.

Understanding REST-like APIs

A REST-like API organizes resources using URLs and HTTP methods. Each method performs a specific operation on a resource.

HTTP MethodEndpointOperation
GET/usersRetrieve all users.
POST/usersCreate a new user.
PUT/users?id=1Update an existing user.
DELETE/users?id=1Delete a user.

Project Structure

📁 rest-api
📄 server.js
📄 package.json (optional)

Creating the API Server

server.js

const http = require("http");

const users = [];

const server = http.createServer((req, res) => {

  const url = new URL(req.url, "http://localhost:3000");

  res.setHeader("Content-Type", "application/json");

  // GET /users
  if (url.pathname === "/users" && req.method === "GET") {
    return res.end(JSON.stringify(users));
  }

  // POST /users
  if (url.pathname === "/users" && req.method === "POST") {

    let body = "";

    req.on("data", chunk => {
      body += chunk;
    });

    req.on("end", () => {

      try {

        const user = JSON.parse(body);

        users.push(user);

        res.writeHead(201);

        res.end(JSON.stringify({
          message: "User created",
          data: user
        }));

      } catch {

        res.writeHead(400);

        res.end(JSON.stringify({
          error: "Invalid JSON"
        }));

      }

    });

    return;
  }

  // PUT /users?id=1
  if (url.pathname === "/users" && req.method === "PUT") {

    const id = Number(url.searchParams.get("id"));

    let body = "";

    req.on("data", chunk => {
      body += chunk;
    });

    req.on("end", () => {

      try {

        const updatedUser = JSON.parse(body);

        const index = users.findIndex(user => user.id === id);

        if (index === -1) {
          res.writeHead(404);

          return res.end(JSON.stringify({
            error: "User not found"
          }));
        }

        users[index] = {
          ...users[index],
          ...updatedUser
        };

        res.end(JSON.stringify(users[index]));

      } catch {

        res.writeHead(400);

        res.end(JSON.stringify({
          error: "Invalid JSON"
        }));

      }

    });

    return;
  }

  // DELETE /users?id=1
  if (url.pathname === "/users" && req.method === "DELETE") {

    const id = Number(url.searchParams.get("id"));

    const index = users.findIndex(user => user.id === id);

    if (index === -1) {

      res.writeHead(404);

      return res.end(JSON.stringify({
        error: "User not found"
      }));

    }

    users.splice(index, 1);

    return res.end(JSON.stringify({
      message: "User deleted"
    }));

  }

  res.writeHead(404);

  res.end(JSON.stringify({
    error: "Route not found"
  }));

});

server.listen(3000, () => {
  console.log("Server running at http://localhost:3000");
});

Request Processing Flow

🌐 Client Sends Request
📍 Check URL Path
🔍 Check HTTP Method
Route Matches
Route Does Not Match
âš™ī¸ Perform CRUD Operation
📨 Return JSON Response
❌ Return 404 Response

Testing the Endpoints

Create a User

POST /users

POST /users HTTP/1.1
Host: localhost:3000
Content-Type: application/json

{
  "id": 1,
  "name": "Alice",
  "email": "alice@example.com"
}

Retrieve All Users

GET /users

GET /users HTTP/1.1
Host: localhost:3000

Update a User

PUT /users?id=1

PUT /users?id=1 HTTP/1.1
Host: localhost:3000
Content-Type: application/json

{
  "name": "Alice Johnson"
}

Delete a User

DELETE /users?id=1

DELETE /users?id=1 HTTP/1.1
Host: localhost:3000

Using fetch()

GET Request

Fetch users

fetch("http://localhost:3000/users")
  .then(response => response.json())
  .then(data => {
    console.log(data);
  });

POST Request

Create user

fetch("http://localhost:3000/users", {
  method: "POST",
  headers: {
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    id: 2,
    name: "Bob",
    email: "bob@example.com"
  })
})
.then(response => response.json())
.then(data => console.log(data));

CRUD Operation Flow

Users Collection
➕ POST → Add User
📖 GET → Read Users
âœī¸ PUT → Update User
đŸ—‘ī¸ DELETE → Remove User

HTTP Status Codes

Status CodeMeaningUsage
200OKSuccessful GET, PUT, or DELETE request.
201CreatedNew resource created successfully.
400Bad RequestMalformed JSON or invalid input.
404Not FoundUnknown route or missing user.
500Internal Server ErrorUnexpected server-side error.

Advantages

  • No external dependencies.
  • Demonstrates the fundamentals of HTTP and REST concepts.
  • Ideal for learning request handling and JSON processing.
  • Easy to extend with additional endpoints.

Limitations

  • Data is stored only in memory.
  • Routes are manually implemented.
  • No built-in middleware or validation.
  • Not suitable for large production applications.

Warning

This example is intended for educational purposes. Production APIs should use persistent databases, comprehensive input validation, authentication, authorization, and structured error handling.

Best Practices

  • Use meaningful HTTP status codes.
  • Validate incoming request data before processing.
  • Handle malformed JSON using try...catch.
  • Return consistent JSON response structures.
  • Separate routing, business logic, and data access into different modules as the application grows.

Best Practice

In RESTful APIs, resource URLs should represent nouns (such as /users), while HTTP methods should define the action to perform. This convention leads to APIs that are intuitive, predictable, and easier to maintain.

Official Documentation

Node.js HTTP Module Documentation

Summary

Summary

You created a simple REST-like JSON API using the Node.js http module. The API supports basic CRUD operations with GET, POST, PUT, and DELETE, processes JSON request bodies, stores data in memory, returns appropriate HTTP status codes, and demonstrates the core principles of RESTful web service development without external frameworks.