Real-world examples of 3 examples of setting up a Node.js server

If you’re trying to learn Node, staring at yet another dry code snippet can feel like watching paint dry. That’s why walking through real, working examples of 3 examples of setting up a Node.js server can make everything finally click. Instead of abstract theory, we’ll build small, practical servers you could actually use in side projects or at work. In this guide, we’ll start with a tiny “hello world” HTTP server, move into an Express-based JSON API, and finish with a slightly more realistic setup that reads data from a file and uses environment variables. Along the way, you’ll see multiple examples of how to structure routes, handle JSON, and organize your server so it doesn’t turn into a tangled mess once you add more features. Think of this as sitting next to a friendly coworker who’s walking you through the code line by line, not lecturing you from a podium. By the end, you’ll have several real examples you can copy, modify, and build on.
Written by
Taylor
Published

Let’s start with the simplest possible server. No frameworks, no build tools, just Node’s built-in http module. This is the first of our 3 examples of setting up a Node.js server, and it’s great for understanding what Express and other frameworks are doing under the hood.

Create a file called server-basic.js:

// server-basic.js
const http = require('http');

const PORT = 3000;

const server = http.createServer((req, res) => {
  // Simple routing based on URL and method
  if (req.url === '/' && req.method === 'GET') {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('Hello from plain Node.js!');
  } else if (req.url === '/time' && req.method === 'GET') {
    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({
      serverTime: new Date().toISOString()
    }));
  } else {
    res.writeHead(404, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ error: 'Not found' }));
  }
});

server.listen(PORT, () => {
  console.log(`Basic server running at http://localhost:${PORT}`);
});

Run it with:

node server-basic.js

Open http://localhost:3000 in your browser and you’ll see your first response. Visit http://localhost:3000/time and you’ll get JSON.

This is the most stripped-down example of a Node.js HTTP server you can build, and it’s the starting point for many tutorials. Even big companies that use more advanced stacks often begin with something that looks a lot like this when they prototype.

A few real examples of where you might use this basic pattern:

  • A quick internal tool that needs only one or two endpoints
  • A health-check endpoint for a background process
  • A small educational demo when teaching HTTP basics

You wouldn’t ship a large production API like this, but as the first of our 3 examples of setting up a Node.js server, it gives you a clean mental model of requests, responses, and routing.


The second example of 3 examples of setting up a Node.js server: Express JSON API

Once you understand the raw http module, you quickly hit friction. You want routing, middleware, and easier JSON handling. That’s where Express comes in. It’s still wildly popular in 2024–2025, and you’ll see it in tons of real examples on GitHub and in production systems.

Install Express in a new project:

mkdir express-api-example
cd express-api-example
npm init -y
npm install express

Create server-express.js:

// server-express.js
const express = require('express');
const app = express();

const PORT = process.env.PORT || 4000;

// Built-in middleware to parse JSON bodies
app.use(express.json());

// In-memory data store (for demo only)
let todos = [
  { id: 1, text: 'Learn Node.js basics', done: false },
  { id: 2, text: 'Build an Express API', done: false }
];

// GET /api/todos - list all todos
app.get('/api/todos', (req, res) => {
  res.json(todos);
});

// GET /api/todos/:id - get one todo
app.get('/api/todos/:id', (req, res) => {
  const id = Number(req.params.id);
  const todo = todos.find(t => t.id === id);

  if (!todo) {
    return res.status(404).json({ error: 'Todo not found' });
  }

  res.json(todo);
});

// POST /api/todos - create a new todo
app.post('/api/todos', (req, res) => {
  const { text } = req.body;

  if (!text || typeof text !== 'string') {
    return res.status(400).json({ error: 'Text is required' });
  }

  const newTodo = {
    id: todos.length ? todos[todos.length - 1].id + 1 : 1,
    text,
    done: false
  };

  todos.push(newTodo);
  res.status(201).json(newTodo);
});

// PATCH /api/todos/:id - mark as done
app.patch('/api/todos/:id', (req, res) => {
  const id = Number(req.params.id);
  const todo = todos.find(t => t.id === id);

  if (!todo) {
    return res.status(404).json({ error: 'Todo not found' });
  }

  todo.done = true;
  res.json(todo);
});

