Skip to main content

Lab-1-3

(2% of the course mark)

Express.js API MVC Lab

  • In this lab, you will build a RESTful API using Express.js following the MVC (Model-View-Controller) design pattern. You will learn how to organize your project into routes, controllers, services, and models to write clean, maintainable, and scalable code.

Lab objectives

  • Set up an Express.js project with TypeScript.

  • Structure a project using the MVC design pattern.

  • Create routes, controllers, services, and models.

  • Handle requests and responses using Express.js.

  • Apply separation of concerns to keep code clean and maintainable.

Create Node.js Express app

  1. Open VSCode and create a folder named Express-API-MVC.

  2. Open the terminal and change the directory to Express-API-MVC.

  3. Initialize the app by running the following command:

npm init -y
  1. Install typescript by running the following commands:
npm install typescript -D
npm install @types/node -D
  1. Initialize tsconfig.json by running the following command:
npx tsc --init
  1. Modify tsconfig.json and uncomment outDir.

  2. Modify tsconfig.json and add the following property:

"removeComments": true
  1. Install nodemon by running the following command:
npm install nodemon -D
  1. Install dotenv by running the following command:
npm install dotenv
  1. Create an .env file in the root of your project and enter the following.
PORT=3000
  1. Install express by running the following commands:
npm install express
npm install @types/express -D
  1. Configure package.json by adding a new entry to the scripts property.
"build": "tsc",
"start": "nodemon ./dist/index",
  1. Configure package.json by adding a new property.
"type": "module"
  1. Create a file named index.ts and add the following code:
// Developer:
// Purpose:

import * as dotenv from "dotenv";
dotenv.config();

import express, { type Request, type Response } from "express";

const PORT: number = process.env.PORT ? parseInt(process.env.PORT) : 3000;
const app = express();

app.all("/{*catchAll}", (req: Request, res: Response) => {
res.status(404).json({
message: `Invalid route (${req.url}) or HTTP method (${req.method}).`,
});
});

app.listen(PORT, () => {
console.log(`Server is running at http://localhost:${PORT}`);
process.on("SIGTERM", shutdown);
process.on("SIGINT", shutdown);
});

function shutdown() {
console.log(`Server is shutting down.`);
process.exit(0);
}
  1. Build the app by running the following command:
npm run build
  1. Start the app by running the following command:
npm run start
Express app status

Verify that your Express app starts without errors before proceeding to the next step.

Express app MVC

  1. In the root folder of the app, create a folder named lib.

  2. In the lib folder, create a file named utils.ts and add the following code:

// Developer:
// Purpose:

import { randomUUID } from "crypto";

export const generateId = () => randomUUID();
  1. In the root folder of the app, create a folder named models.

  2. In the models folder, create a file named userModel.ts and add the following code:

// Developer:
// Purpose:

import { generateId } from "../lib/utils.js";

export interface User {
id: string;
name: string;
}

const users: User[] = [];

export const UserModel = {
findAll: (): User[] => users,

findById: (id: string): User | undefined => users.find((u) => u.id === id),

create: (name: string): User => {
const newUser = { id: generateId(), name };
users.push(newUser);
return newUser;
},

update: (id: string, name: string): User | undefined => {
const user = users.find((u) => u.id === id);
if (user) user.name = name;
return user;
},

delete: (id: string): boolean => {
const index = users.findIndex((u) => u.id === id);
if (index === -1) return false;
users.splice(index, 1);
return true;
},
};
  1. In the root folder of the app, create a folder named services.

  2. In the services folder, create a file named userService.ts and add the following code:

// Developer:
// Purpose:

import { UserModel } from "../models/userModel.js";

export const UserService = {
getAll: () => UserModel.findAll(),
getById: (id: string) => UserModel.findById(id),
create: (name: string) => UserModel.create(name),
update: (id: string, name: string) => UserModel.update(id, name),
delete: (id: string) => UserModel.delete(id),
};
  1. In the root folder of the app, create a folder named controllers.

  2. In the controllers folder, create a file named userController.ts and add the following code:

// Developer:
// Purpose:

import { type Request, type Response } from "express";
import { UserService } from "../services/userService.js";

