3.6 KiB
Movie Collection API
A simple Movie Collection REST API built with Node.js + Express, plus a single-page
frontend that talks to it with fetch().
A Movie is one entry in the collection; the collection is /api/movies. Movies are
kept in a plain JavaScript array, so there is no database — records added through the
API disappear when the server restarts.
Running
npm install
npm start # node server.js
Open http://localhost:3000.
npm run dev # node --watch server.js
PORT=4000 npm start
Schema
Each movie has four fields:
| Field | Type | Notes |
|---|---|---|
id |
integer ≥ 1 | assigned by the server, never sent by the client |
title |
string, 1–120 chars | required, trimmed |
genre |
string, 1–60 chars | required, trimmed |
year |
integer, 1888–2100 | required; "2010" and 2010 are both accepted |
The three request fields are required. Anything missing or blank comes back as a 400
with one message per field.
Endpoints
| Method | Path | Result |
|---|---|---|
GET |
/api/movies |
all movies → 200 |
GET |
/api/movies/:id |
one movie → 200, or 404 if there is no such id |
POST |
/api/movies |
add a movie → 201 with a Location header |
GET |
/ |
the frontend |
Errors come back as a message plus the offending fields:
{ "error": "Title is required.", "fields": { "title": "Title is required.", "year": "Year is required." } }
Examples
curl http://localhost:3000/api/movies
curl http://localhost:3000/api/movies/2
curl -X POST http://localhost:3000/api/movies \
-H 'content-type: application/json' \
-d '{"title":"Inception","genre":"science fiction","year":2010}'
GET /api/movies → 200
[
{ "id": 1, "title": "Interstellar", "genre": "Science Fiction", "year": 2014 },
{ "id": 2, "title": "Avengers: Endgame", "genre": "Action", "year": 2019 },
{ "id": 3, "title": "Coco", "genre": "Animation", "year": 2017 }
]
POST /api/movies → 201 · Location: /api/movies/4
{ "id": 4, "title": "Inception", "genre": "science fiction", "year": 2010 }
POST /api/movies with {} → 400
{
"error": "Title is required.",
"fields": {
"title": "Title is required.",
"genre": "Genre is required.",
"year": "Year is required."
}
}
GET /api/movies/abc → 400
{ "error": "ID must be a whole number, like 1." }
GET /api/movies/99 → 404
{ "error": "No movie with ID 99." }
Frontend
index.html is a single file (no build step, no framework) served by the same Express
app. It:
- displays the movie list in a table,
- has a form for adding a movie (title, genre, year),
- uses
fetch()for every call to the API, - refreshes the list after a movie is added.
Clicking a row fetches that one movie from GET /api/movies/:id and shows it in a detail
panel, so all three endpoints are exercised from the UI.
Files
server.js Express app: schemas, in-memory store, routes, error handling
index.html the frontend (styles + fetch logic, no dependencies)
package.json express + zod
Project structure notes
- Storage is
const movies = []inserver.js, with anextIdcounter for the auto-assignedid. It is seeded with three movies on startup. - Validation uses zod. One schema per shape (
movieDraftSchemafor the request body,movieSchemafor what is stored and returned), and a small helper turns a failure into the{ error, fields }response the frontend renders next to each input. node_modules/is git-ignored.