0% found this document useful (0 votes)
17 views10 pages

Readme

The document outlines exercises to build a REST API using Express and Swagger. It describes setting up the project and server, then adding endpoints to GET a list of doctors, GET a doctor by ID, and POST a new doctor. It also covers using Postman for testing and adding validation for doctor IDs.

Uploaded by

robinshrestha089
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
17 views10 pages

Readme

The document outlines exercises to build a REST API using Express and Swagger. It describes setting up the project and server, then adding endpoints to GET a list of doctors, GET a doctor by ID, and POST a new doctor. It also covers using Postman for testing and adding validation for doctor IDs.

Uploaded by

robinshrestha089
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 10

REST Exercises

Setup
1. Use starter pack provided for this course and unzip it.
2. Create your own project folder ( <student-id>-rest-api ) and copy over all the unzipped files
( README.md optional). Make sure the 2 'hidden' files like .editorconfig and .gitignore are also
copied over.
3. Download the following:
Postman to quickly send REST API requests and save them in a collection for testing.
JSONView Chrome Extension to better view your JSON responses in the browser.

Goal
In these exercises, we will learn how to create a REST API using Express. We will also generate documentation for our
API using Swagger.

There are a few approaches:

1. Write the API, create a swagger.json file that will be used by the Swagger UI.

2. Write the API, add documentation in the code to auto-generate the Swagger docs & UI.

We will be using the first approach, but a section below will also provide some detail on how the other approach can
be done.

Endpoints
At the end of this exercise, we should have the following endpoints:

GET /doctors
GET /doctors/:id
POST /doctors
GET /patients
GET /patients/:id
POST /doctors
GET /visits (with query parameters)

Exercises
REST Exercises
Setup
Goal
Endpoints
Exercises

Hello World Basic Setup


Adding middleware
Set up data as a module

Get a list of doctors


Get a doctor by id
Using Postman

Adding a doctor
Error Handling and Parameter Validation
Adding validation for the doctor id
Allowing specific properties for POST /doctors

Individual Exercise: Add similar endpoints for the patients


Get a list of visits by doctor id, patient id, or both
Swagger Documentation
Update the Swagger docs
Bonus Individual Exercises
Approach 2: Auto-generate Swagger UI based on documentation in code
Implement error handling for the GET /visits route
Code organization: separate routes
Refactoring your code

Hello World Basic Setup


Goal: Set up Express server with a Hello World example. Set up middleware and data.

First, let's get an Express server up and running. Express is a a minimal and flexible Node.js web application
framework that will be serving up our REST endpoints.

1. In your local folder set up from the previous section, run npm init -y (defaults) to set up our
package.json file.

2. Install the packages we need by running the following in your terminal:

npm install esm express cors body-parser

body-parser : Documentation. Parses the HTTP request body, making it easier to access
request.body in routes.

cors : Documentation. Cross-Origin Resource Sharing (CORS) is a mechanism that uses additional
HTTP headers to tell a browser to let a web application running at one origin (domain) have
permission to access selected resources from a server at a different origin. A web application
makes a cross-origin HTTP request when it requests a resource that has a different origin (domain,
protocol, and port) than its own origin. To avoid running into cors issues when trying to call your
REST service from the React UI we'll be creating later, ensure that your service allows cors.

These 2 middleware will essentially run before the request gets to our endpoints. We also need:

esm : Documentation. This package allows us to use ES6 syntax in our code for importing and
exporting modules.

express : Documentation. This is the framework we're using for our REST API.

3. In your folder (created from the Local Setup section), create a folder src , then within the folder create a
file called index.js . Add the following code (comments are added for explanation). This snippet was
based on Express documentation in Getting Started.

import express from "express"; // importing the module


const app = express(); // creating an Express app
const { PORT = 3000 } = process.env;

// set up route for '/', http://expressjs.com/en/5x/api.html#res.send


// this will show up on `localhost:3000` in the browser
app.get("/", (request, response) => response.send("Hello World!"));

// server will start listening for requests, the function is called immediately
once the server is ready. Console.logs show up in your terminal.
app.listen(PORT, () =>
console.log(`Hello World, I'm listening on port ${PORT}!`)
);

4. In package.json , add your start script

"start": "node -r esm src/index.js"

Alternatively, you can install nodemon to watch for changes and avoid having to stop and start the server
every time you make changes. Add a script to run in development that runs nodemon.

npm install nodemon

"dev": "nodemon -r esm src/index.js"

5. Run your app and navigate to localhost:3000 . You should see your first "Hello World" example.

Adding middleware
1. Our middleware (body-parser and cors) should run before the request goes to our endpoints. To set this up,
add the following before the app.get() . You can read more about using middlewares with app.use in
the documentation.

import bodyParser from "body-parser";

import cors from "cors";

app.use(bodyParser.json()).use(cors());

app.get("/", (request, response) => response.send("Hello World!"));

2. In the next exercises, you can try commenting out the app.use line to see how your routes are affected.

Set up data as a module