export const UserController = {
getAll: (req: Request, res: Response): void => {
res.json(UserService.getAll());
},

getById: (req: Request, res: Response): void => {
const user = UserService.getById(String(req.params.id));
user ? res.json(user) : res.status(404).json({ message: "User not found" });
},

create: (req: Request, res: Response): void => {
const { name } = req.body;
if (!name) {
res.status(400).json({ message: "Name is required" });
return;
}
res.status(201).json(UserService.create(name));
},

update: (req: Request, res: Response): void => {
const { name } = req.body;
const user = UserService.update(String(req.params.id), name);
user ? res.json(user) : res.status(404).json({ message: "User not found" });
},

delete: (req: Request, res: Response): void => {
const deleted = UserService.delete(String(req.params.id));
deleted
? res.status(204).send()
: res.status(404).json({ message: "User not found" });
},
};
  1. In the root folder of the app, create a folder named routes.

  2. In the routes folder, create a file named userRouter.ts and add the following code:

// Developer:
// Purpose:

import { Router } from "express";
import { UserController } from "../controllers/userController.js";

const router = Router();

router.get("/", UserController.getAll);
router.get("/:id", UserController.getById);
router.post("/", UserController.create);
router.put("/:id", UserController.update);
router.delete("/:id", UserController.delete);

export default router;
  1. Update index.ts and add the following code:
// Developer:
// Purpose:

import * as dotenv from "dotenv";
dotenv.config();

import express, { type Request, type Response } from "express";

const PORT: number = process.env.PORT ? parseInt(process.env.PORT) : 3000;

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

const app = express();

app.use(express.json());
app.use("/api/users", userRouter);

app.all("/{*catchAll}", (req: Request, res: Response) => {
res.status(404).json({
message: `Invalid route (${req.url}) or HTTP method (${req.method}).`,
});
});

app.listen(PORT, () => {
console.log(`Server is running at http://localhost:${PORT}`);
process.on("SIGTERM", shutdown);
process.on("SIGINT", shutdown);
});

function shutdown() {
console.log(`Server is shutting down.`);
process.exit(0);
}

Test create

  1. Open Postman, click File > New... > HTTP.

  2. Set the HTTP method to POST and the URL to http://localhost:3000/api/users.

  3. Select Body, then raw, and then JSON.

  4. Set the Body to:

{
"name": "Sam Altman"
}
  1. Click on Send. Take a screenshot and save it as post.png.

  2. Take note of the id, as it will be used in subsequent requests.

Test read

  1. Open Postman, click File > New... > HTTP.

  2. Set the HTTP method to GET and the URL to http://localhost:3000/api/users.

  3. Click on Send. Take a screenshot and save it as get-all.png.

  4. Set the HTTP method to GET and the URL to http://localhost:3000/api/users/id, where id is the id of the previously created item.

  5. Click on Send. Take a screenshot and save it as get-one.png.

Test update

  1. Open Postman, click File > New... > HTTP.

  2. Set the HTTP method to PUT and the URL to http://localhost:3000/api/users/id, where id is the id of the previously created item.

  3. Select Body, then raw, and then JSON.

  4. Set the Body to:

{
"name": "Steve Jobs"
}
  1. Click on Send. Take a screenshot and save it as put.png.

Test delete

  1. Open Postman, click File > New... > HTTP.

  2. Set the HTTP method to DELETE and the URL to http://localhost:3000/api/users/id, where id is the id of the previously created item.

  3. Click on Send. Take a screenshot and save it as delete.png.

Modify Express app MVC

  1. Modify Express-API-MVC app and create new route, controller, service, and model to support the following:
export interface Department {
id: string;
name: string;
description: string;
}

Submission

  1. Create a folder named submit.
Delete node_modules

Delete the node_modules folder on any app that you are submitting.

  1. Copy the Express-API-MVC folder and all the screenshots (post.png, get.png, put.png, and delete.png) to the submit folder.

  2. Create a zip file of the submit folder.

  3. Navigate back to where the lab was originally downloaded, there should be a Submissions section (see below) where the zip file can be uploaded.

submission