Node.js Project: Notes API

This Node.js project combines everything from the tutorial into one small but realistic app: a notes REST API built with Express that saves notes to a JSON file. It uses ES modules, fs/promises, path, middleware, environment variables and centralized error handling.

Project Setup

mkdir notes-api && cd notes-api
npm init -y
npm pkg set type=module
npm install express
echo PORT=4000 > .env
npm pkg set scripts.dev="node --env-file=.env --watch server.js"
added 64 packages in 3s

The Storage Module

// store.js
import { readFile, writeFile } from 'node:fs/promises';
import path from 'node:path';
import { randomUUID } from 'node:crypto';

const FILE = path.join(import.meta.dirname, 'notes.json');

async function load() {
  try {
    return JSON.parse(await readFile(FILE, 'utf8'));
  } catch (err) {
    if (err.code === 'ENOENT') return [];
    throw err;
  }
}
const save = (notes) => writeFile(FILE, JSON.stringify(notes, null, 2));

export async function list() { return load(); }

export async function create(text) {
  const notes = await load();
  const note = { id: randomUUID(), text, createdAt: new Date().toISOString() };
  notes.push(note);
  await save(notes);
  return note;
}

export async function remove(id) {
  const notes = await load();
  const rest = notes.filter((n) => n.id !== id);
  await save(rest);
  return rest.length !== notes.length;
}
(module exports list, create and remove)

The Server

// server.js
import express from 'express';
import * as store from './store.js';

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

app.get('/api/notes', async (req, res, next) => {
  try { res.json(await store.list()); } catch (e) { next(e); }
});

app.post('/api/notes', async (req, res, next) => {
  try {
    const text = req.body.text?.trim();
    if (!text) return res.status(400).json({ error: 'text is required' });
    res.status(201).json(await store.create(text));
  } catch (e) { next(e); }
});

app.delete('/api/notes/:id', async (req, res, next) => {
  try {
    const ok = await store.remove(req.params.id);
    ok ? res.status(204).end() : res.status(404).json({ error: 'Note not found' });
  } catch (e) { next(e); }
});

app.use((req, res) => res.status(404).json({ error: 'Not found' }));
app.use((err, req, res, next) => {
  console.error(err);
  res.status(500).json({ error: 'Server error' });
});

const port = Number(process.env.PORT ?? 3000);
app.listen(port, () => console.log(`Notes API on http://localhost:${port}`));
$ npm run dev
Notes API on http://localhost:4000

POST /api/notes {"text":"Learn streams"}
{"id":"5f0c...","text":"Learn streams","createdAt":"2026-10-31T10:00:00.000Z"}

How It Works

  • store.js hides the file storage, so you could swap it for a database later without touching routes.
  • The try/catch + next(e) pattern sends unexpected errors to one handler (works in Express 4 and 5).
  • randomUUID gives each note a unique id without any extra package.
  • The port comes from .env, loaded by --env-file.

Exercises

  • Add PUT /api/notes/:id to edit a note's text.
  • Support ?search=word to filter notes by text.
  • Add an API-key middleware that protects POST and DELETE.
  • Replace the JSON file with SQLite or MongoDB.
  • Build a React front end that lists, adds and deletes notes using fetch.

Frequently asked questions

What are good Node.js projects for beginners?

A notes or todo REST API, a URL shortener, a CLI that renames files, a weather API proxy, and a real-time chat with WebSockets are popular beginner projects.

Can I use a JSON file as a database in Node.js?

For learning and tiny tools, yes. It does not handle many simultaneous writes safely, so real apps should use a database such as SQLite, PostgreSQL or MongoDB.

How do I deploy a Node.js API?

Push your code to Git and deploy to a platform like Render, Railway or Fly.io, or a VPS running Node behind Nginx with pm2. Set environment variables in the host's settings.