R
Rishtaara
Knowledge Hub
Technology & IT

Node.js Fundamentals: Backend Guide

By Rishtaara Editorial Team38 min read
#Node.js#Express#REST#Backend

npm, HTTP servers, Express, middleware, REST APIs, and database integration.

What Node.js actually is

Node.js is a JavaScript runtime built on the V8 engine. It lets you run JavaScript outside the browser, mainly for servers, CLI tools, and backend automation.

Its event-driven, non-blocking I/O model makes it efficient for APIs and real-time apps where many concurrent requests do short waiting operations.

Where Node fits in a stack

  • HTTP API layer and backend services.
  • Authentication and session handling.
  • File uploads and background workers.
  • Build tooling, scripts, and task automation.
Node REPL and version checks
node -v
npm -v
node
> console.log("Rishtaara Node.js course");

CommonJS and ES Modules

Modern Node supports ES Modules with import/export. CommonJS (require/module.exports) still exists in many legacy projects.

ES module export and import
// math.js
export function add(a, b) {
  return a + b;
}

// app.js
import { add } from "./math.js";
console.log(add(5, 7));

npm essentials

  • npm init creates package metadata and scripts.
  • npm install adds dependencies to package.json.
  • Use scripts for repeatable commands (dev, test, lint).
  • Keep dependencies updated and audited.
Project bootstrap
npm init -y
npm install express
npm install -D nodemon
npm pkg set type=module

Using the built-in http module

Before Express, learn how raw request and response objects work. This helps you debug headers, status codes, and response lifecycle in any framework.

Minimal HTTP server
import http from "node:http";

const server = http.createServer((req, res) => {
  if (req.url === "/health") {
    res.writeHead(200, { "Content-Type": "application/json" });
    res.end(JSON.stringify({ ok: true }));
    return;
  }

  res.writeHead(404, { "Content-Type": "text/plain" });
  res.end("Not found");
});

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

HTTP request lifecycle basics

  • Client sends method, URL, headers, and optional body.
  • Server routes to handler logic.
  • Handler validates input and performs business work.
  • Response returns status code, headers, and payload.

Why Express

Express simplifies route definitions, middleware composition, JSON handling, and error propagation. It is still a common baseline in Node backend interviews and real projects.

Basic Express app
import express from "express";

const app = express();
app.use(express.json());

app.get("/", (req, res) => {
  res.json({ message: "Welcome to Node.js Fundamentals" });
});

app.get("/courses/:slug", (req, res) => {
  res.json({ slug: req.params.slug });
});

app.listen(4000, () => {
  console.log("API running on http://localhost:4000");
});

Routing patterns

  • Route params for dynamic segments.
  • Query params for filtering and pagination.
  • Separate routers for domain modules.
  • Keep controller logic slim and testable.

Middleware chain mental model

Middleware runs in order and can inspect, transform, or reject requests. Good middleware design keeps auth, validation, and logging reusable across routes.

Request logger and simple auth guard
function requestLogger(req, res, next) {
  console.log(req.method, req.url);
  next();
}

function authGuard(req, res, next) {
  const token = req.headers.authorization;
  if (!token) {
    return res.status(401).json({ error: "Unauthorized" });
  }
  next();
}

app.use(requestLogger);
app.get("/protected", authGuard, (req, res) => {
  res.json({ secret: "Only for authenticated users" });
});

Error middleware

  • Call next(error) from route handlers.
  • Do not leak stack traces in production responses.
  • Normalize error shape for frontend consistency.
Centralized error handler
app.use((err, req, res, next) => {
  console.error("Unexpected error:", err);
  res.status(500).json({ error: "Internal server error" });
});

REST resource design

  • Use nouns for resources: /users, /courses, /enrollments.
  • GET for reads, POST for creation, PUT/PATCH for updates, DELETE for removal.
  • Return proper status codes and structured JSON responses.
  • Add pagination and filtering for list endpoints.

CRUD routes example

In-memory course API sketch
const courses = [{ id: 1, title: "Node Basics" }];

app.get("/api/courses", (req, res) => {
  res.json({ data: courses });
});

app.post("/api/courses", (req, res) => {
  const newCourse = { id: Date.now(), title: req.body.title };
  courses.push(newCourse);
  res.status(201).json({ data: newCourse });
});

app.patch("/api/courses/:id", (req, res) => {
  const id = Number(req.params.id);
  const course = courses.find((c) => c.id === id);
  if (!course) return res.status(404).json({ error: "Course not found" });
  course.title = req.body.title ?? course.title;
  res.json({ data: course });
});

Async/await in real handlers

Most backend operations are asynchronous: database calls, external APIs, and file services. Use async/await with try/catch for readable control flow and safer error propagation.

Async route with repository function
async function getCourseById(id) {
  // pretend this is a DB call
  return { id, title: "Node.js Fundamentals" };
}

app.get("/api/courses/:id", async (req, res, next) => {
  try {
    const course = await getCourseById(req.params.id);
    if (!course) return res.status(404).json({ error: "Not found" });
    res.json({ data: course });
  } catch (error) {
    next(error);
  }
});

Database integration principles

  • Use connection pooling, not per-request new connections.
  • Validate and sanitize input before persistence.
  • Wrap multi-step state changes in transactions when supported.
  • Log slow queries and timeouts.

Project scope

Create a production-style backend service for course enrollment with users, courses, and enrollments resources. Include validation, auth middleware, and persistent storage integration.

Milestone checklist

  • Scaffold Express app with modular routes and controllers.
  • Implement CRUD endpoints for users and courses.
  • Create enrollment endpoint with duplicate-enrollment guard.
  • Add pagination, filtering, and basic rate limiting.
  • Write API tests for success and failure scenarios.
Enrollment route starter
app.post("/api/enrollments", async (req, res, next) => {
  try {
    const { userId, courseId } = req.body;
    // 1) validate payload
    // 2) ensure user and course exist
    // 3) prevent duplicate enrollment
    // 4) insert enrollment row/document
    res.status(201).json({ message: "Enrollment created" });
  } catch (error) {
    next(error);
  }
});

Key Takeaways

  • Node.js enables JavaScript backend development with event-driven I/O.
  • Express routing and middleware patterns are core backend building blocks.
  • Robust APIs require validation, error handling, and consistent responses.
  • Async/await and connection management are essential for real DB workloads.
  • A complete Node project combines architecture, correctness, and tests.

Frequently Asked Questions

Do I need to learn core http before Express?
Yes, at least the basics. Understanding raw request/response behavior makes Express debugging and performance reasoning much easier.
How should I structure a Node backend for scale?
Start with route-controller-service-repository separation, centralized config, reusable middleware, and a predictable error/response layer.
Is Node.js good for CPU-heavy tasks?
It is best for I/O-heavy workloads. For CPU-heavy jobs, use worker threads, queues, or offload to dedicated processing services.