1. In data.js , assign the object to a variable called data. Export the variable.

const data = { ... }


export default data;

2. In src/index.js , import the data module at the top.

import data from "../data"; // the `./` denotes the folder structure

Get a list of doctors


Goal: Set up endpoint to retrieve a list of doctors through GET /doctors .
Let's set up an endpoint to retrieve a list of doctors GET /doctors . Here is the documentation for the methods
we're using:

Response.json()
App.get().

It's convention to prefix routes with /api/v1 to indicate the version.

In src/index.js , add the following after the first app.get line:

app.get("/api/v1/doctors", (req, res) => res.json(data.doctors));

Navigate to your browser and go to localhost:3000/api/v1/doctors to see a list of doctors.

Get a doctor by id
Goal: Set up endpoint to retrieve a list of doctors through GET /doctors/:id .

Let's set up an endpoint to retrieve a doctor by id GET /doctors/:id . We're using the same methods as above,
but now we can have access to the request parameters (id).

Request.params

1. Set up the route. Parameters are denoted with a : prefix. Let's try logging what's in req.params .

app.get("/api/v1/doctors/:id", (req, res) => {


console.log(req.params); // returns an object with { id: '1' }
});

2. Find the doctor from the data object and return it. We'll implement error handling later, follow the happy
path for now. Note that the params passed are of String type, while our data has Number ids.

app.get("/api/v1/doctors/:id", (req, res) => {


const id = parseInt(req.params.id);
const doctor = data.doctors.find((doc) => doc.id === id);
return res.json(doctor);
});

3. Go to your browser and test out the route for localhost:3000/api/v1/doctors/1 . Try another id.

Using Postman
Goal: Use Postman to test your endpoints.

So far, we've been going to the browser to test the endpoints. We can also use Postman (and we will need it for
POST requests in later sections).

1. Open up Postman. You should not need to sign in or create an account.

2. Create a New Collection, name it REST API Exercises.

3. Click on New -> New Request. Let's do the first GET /doctors request, so fill in the form with the request
name, a description, and make sure it saves to the correct collection.

4. Choose the GET request through the dropdown and fill in the URL. Click Send to send the request. You
should see the response JSON.
5. Create another request for the GET /doctors/id request using the same steps above.

Adding a doctor
Goal: Set up endpoint to add a doctor through POST /doctors .

Let's set up an endpoint to add a doctor to our list. We're using these methods:

App.post()
Request.body
Response.status()
HTTP Status Codes

1. In src/index.js , add the POST request. We are adding the doctor details into our data.doctors list
and assigning it an id.

app.post("/api/v1/doctors", (req, res) => {


const nextId = data.doctors.length + 1;
const doctor = { id: nextId, ...req.body };

data.doctors.push(doctor);
res.status(201).json(doctor); // 201 means Resource Created
});

2. In Postman, set up a new POST request for the endpoint. Click on the Body tag (this is where your doctor
information will be sent), click on the raw selection and click on the dropdown to select JSON. The
screenshot below shows how it should be set up. Add a JSON object in the textarea with a name property.

3. Send the request, then check your doctors list with a GET request again. You should see the newly added
doctor at the end of the list.

Error Handling and Parameter Validation


Goal: Add parameter validation for getting a doctor by ID and adding a doctor.

We implemented the happy paths for our routes, but what could go wrong?
In GET /doctors/:id, the id passed in the route could be letters
In GET /doctors/:id , the id doesn't exist in data
In POST /doctors, we don't check what details we're allowing.
When there is an error, what should the response send back?

Adding validation for the doctor id


Let's update the code to check if the id param sent in the request is a valid id. We're going to make the assumption
that ids are numbers.

1. In your route for app.get("/api/v1/doctors/:id") , add a function to check for an invalid id. We're
using the following methods:

Number.isNaN()

We're keeping the function inside the route for now to make the code-along easy to follow (everything in
one file), but for separation of responsibilities, you may want to extract this logic to a Validator object.

In src folder, create a subfolder utils , then create a file functions.js

export const isInvalidId = (id) => {


return Number.isNaN(parseInt(id, 10));
};

2. In src/index.js, use the validator function, to check if it's an invalid ID. If not, return an error. We're using the
following methods:

Response.status()
HTTP Status Codes

import * as utilities from "./utils/functions";


....
if (utilities.isInvalidId(req.params.id)) {
return res.status(400).json({ error: "Invalid id." });
}

3. We also want to check if the doctor with that id exists in the data. If not, return an error.

const id = parseInt(req.params.id, 10);


const doctor = data.doctors.find((doctor) => doctor.id === id);

if (!doctor) {
return res.status(404).json({ error: "Doctor not found." });
}

4. The route should now look like this:

app.get("/api/v1/doctors/:id", (req, res) => {


if (utilities.isInvalidId(req.params.id)) {
return res.status(400).json({ error: "Invalid id." });
}
const id = parseInt(req.params.id);
const doctor = data.doctors.find((doc) => doc.id === id);
if (!doctor) {
return res.status(404).json({ error: "Doctor not found." });
}

return res.json(doctor);
});