// Fallback error handler
app.use((err, req, res, next) => {
  console.error(err);
  res.status(500).json({ error: 'Internal server error' });
});

app.listen(PORT, () => {
  console.log(`Express API server running on http://localhost:${PORT}`);
});

Now run:

node server-express.js

Hit http://localhost:4000/api/todos and you’ll see JSON. You can use a tool like curl, Postman, or VS Code’s REST client to send POST and PATCH requests.

This second entry in our 3 examples of setting up a Node.js server shows several patterns you’ll see in real-world APIs:

  • Separate routes by HTTP method and path
  • Use JSON as the default data format
  • Validate inputs and send helpful error messages
  • Use middleware for cross-cutting concerns like parsing and error handling

More real examples of how this Express server pattern is used

To give you more than just abstract theory, here are a few realistic scenarios where this style of Express server shows up:

  • A lightweight backend for a React or Vue single-page app that needs a few CRUD endpoints
  • A prototype API for a mobile app before the team invests in a larger service architecture
  • A webhook receiver for third-party services (payment gateways, chat apps, etc.)
  • A small internal analytics collector that receives JSON payloads from different tools

If you browse through open-source projects on GitHub, many of the best examples of beginner-friendly backends use almost this exact structure. You’ll see the same app.get, app.post, and app.use patterns repeated over and over.

For a broader context on JavaScript and Node usage in industry, you can look at educational resources from universities such as Harvard’s CS50 materials which often reference modern web stacks, including Node-based servers.


The third example of 3 examples of setting up a Node.js server: environment variables and file data

The third of our 3 examples of setting up a Node.js server adds two things you’ll almost always need in real projects in 2024–2025:

  • Configuration via environment variables
  • Data loaded from a file instead of hardcoded arrays

You’ll see this pattern in real examples across many companies: a small Node service reading configuration from .env, reading data from disk or a database, and exposing a few focused endpoints.

First install a couple of helpers in a new folder:

mkdir env-file-server
cd env-file-server
npm init -y
npm install express dotenv

Create a .env file (this should not be committed to public repos):

PORT=5000
DATA_FILE=./data/products.json

Create a data folder and a products.json file:

[
  { "id": 1, "name": "Mechanical Keyboard", "price": 99.99 },
  { "id": 2, "name": "USB-C Hub", "price": 39.5 },
  { "id": 3, "name": "Noise-Cancelling Headphones", "price": 199.0 }
]

Now create server-env-file.js:

// server-env-file.js
require('dotenv').config();
const fs = require('fs');
const path = require('path');
const express = require('express');

const app = express();
const PORT = process.env.PORT || 5000;
const DATA_FILE = process.env.DATA_FILE || './data/products.json';

app.use(express.json());

// Helper to read products from file
function readProducts() {
  const filePath = path.resolve(DATA_FILE);
  const raw = fs.readFileSync(filePath, 'utf8');
  return JSON.parse(raw);
}

// GET /api/products - list products
app.get('/api/products', (req, res) => {
  try {
    const products = readProducts();
    res.json(products);
  } catch (err) {
    console.error('Failed to read products:', err);
    res.status(500).json({ error: 'Failed to load products' });
  }
});

// GET /api/products/:id - get product by id
app.get('/api/products/:id', (req, res) => {
  try {
    const id = Number(req.params.id);
    const products = readProducts();
    const product = products.find(p => p.id === id);

    if (!product) {
      return res.status(404).json({ error: 'Product not found' });
    }

    res.json(product);
  } catch (err) {
    console.error('Error getting product:', err);
    res.status(500).json({ error: 'Failed to load product' });
  }
});

// Simple health check endpoint
app.get('/health', (req, res) => {
  res.json({ status: 'ok', time: new Date().toISOString() });
});

app.listen(PORT, () => {
  console.log(`Env/file-based server running on http://localhost:${PORT}`);
});

Run it with:

node server-env-file.js

Now you have a server where:

  • The port is configurable via environment variables
  • The data file path is configurable
  • Data is loaded from disk, mimicking a small product catalog

This third entry in our 3 examples of setting up a Node.js server is much closer to how small production services are actually wired. In real deployments, the data might come from PostgreSQL or MongoDB instead of a JSON file, but the shape of the code is surprisingly similar.

