1. Source code (app.
js)
// [Link]
const express = require('express');
const app = express();
[Link]([Link]()); // parse JSON bodies
// Simple in-memory store
let items = [
{ id: 1, title: 'The Matrix', director: 'Lana Wachowski', year: 1999 },
{ id: 2, title: 'Inception', director: 'Christopher Nolan', year: 2010 }
];
// Next id generator (simple)
let nextId = [Link] ? [Link](...[Link](i => [Link])) + 1 : 1;
/*
GET /items
- Return array of all items
*/
[Link]('/items', (req, res) => {
[Link](200).json(items);
});
/*
POST /items
- Create a new item. Required fields: title, director.
- Optional: year (integer)
- Expects Content-Type: application/json
*/
[Link]('/items', (req, res) => {
const { title, director, year } = [Link];
// Basic validation
if (!title || typeof title !== 'string' || ![Link]()) {
return [Link](400).json({ error: "Field 'title' is required and must
be a string." });
}
if (!director || typeof director !== 'string' || ![Link]()) {
return [Link](400).json({ error: "Field 'director' is required and
must be a string." });
}
if (year !== undefined && ) {
// allow string numbers by parsing? Here we enforce integer
return [Link](400).json({ error: "Field 'year' must be an integer if
provided." });
}
const newItem = {
id: nextId++,
title: [Link](),
director: [Link]()
};
if (year !== undefined) [Link] = year;
[Link](newItem);
return [Link](201).json(newItem);
});
/*
PUT /items/:id
- Update an existing item by id
- Partial update allowed: accept any subset of fields (title, director,
year)
*/
[Link]('/items/:id', (req, res) => {
const id = parseInt([Link], 10);
if () {
return [Link](400).json({ error: 'Invalid id parameter.' });
}
const item = [Link](i => [Link] === id);
if (!item) return [Link](404).json({ error: 'Item not found.' });
const { title, director, year } = [Link];
if (title !== undefined) {
if (typeof title !== 'string' || ![Link]()) return
[Link](400).json({ error: "Field 'title' must be a non-empty string." });
[Link] = [Link]();
}
if (director !== undefined) {
if (typeof director !== 'string' || ![Link]()) return
[Link](400).json({ error: "Field 'director' must be a non-empty
string." });
[Link] = [Link]();
}
if (year !== undefined) {
if () return [Link](400).json({ error: "Field
'year' must be an integer." });
[Link] = year;
}
return [Link](200).json(item);
});
/*
DELETE /items/:id
- Delete item by id
*/
[Link]('/items/:id', (req, res) => {
const id = parseInt([Link], 10);
if () {
return [Link](400).json({ error: 'Invalid id parameter.' });
}
const idx = [Link](i => [Link] === id);
if (idx === -1) return [Link](404).json({ error: 'Item not found.' });
[Link](idx, 1);
return [Link](200).json({ message: 'Item deleted.' });
});
// Generic 404 for other routes
[Link]((req, res) => {
[Link](404).json({ error: 'Not found' });
});
// Error handler (simple)
[Link]((err, req, res, next) => {
[Link](err);
[Link](500).json({ error: 'Internal server error' });
});
// Start server
const PORT = [Link] || 3000;
[Link](PORT, () => {
[Link](`Server running on [Link]
});
2. Configuration files
[Link]
{
"name": "express-items-api",
"version": "1.0.0",
"description": "Simple RESTful API for items (Express)",
"main": "[Link]",
"scripts": {
"start": "node [Link]",
"dev": "nodemon [Link]"
},
"dependencies": {
"express": "^4.18.2"
},
"devDependencies": {
"nodemon": "^2.0.22"
},
"author": "",
"license": "MIT"
}
.gitignore
node_modules/
.env
3. Setup & run instructions
1. Node & npm: Ensure [Link] (v14+) and npm are installed.
2. Create project folder and place [Link], [Link], .gitignore.
3. Install dependencies:
4. npm install
5. npm install --save-dev nodemon # optional for dev
6. Run:
o Development (auto-restart):
o npm run dev
o Production:
o npm start
7. API will be reachable at [Link]
4. API Documentation (clear & detailed)
Base URL: [Link]
4.1 GET /items
Description: Retrieve all items.
Request: GET /items
Response: 200 OK
[
{ "id": 1, "title": "The Matrix", "director": "Lana Wachowski", "year": 1999
},
{ "id": 2, "title": "Inception", "director": "Christopher Nolan", "year":
2010 }
]
4.2 POST /items
Description: Add a new item.
Request: POST /items
o Headers: Content-Type: application/json
o Body (JSON):
o { "title": "Interstellar", "director": "Christopher Nolan",
"year": 2014 }
Validations:
o title (string) required
o director (string) required
o year (integer) optional
Responses:
o 201 Created with created object
o 400 Bad Request when missing/invalid fields
Example response (201):
{ "id": 3, "title": "Interstellar", "director": "Christopher Nolan", "year":
2014 }
4.3 PUT /items/{id}
Description: Update existing item (partial updates allowed).
Request: PUT /items/3
o Headers: Content-Type: application/json
o Body: any subset of { title, director, year }
Responses:
o 200 OK with updated object
o 400 Bad Request invalid payload
o 404 Not Found if id doesn't exist
Example request body
{ "title": "Interstellar (Extended)", "year": 2015 }
4.4 DELETE /items/{id}
Description: Delete item by id.
Request: DELETE /items/3
Responses:
o 200 OK { "message": "Item deleted." }
o 404 Not Found if id doesn't exist
5. Testing (Postman & curl examples)
Using curl
GET all
curl -i [Link]
POST create
curl -i -X POST [Link] \
-H "Content-Type: application/json" \
-d '{"title":"Interstellar","director":"Christopher Nolan","year":2014}'
PUT update
curl -i -X PUT [Link] \
-H "Content-Type: application/json" \
-d '{"title":"Interstellar (Remastered)"}'
DELETE
curl -i -X DELETE [Link]
Using Postman
1. Create a new Collection Items API.
2. Add requests for each endpoint:
o GET [Link]
o POST [Link] (Body → raw → JSON)
o PUT [Link]
o DELETE [Link]
3. For POST/PUT set header Content-Type: application/json.
4. Send and verify response codes & body match documentation.
Suggested tests to include:
Create item with missing title → expect 400.
Update non-existent id → expect 404.
Delete existing item → follow by GET to ensure removed.
6. Report (Implementation process, challenges, key
learnings)
Implementation process
1. Choice of tech: [Link] with Express for minimal boilerplate and wide familiarity.
2. Data store: Used in-memory items array to keep the assignment simple and easily
testable.
3. Routes: Implemented the required CRUD endpoints (GET, POST, PUT, DELETE).
4. Validation: Added basic server-side validation for required fields and types.
5. Error handling: Return appropriate HTTP status codes and JSON error messages.
6. Testing: Manual testing with curl and Postman to cover success and error cases.
Challenges faced
Validation choices: Deciding how strict to be (e.g., should year accept string digits?). I
enforced integer year to keep parsing predictable.
Persistence: In-memory storage is volatile; for persistence, a DB (SQLite/Postgres) is
required.
Edge cases: Ensuring update requests can be partial and still validated for types.
Key learnings
REST design requires clear mapping of HTTP verbs and status codes.
Input validation prevents malformed data entering the system and provides clearer
feedback to clients.
Even a basic API benefits from a concise documentation and consistent error responses.
7. Submission checklist (what to hand in)
[Link] — source code (required)
[Link] — dependencies and scripts (required)
[Link] — Setup/run/testing instructions (include content above in your README)
Report (can be the "Report" section above copied into a document)
API documentation (copy section 4 into docs)
Optional: Postman collection export JSON (recommended)