Allowing specific properties for POST /doctors


Currently, our POST route is adding everything passed through the request body. We want to guard against bad
data, as well as make sure the required information is there (such as the doctor's name).

1. Check for the existence of name in the body. If it doesn't exist, or it's empty, return an error.

if (!req.body.name) {
return res.status(400).json({ error: "Doctor needs a name parameter." });
}

2. Replace the spread operator ...req.body with the properties you're allowing.

const doctor = { id: nextId, name: req.body.name };

Individual Exercise: Add similar endpoints for the patients


Using what you learned from the first 3 exercises, set up 3 endpoints for the Patients resource:

GET /patients
GET /patients/:id
POST /patients

Set up Postman requests for all three.

Get a list of visits by doctor id, patient id, or both


Goal: Set up an endpoint to retrieve a list of visits, filtered by doctor id, patient id or both ids.

A visit is an object that contains the doctor id, patient id and a date. We can implement getting a list of visits based
on either doctor or patient id, or both.

We can do this using a query string in our route. If there is no query string, we can return the full list of visits.

We'll be using these methods:

Request.query

1. In src/index.js , set up the route. Let's try returning all of the visits in our data file.

app.get("/api/v1/visits", (req, res) => {


console.log(req.query);
return res.json(data.visits);
});

2. Test the route on Postman to see what is returned by req.query in your terminal. Your URL on Postman
should look something like localhost:3000/api/v1/visits?doctorid=1&patientid=1 . You can
edit the query parameters through the Postman UI (or toggle them to be included or not).

3. Implement the logic to find the correct visits.


app.get("/api/v1/visits", (req, res) => {
const { doctorid, patientid } = req.query;
let visits = data.visits;

if (doctorid) {
visits = visits.filter(
(visit) => visit.doctorid === parseInt(doctorid, 10)
);
}

if (patientid) {
visits = visits.filter(
(visit) => visit.patientid === parseInt(patientid, 10)
);
}

return res.json(visits);
});

Swagger Documentation
Goal: Set up Swagger documentation.

We provide a starter swagger.json file for you in the starter repo. Let's set it up to show our documentation.

1. Install the package for swagger-ui-express . This module allows you to serve auto-generated swagger-
ui generated API docs from express, based on a swagger.json file. The result is living documentation for
your API hosted from your API server via a route. Documentation.

npm install swagger-ui-express

2. In src/index.js , import the 2 modules you need.

import swaggerUi from "swagger-ui-express";


import swaggerDocument from "../swagger.json";

3. Add a route to show the documentation.

app.use("/api/v1/docs", swaggerUi.serve, swaggerUi.setup(swaggerDocument));

4. Navigate to that route to see an interactive Swagger documentation.

5. Read up on the Swagger Documentation structure in their documentation. Using that structure, add API
documentation for the patient routes.

Update the Swagger docs


1. Update your Swagger documentation to account for the /patients routes.

Bonus Individual Exercises


Approach 2: Auto-generate Swagger UI based on documentation in code
This approach uses the express-swagger-generator package.

You use comments in the code to write the documentation. For example:

/**
* @typedef Patient
* @property {string} id.required - Normally an auto increment number stored as string
* @property {string} name.required - Full name
*/

/**
* Get a single patient by ID
* @route GET /patients/{id}
* @group Patients
* @param {string} id.path.required - Patient ID
* @returns {Patient.model} 200 - Patient with requested ID
* @returns {string} 404 - Requested patient not found
* @produces application/json
*/

To set up swagger, add this to src/index.js

import expressSwaggerGenerator from "express-swagger-generator";


const host = `localhost:${port}`;
const basePath = "/"; // The forward slash is important!

// Options for the Swagger generator tool


const options = {
// The root document object for the API specification
// More info here: https://github.com/OAI/OpenAPI-
Specification/blob/master/versions/2.0.md#schema
swaggerDefinition: {
info: {
title: "Health Insurance API",
description: "This is Web service for patients, doctors and visits.",
version: "1.0.0",
},
host: host,
basePath: basePath,
produces: ["application/json"],
schemes: ["http", "https"],
},
basedir: __dirname, // Absolute path to the app
files: ["./routes/**/*.js"], // Relative path to the API routes folder to find the
documentation
};

// Initialize express-swagger-generator and inject it into the express app


expressSwaggerGenerator(app)(options);

Implement error handling for the GET /visits route


We did the happy path above. Take some time to implement error handling for when:
Doctorid param is not a number
Patientid param is not a number

Code organization: separate routes


Right now, we have endpoints for doctors, patients and visits all in one file in src/index.js . To organize our
code, we can split these up into 3 high-level routes (one for each).

Use the following to set up a routes folder and refactor your code to be more organized. Keep in mind that
refactoring means nothing should change behaviour-wise in your code.

Router
express.Router
A Guide to Routing - the last 2 sections on app.route() and express.Router are most relevant.

Refactoring your code


Extract the validations and logic within a route to separate class.

You might also like