Real examples of where this pattern shines

This style of server works well for:

  • Small internal tools that read from CSV or JSON exports
  • Feature flags or configuration services backed by a simple file
  • Low-traffic APIs that don’t justify a full database yet
  • Teaching environments where you want to avoid database setup but still simulate data loading

Educational sites and courses that teach backend development often recommend separating configuration from code like this, echoing long-standing software engineering guidance you’ll also see in computer science curricula at universities such as MIT and Harvard.


Comparing all 3 examples of setting up a Node.js server

Now that we’ve walked through these 3 examples of setting up a Node.js server, it helps to zoom out and see when you might choose each one.

The plain http server is ideal when you want:

  • A tiny footprint with zero dependencies
  • To learn how requests and responses work at the lowest level
  • A throwaway tool or a quick experiment

The Express JSON API example is a sweet spot for:

  • Most small to medium APIs
  • Projects where you value ecosystem and tutorials
  • Rapid prototyping with clear, readable routing

The env/file-based server is a bridge toward:

  • Production-ready configuration practices
  • Working with external data sources (files, then databases)
  • Deploying to environments where you can’t hardcode ports or secrets

In 2024–2025, you’ll also see newer frameworks like Fastify and NestJS gaining attention. They often build on the same concepts you’ve seen here, just with more structure or performance tweaks. Once you’re comfortable with these 3 examples of setting up a Node.js server, jumping into those frameworks feels much less intimidating.


More examples include logging, security headers, and rate limiting

To round things out, it’s worth mentioning a few extra patterns you’ll see in the best examples of real-world Node servers. You can layer these on top of any of the 3 examples of setting up a Node.js server we just walked through.

Logging middleware – Using packages like morgan or custom middleware to log each request. This is handy for debugging and compliance.

Security headers – In Express, a library like helmet sets helpful HTTP headers to reduce common risks. While health and cybersecurity are different fields, you’ll notice that organizations such as the National Institute of Standards and Technology (NIST) publish guidance on security practices that align with ideas like defense in depth.

Rate limiting – For public APIs, you might use express-rate-limit to prevent abuse. This is especially relevant if you’re exposing endpoints to the open internet.

CORS configuration – If your frontend runs on a different origin, you’ll often add the cors package to control cross-origin access.

Each of these is another example of the same core idea: start with a simple Node server, then gradually add specific pieces as your needs grow.


FAQ: examples of common Node.js server questions

Q: Can you give an example of a very simple Node.js server for learning?
Yes. The first bare-bones http server in this article is a classic example of a minimal setup. It listens on a port, inspects req.url and req.method, and sends back plain text or JSON. Many beginner tutorials online use almost the same code as their first example of a Node.js server.

Q: Which of these 3 examples of setting up a Node.js server should I use for a real project?
For anything beyond a quick experiment, the Express JSON API example is usually the most practical starting point. It strikes a balance between simplicity and flexibility, and it matches a lot of real examples you’ll see in production codebases.

Q: Are there other examples of Node.js servers beyond these three?
Absolutely. Real examples include GraphQL servers built with Apollo, WebSocket servers for chat apps, and serverless handlers on platforms like AWS Lambda. The three examples here are intentionally small so you can understand the foundation before adding more moving parts.

Q: How do I keep my Node.js server code organized as it grows?
Start by splitting routes into separate files, then add folders for controllers, services, and models as needed. Many of the best examples of open-source Node projects on GitHub follow this pattern. Once your app gets larger, opinionated frameworks like NestJS can help give you a consistent structure.

Q: Where can I learn more about secure coding practices for APIs?
While not Node-specific, resources from organizations like NIST and university security courses (for example, those listed on Harvard.edu) provide guidance on authentication, authorization, and data protection that apply directly to building Node.js servers.


By walking through these 3 examples of setting up a Node.js server—from the bare-bones HTTP version to the Express API and finally the env/file-based setup—you’ve seen how to go from “hello world” to something that looks a lot closer to what developers ship in the real world. You can mix and match pieces from each example, and as you build more projects, you’ll start to recognize these patterns everywhere.

Explore More Building a Simple API with Node.js

Discover more examples and insights in this category.

View All Building a Simple API with Node.js