```javascript=
import express, { json, Request, Response } from 'express';
import cors from 'cors';
import morgan from 'morgan';
import { port, url } from './config.json';
import {
clear,
peopleAdd,
personView,
personEdit,
peopleList,
personRemove,
peopleStats,
} from './people';
const app = express();
// Use middleware that allows for access from other domains (needed for frontend to connect)
app.use(cors());
// Use middleware that allows us to access the JSON body of requests
app.use(json());
// Use middleware to log (print to terminal) incoming HTTP requests (OPTIONAL)
app.use(morgan('dev'));
app.get('/', (req: Request, res: Response) => {
res.json({ message: 'Welcome to the root URL of names ages!' });
});
// TODO TUTOR 0: Clear
app.get('/clear', (req: Request, res: Response) => {
res.json(clear());
});
// TODO GROUP 1: Add a person
// POST request - therefore data is in the request BODY
app.post('/people/add', (req: Request, res: Response) => {
const name = req.body.name as string;
const age = parseInt(req.body.age);
// Call peopleAdd - then check for errors
const resultFromCall = peopleAdd(name, age);
// If an error object is returned - send a 400 status code (or whatever the spec requires)
if ('error' in resultFromCall) { // checks for key 'error' in return obj
return res.status(400).json(resultFromCall);
}
// otherwise simply send response as is
res.json(resultFromCall);
});
// TODO GROUP 2: Read a person's details
// GET request - therefore data is in request query! (Option 1)
app.get('/people/view', (req: Request, res: Response) => {
const personId = parseInt(req.query.personId);
// Call personView - then check for errors
const resultFromCall = personView(personId);
// If an error object is returned - set a 400 status code (or whatever the spec requires)
if ('error' in resultFromCall) { // checks for key 'error' in return obj
return res.status(400).json(resultFromCall);
}
res.json(resultFromCall);
});
// GET request - can put id in path param (Option 2)
app.get('/people/view/:id', (req: Request, res: Response) => {
const personId = parseInt(req.params.id); // must make sure the params.<name> matches name in url (ie. /person/view/:<name>)
// Call personView - then check for errors
const resultFromCall = personView(personId);
// If an error object is returned - set a 400 status code (or whatever the spec requires)
if ('error' in resultFromCall) { // checks for key 'error' in return obj
return res.status(400).json(resultFromCall);
}
res.json(resultFromCall);
});
// TODO GROUP 3: List all people
// GET request - most likely will put the data inside query - path params are more for ids etc.
app.get('/people/list', (req: Request, res: Response) => {
const minAge = parseInt(req.query.minAge);
// Call peoepleList - then check for errors
const resultFromCall = peopleList(minAge);
// If an error object is returned - set a 400 status code (or whatever the spec requires)
if ('error' in resultFromCall) { // checks for key 'error' in return obj
return res.status(400).json(resultFromCall);
}
res.json(resultFromCall);
});
// TODO GROUP 4: Update a person
// PUT request - data goes in the body! Can also put in the id in the path so why not
app.put('/people/:id/update', (req: Request, res: Response) => {
const personId = parseInt(req.params.id);
const name = req.body.name as string;
const age = parseInt(req.body.age);
// Call personUpdate - then check for errors
const resultFromCall = personUpdate(personId, name, age);
// If an error object is returned - set a 400 status code (or whatever the spec requires)
if ('error' in resultFromCall) { // checks for key 'error' in return obj
return res.status(400).json(resultFromCall);
}
res.json(resultFromCall);
});
// TODO GROUP 5: Remove a person
// DELETE request - data goes in the query (or path but probably query in this case)
app.delete('/people/remove', (req: Request, res: Response) => {
const personId = parseInt(req.query.personId);
// Call personRemove - then check for errors
const resultFromCall = personRemove(minAge);
// If an error object is returned - set a 400 status code (or whatever the spec requires)
if ('error' in resultFromCall) { // checks for key 'error' in return obj
return res.status(400).json(resultFromCall);
}
res.json(resultFromCall);
});
app.listen(port, () => {
console.log(`Express Server started and awaiting requests at the URL: '${url}:${port}'`);
});
```
Implementations of functions in people.ts
```javascript=
import { Person, getData } from './dataStore';
const findPerson = (personId: number): Person | undefined => {
// NOTE: requires { "lib": ["ES2015"] } in tsconfig.json
return getData().people.find(person => person.personId === personId);
};
const findPersonByName = (name: string): Person | undefined => {
// NOTE: requires { "lib": ["ES2015"] } in tsconfig.json
return getData().people.find(person => person.name === name);
};
export function clear() {
getData().people = [];
return {};
}
export function peopleAdd(name: string, age: number) {
const data = getData();
if (!name || findPersonByName(name) || age <= 0) {
return { error: 'Name is empty, name already exists or age is not strictly positive.' };
}
const person = { name, age, personId: data.numPeopleCreated };
data.people.push(person);
data.numPeopleCreated += 1;
return { personId: person.personId };
}
export function peopleList(minAge: number) {
const data = getData();
if (minAge < 0) {
return { error: 'minAge must be non-negative' };
}
const people = data.people
.filter(p => p.age >= minAge)
.sort((p1, p2) => p2.age - p1.age || p1.name.localeCompare(p2.name));
return { people };
}
export function personView(personId: number) {
const person = findPerson(personId);
if (person === undefined) {
return { error: 'no such person with given id ' };
}
return { person };
}
export function personEdit(personId: number, name: string, age: number) {
const person = findPerson(personId);
if (person === undefined) {
return { error: 'name is empty or age is not strictly positive' };
}
if (!name || age <= 0 || (name !== person.name && findPersonByName(name))) {
return { error: 'name is empty, age is not strictly positive or name is already taken' };
}
person.name = name;
person.age = age;
return {};
}
export function personRemove(personId: number) {
if (!findPerson(personId)) {
return { error: 'person with given id does not exist' };
}
const data = getData();
data.people = getData().people.filter(person => person.personId !== personId);
return {};
}
export function peopleStats() {
const data = getData();
if (data.people.length === 0) {
return { error: 'no entries available in the data store' };
}
return {
stats: {
minAge: Math.min(...data.people.map(nameAge => nameAge.age)),
maxAge: Math.max(...data.people.map(nameAge => nameAge.age)),
averageAge: data.people.reduce((sum, nameAge) => sum + nameAge.age, 0) / data.people.length,
}
};
}
// CRUD