routes/index.js: used for defined all the endpoints:

import express from "express";
import * as notes from "./notes.js";

const router = express.Router();

router.get("/notes", notes.list);
router.post("/notes", notes.create); // curl -X POST http://localhost:3000/notes
router.get("/notes/:id", notes.read);
router.post("/notes/:id", notes.update); // curl -X POST http://localhost:3000/notes/123
router.delete("/notes/:id", notes.deleteNote); // curl -X DELETE http://localhost:3000/notes

export default router;

 

routes/notes.js: used for defined collections for CRUD:

import * as Note from "../model/Note.js";

// curl "http://localhost:3000/notes?sort=DESC"
export function list(req, res) {
  let { sort } = req.query;
  sort = sort ? sort.toLowerCase() : "desc";
  if (sort !== "asc" && sort !== "desc") {
    return res.status(400).send("invalid sort params");
  }
  const notes = Note.getNotes(sort);
  res.json({ notes });
}

// curl -X POST -d title="hello" -d body="world" http://localhost:3000/notes/
export async function create(req, res) {
  const { title, body } = req.body;
  if (title == undefined || body == undefined) {
    return res.status(400).send("Invalid request");
  }
  const note = Note.createNote({ title, body });
  res.status(201).send({
    message: "ok",
    data: note,
  });
}

export async function read(req, res) {
  const { id } = body.params;
  if (id == undefined) {
    return res.status(400).send("Invalid request");
  }
  const note = await Note.getNode(id);
  res.json(note);
}

// curl -X POST -d title="hello" -d body="world" http://localhost:3000/notes/123
export async function update(req, res) {
  const { id } = req.params;
  const { title, body } = req.body;
  if (title == undefined && body == undefined) {
    return res.status(400).send("Invalid request");
  }
  const note = Note.updateNote(id, { title, body });
  res.send({ message: "ok", data: note });
}

// curl -X DELETE http://localhost:3000/notes/123
export async function deleteNote(req, res) {
  const { id } = req.params;
  await Note.deleteNode(id);
  res.send("ok");
}

 

model/Note.js: Do CRUD:

import { v4 as uuid } from "uuid";
import MapStore from "../lib/mapstore.js";
const NOTES = new Map();
const store = new MapStore("notes.json");
/*
Note {
    id: String
    title: String
    body:  String
    lastEdited: Date
}*/

store
  .read()
  .then((notes) => {
    for (let [id, note] of notes) {
      NOTES.set(id, note);
    }
  })
  .catch((err) => {
    console.error(err);
  });

export function getNotes(sort) {
  const notes = Array.from(NOTES.values());
  if (sort === "asc") {
    return notes.sort((a, b) => {
      return a.lastEdited - b.lastEdited;
    });
  } else {
    return notes.sort((a, b) => {
      return b.lastEdited - a.lastEdited;
    });
  }
  return;
}

export async function createNote({ title, body }) {
  const id = uuid();
  const lastEdited = Date.now();
  const note = {
    id,
    title,
    body,
    lastEdited,
  };
  NOTES.set(id, note);
  await store.save(NOTES);
  return { ...note };
}

export async function updateNote(id, { title, body }) {
  if (!NOTES.has(id)) {
    return null;
  }
  const note = NOTES.get(id);
  note.title = title ?? note.title;
  note.body = body ?? note.body;
  note.lastEdited = Date.now();
  await store.save(NOTES);
  return { ...note };
}

export function getNode(id) {
  if (!NOTES.has(id)) {
    return null;
  }
  const note = NOTES.get(id);
  return { ...note };
}

export async function deleteNode(id) {
  const successed = NOTES.delete(id);
  await store.save(NOTES);
  return successed;
}

 

lib/mapstore.js: runtime memory:

import { readFile, writeFile } from "fs/promises";
import path from "path";
const dataDir = "data";
class MapStore {
  constructor(filename) {
    this.filepath = path.resolve(dataDir, filename);
  }
  async save(data) {
    console.log(`writing to ${this.filepath}`);
    const serializedData = JSON.stringify(Array.from(data.entries()));
    await writeFile(this.filepath, serializedData);
  }

  async read() {
    console.log(`readubg frin ${this.filepath}`);
    const data = await readFile(this.filepath, "utf-8");
    const parsed = JSON.parse(data);
    return new Map(parsed);
  }
}

export default MapStore;

 

index.js:

import express from "express";
// Logging
import morgan from "morgan";

import routes from "./routes/index.js";

const app = express();

app.use(
  morgan(":method :url :status :res[content-length] - :response-time ms")
);
// encode post body and available on req.body
app.use(express.urlencoded({ extended: true }));

// Routers
app.use(routes);

// Trigger a error for testing
app.get("/error", function (req, res) {
  throw new Error("oops I made a mistake");
});

// Error handler middleware
app.use(function (err, req, res, next) {
  console.error(err.stack);
  res.status(500).send(err.message);
});

// 404 catch
app.use(function (req, res) {
  res.status(404).send("not found");
});

app.listen(3000, function () {
  console.log("app is listening on port 3000");
});