Basic RESTful Service – Part I

One can use different programming languages, frameworks and libraries to develop a RESTful web server which exposes the same API. In this post I will implement a RESTful server using JavaScript, Express, and npm libraries.

Given that one needs to start with some level of requirements, we will use the following short and simple statement:

“Implement a very simple RESTful API for a storage server that supports store, query, retrieve and delete operations using DICOM data”.

Architecting, designing, implementing and testing a useful DICOM storage system represents a large undertaking which would require multiple developers and about a year. What we will do instead, is to get a flavor of what is needed, keeping in mind that it is very important to have a reasonable good understanding of the domain (in this case DICOM) before architecting and designing software for a project.

In general one needs a user / customer who is willing to help by providing comments and suggestions about how the software should interact with physicians and nurses in a healthcare facility. In our case I will wear several hats and will just implement a few routes, some of them using different approaches, in order to experiment and be able to determine what works best for our specific case. Please note that different services will have similarities and differences which should be reflected in the resulting API.

DICOM stands for Digital Imaging and Communications in Medicine (that was a mouthful). You can learn more about it here. DICOM can be seen as a protocol to be used when moving data on a network. It is used to exchange images and reports.

The life cycle of an image starts in a machine (called a modality) that is able to generate some type of image fora patient. The resolution, dimensions, depth per pixel, among others, will vary between modalities. An example of an image is an x-ray. There are modalities that capture several images in a sequence (e.g., CT or MRI scanner).

After the set of images is captured, they are verified by the technician and sent to a radiologist for reading. The radiologist dictates a report which is transcribed. The images and report then make their way to the referring physician. The referring physician is the one that after examining the patient, decided that he / she needed additional information to be able to diagnose the issue afflicting the patient.

Images need to be stored for different time periods depending on different regulations. Some images must be stored for a set of years, others until the patient reaches a specific age, while others should be stored for the life of the patient. The idea behind is to be able to compare images and be able to determine if changes should be looked at. A typical example of having a large set spanning several years, are images in mammograms.

DICOM uses a hierarchy for its data. The hierarchy is:

Patient

Study

Series

Image

A single patient may have one or more studies. A Study may have one or more series. A series may have one or more images. This looks simple but different healthcare facilities assign different IDs to the same patient. With time, a patient may change their name. Occasionally a name is entered using an abbreviation or a nickname. These are some simple examples of the issues that your software would have to contend with and attempt to resolve while making the software reliable and easy to use.

Our software would have to comply with FDA and HIPAA regulations. We will not get into compliance issues in this post.

Before we start experimenting with some JavaScript code, let’s go back to our requirements. It seems that we need to support CRUD operations at the patient, study, series and image levels.

When a study is captured, there are some guidelines regarding altering the individual images, adding or deleting them. So we will have to make sure things are not deleted but flagged for deletion.

Due to HIPAA regulations access to DICOM data must be restricted. This implies that some type of access will have to be associated with individual users.

We will start with a simple implementation, which we will be able to enhance in future posts. One of the goals will be to determine how well the system performs. Our final implementation (left for a future post) will use microservices. We would like to compare basic implementations using different programming languages (e.g., Java, JavaScript) and different frameworks.

It is always important to learn and experiment before committing to a specific approach. The more time and efforts put into a project, the harder it is to make changes.

There seems to be a false believe that using Agile and following the rituals will assure success. Take a look at products and services offered by companies in different markets and you will come to the conclusion that there has to be a better way to develop better and robust software. Microservices is promising to be the next and better approach. That said, as stated by Fred Brooks there is no silver bullet.

Before Agile was defined, I worked on and used a software development methodology which I called Cyclic Development Process (CDP). It seems to address very well the issues of change in the SDLC. I am currently reading Deep Learning by Ian Goodfellow, Yoshua Bengio and Aaron Courville. The book has nothing to do with software development methodologies or RESTful APIs. That said, chapter 11 Practical Methodology states:

“Many of the recommendations in this chapter are adapted from Ng (2015).

We recommend the following practical design process:

o Determine your goals – what the error metric to use, and your target value for this error metric. Those goals and error metrics should be driven by the problem that the application is intended to solve.

o Establish a working end-to-end pipeline as soon as possible, including the estimation of the appropriate performance metrics.

o Instrument the system well to determine bottlenecks in performance. Diagnose which components are performing worse than expected and whether poor performance is due to overfitting, underfitting, or a defect in the data or software.

o Repeatedly make incremental changes such as gathering new data, adjusting hyperparameters, or changing algorithms, based on specific findings from your instrumentation.”

Ng (2015):  Advice for applying machine learning.

Read the quote from the Machine Learning book and pause for a few minutes to think about general software development using the programming language, frameworks and libraries of choice…

The advice with some editing due to context, directly applies to software development in general. Also note it has little to do with Agile.

I have taken a course in Machine Learning by Andrew Ng. Like the way he presents the materials. He seems to follow the KISS (Keep It Simple, Stupid) principle. Watch one of his videos on YouTube or better yet, take a course in Machine Learning taught by professor Andrew Ng.

That said, most software development projects which seem to be using the Agile methodology, tend to have one and only one goal:  complete all the tasks in the current sprint at all cost. The issue is that due to the reduce amount of time and lack of understanding what is going on, the resulting code in general is full of issues and bugs. The perceived solution is to run CI/CD, use unit testings, and spend 40+ hours (to say the least) solving bugs and leaving the issues to be refactored on a future date which increases the technical debt. This is why most products and services need to be overhauled from the time they are introduced. Of course there are some exceptions (e.g., Amazon shopping, Google Gmail) and there are huge number failures (e.g., banking, booking trips, and paying bills).

My advice, think, plan, and document before diving into the implementation. Deliver software once a week. Revisit your assumptions periodically to make sure all is well.

OK, enough is enough. Will step down from my soap box and let’s go over my notes and code for this mini project.

All web browsers are not created equal. Chrome’s v8 engine seems to be one of the fastest in parsing JavaScript. Node.exe which is the machine that executes JavaScript applications is also implemented around the v8 engine.

Node is NOT a programming language. Node is not a framework. Node is a runtime environment for executing JavaScript code.

Node has non-blocking and asynchronous calls. A non-blocking call executes and returns results back to the caller before the caller is able to proceed. Asynchronous calls make use of a callback mechanism which specifies a function / method to be executed when the operation completes. This way the caller is free to continue with other tasks and does not have to wait for the completion of any single task for it to complete. Asynchronous tasks make us of an event queue.

Node is ideal for I/O-intensive apps. Do not use Node for CPU-intensive apps. A storage server is a typical example of an I/O intensive app. Image processing is an example of an intensive CPU task. That said, with the use of GPUs and TPUs and adequate libraries, it is possible to easily dispatch several operations in parallel and then rendezvous at the proper point in the code.

In this post I am using the latest version of Node.js at the time available.

C:\>node --version
v10.9.0

URL:  https://nodejs.org

download to:  c:\temp\node-v10.15.3-x64.msi

C:\>node --version
v10.15.3

I had installed in my Windows 10 machine node v10.9.0 so I used the information here provided to update to v10.15.3.

I am using Visual Studio Code from Microsoft as my IDE. It is free to download and use. I installed it using the information that follows:

Product:  Visual Studio Code

download to:  c:\temp\VSCodeUserSetup-x64-1.33.0.exe

Installed at:  C:\Users\John\AppData\Local\Programs\Microsoft VS Code

Version: 1.33.1 (user setup)
Commit: 51b0b28134d51361cf996d2f0a1c698247aeabd8
Date: 2019-04-11T08:27:14.102Z
Electron: 3.1.6
Chrome: 66.0.3359.181
Node.js: 10.2.0
V8: 6.6.346.32
OS: Windows_NT x64 10.0.17134

In node (shortcut for Node.js) one should avoid defining variables in the global scope. This is a general recommendation no matter which programming language you use. Use modules to separate code. Every file in your solution and set of libraries is considered a module. Variables are scoped per module and per function as we will see during the first implementation of our RESTful server. We should try to avoid synchronous calls specially when dealing with the file system and network. Their speed is several order slower that the speed of CPU and GPU units.

Let’s talk a little bit about Express.js.

Express is the back-end component of the MEAN stack together with the MongoDB database software and AngularJS front-end framework.

RESTful services == RESTful APIs

Operations / requests are initiated by a client over HTTP. The request reaches the server. The server decides if the request is valid and represents a resource. The server performs the associated operations and returns a response to the client. It is important to recognize that once the operation is completed, the server does not keep track of the state of the client.

There is a set of operations that we will be using / implementing in our RESTful service using HTTP methods. These are:

GET getting data

POST creating data

PUT updating data

DELETE to delete data

The name for our RESTful service will be express-demo. Perhaps I should have considered a better name. Given that I have been experimenting for a couple days and have collected several screen shots, I will leave it like it is for now.

Let’s create the project as illustrated by the following screen capture (recall we are using Windows not Linux):

C:\Users\John>mkdir express-demo

C:\Users\John>cd express-demo

C:\Users\John\express-demo>npm init --yes
Wrote to C:\Users\John\express-demo\package.json:

{
  "name": "express-demo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

We now need to install Express.js as illustrated by the following screen capture:

C:\Users\John\express-demo>npm install express
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN express-demo@1.0.0 No description
npm WARN express-demo@1.0.0 No repository field.

+ express@4.16.4
added 48 packages from 36 contributors and audited 121 packages in 9.337s
found 0 vulnerabilities

There is a common cycle we encounter in most software development tasks. We edit, compile and run to verify all is progressing according to plan or if we encounter and issue, to correct it. I tend to use TDD so I repeat the cycle using incremental steps. Any shortcut to reduce the time to compile-run is welcomed. There is a tool named nodemon that restarts node every time we make changes in the project folder.

Let’s install and run nodemon:

C:\Users\John\express-demo>npm install -g nodemon
C:\Users\John\AppData\Roaming\npm\nodemon -> C:\Users\John\AppData\Roaming\npm\node_modules\nodemon\bin\nodemon.js a global node_modules

> nodemon@1.18.11 postinstall C:\Users\John\AppData\Roaming\npm\node_modules\nodemon
> node bin/postinstall || exit 0

Love nodemon? You can now support the project via the open collective:
 > https://opencollective.com/nodemon/donate

npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.7 (node_modules\nodemon\node_modules\fsevents):  l
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.7: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})

+ nodemon@1.18.11
added 223 packages from 129 contributors in 38.359s

C:\Users\John\express-demo>nodemon index.js     <==== instead of node
[nodemon] 1.18.11
[nodemon] to restart at any time, enter `rs`
[nodemon] watching: *.*
[nodemon] starting `node index.js`
listening on port: 3000 ...                     <==== was

after a change in the code

[nodemon] restarting due to changes...
[nodemon] starting `node index.js`
server listening on port: 3000 ...              <==== changed to C:\Users\John\express-demo>echo %PORT%
4444                                            <==== port for server C:\Users\John\express-demo>nodemon index.js
[nodemon] 1.18.11
[nodemon] to restart at any time, enter `rs`
[nodemon] watching: *.*
[nodemon] starting `node index.js`
server listening on port:  4444 ...             <==== changed

As you can see, after installing the package we start our server contained in the index.js JavaScript source file. OK, for the next project I will make sure to use more descriptive names for files and folders.

I think we should take a look at some code:

// **** define the port to listen on ****
// const port = 4444;
const port = process.env.STORAGE_SERVER_PORT || 4444;

// **** listener info ****
app.listen(port, () => {
  console.log(`storage server listening on port:  ${port} ...`);
});

Let’s take a look at some query string parameters. The following screen capture from the command console shows the execution of the code:

e.g.,  localhost:4444/api/studies/2019/5?sortBy=name

C:\Users\John\express-demo>nodemon index.js
[nodemon] 1.18.11
[nodemon] to restart at any time, enter `rs`
[nodemon] watching: *.*
[nodemon] starting `node index.js`
storage server listening on port:  4444 ...
{}
year: 2019 month: 04 day: 13
{ soryBy: 'name' }
year: 2019 month: 04 day: 13

Following is the associated code for routes that respond to some queries (get):

/*
 * route and handler (check if alive)
 */
app.get("/", (req, res) => {
  res.send("storage server is up and running !!!");
});

/*
 * route and handler (list of all patients)
 */
app.get("/api/patients", (req, res) => {
  res.send(patients);
});

/*
 * route and handler (single patient by ID)
 */
app.get("/api/patients/id/:id", (req, res) => {
  // **** extract the patient ID ****
  const patientID = req.params.id;

  // ???? log patient ID ????
  console.log(`patientID: ${patientID}`);

  // **** find the data in the array (in MongoDB) ****
  const patient = patients.find(p => p.id === parseInt(patientID));

  // **** send the patient back to the caller ****
  if (!patient) return res.status("404").send(`patient ${patientID} NOT found`);

  // **** send response ****
  res.send(patient);
});

/*
 * route and handler (single patient by name)
 */
app.get("/api/patients/name/:name", (req, res) => {
  // **** get the patient name ****
  const patientName = req.params.name;

  // ???? display the patient name ????
  console.log(`patientName ==>${patientName}<==`); // **** find the data in the array (in MongoDB) **** const patient = patients.find(p => p.name === patientName);

  // **** send the patient record to the caller ****
  if (!patient)
    return res.status("404").send(`patient ==>${patientName}<== NOT found`); // **** send response **** res.send(patient); }); /* * route and handler (patient name) */ app.get("/api/patients/name/:fn/:ln", (req, res) => {
  // **** extract the name of the patient ****
  var patientFirstName = req.params.fn;
  var patientLastName = req.params.ln;

  // ???? ????
  console.log(`patient name: ${patientFirstName}, ${patientLastName}`);

  // **** find the record in the array (in MongoDB) ****
  const patient = patients.find(
    p => p.name === patientFirstName + " " + patientLastName
  );

  // **** send the patient record to the caller ****
  if (!patient)
    return res
      .status("404")
      .send(`patient ==>${patientFirstName} ${patientLastName}<== NOT found`); // **** **** res.send(patient); }); /* * route and handler (studies per date) */ app.get("/api/studies/date/:year/:month/:day", (req, res) => {
  var year = req.params.year;
  var month = req.params.month;
  var day = req.params.day;

  // ???? ????
  console.log(req.query);

  console.log(`year: ${year} month: ${month} day: ${day}`);

  // **** send response ****
  res.send(req.params);
});

As an example, if we issue a get query to display all patients, the flowing results are returned:

C:\Users\John\express-demo>nodemon index.js
[nodemon] 1.18.11
[nodemon] to restart at any time, enter `rs`
[nodemon] watching: *.*
[nodemon] starting `node index.js`
storage server listening on port:  4444 ...

request on Chrome:
http://localhost:4444/api/patients/

response on chrome:
[{"id":1,"name":"James Bond"},{"id":2,"name":"Money Penny"},{"id":3,"name":"John Canessa"},{"id":4,"name":"Jane Doe"},{"id":5,"name":"John Doe"}]

Where is the data coming from? As this time the code is hardcoded in an array in the server application as illustrated by:

// **** define some patients in an array of objects (will use MongoDB in the future) ****
const patients = [
  { id: 1, name: "James Bond" },
  { id: 2, name: "Money Penny" },
  { id: 3, name: "John Canessa" },
  { id: 4, name: "Jane Doe" },
  { id: 5, name: "John Doe" }
];

To issue a query we could use cURL. cURL is a command line interface (CLI) application used to transfer data using multiple protocols including HTTP. It is relatively easy to use and you can embed it in applications. It seems to me that it is easier to use in Linux than on Windows.

The following are some of the command arguments and modifiers to cURL that we will use to POST a new patient to our server:

// on a command console use curl with the following arguments:

-d, --data <data>           HTTP POST data
-H, --header <header/@file> Pass custom header(s) to server
-i, --include               Include protocol response headers in the output
-X, --request <command>     Specify request command to use

Now let’s see how we can use cURL to post data:

C:\Users\John>cd express-demo

C:\Users\John\express-demo>type patient.json
{
    "id": "0",
    "name": "Donald Duck"
}

C:\Users\John\express-demo>curl -i -X POST -H "Content-Type: application/json" -d "@patient.json" http://localhost:4444/api/patients
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 29
ETag: W/"1d-yJigowATU94JB686bEqiSwipRIg"
Date: Sun, 14 Apr 2019 14:21:18 GMT
Connection: keep-alive

{"id":6,"name":"Donald Duck"}
C:\Users\John\express-demo>type patient.json
{
    "id": "0",
    "name": "Donald Duck"
}

[nodemon] restarting due to changes...
[nodemon] starting `node index.js`
storage server listening on port:  4444 ...
patient.id: 6 patient.name ==>Donald Duck<==

We can now use Chrome to see the results. The URL and results follow:

http://localhost:4444/api/patients/

[{"id":1,"name":"James Bond"},{"id":2,"name":"Money Penny"},{"id":3,"name":"John Canessa"},{"id":4,"name":"Jane Doe"},{"id":5,"name":"John Doe"},{"id":6,"name":"Donald Duck"}]

Most browsers have a tool to help us with sending requests to an application. In Chrome it used to be Postman. Lately the plugin has been replaced with a standalone application. You can learn more about it and install it as I did following these steps:

chrome extension:  Postman

Google search:  chrome postman

link:  https://chrome.google.com/webstore/detail/postman/fhbjgbiflinjbdggehcddcbncdddomop?hl=en

click on top right side <Add to Chrome>

On pop-up click <Add app>

When Chrome displays apps click on the Postman icon

On the pop-up:
This Chrome app is being deprecated
Please switch to our free native apps that have more advanced features.
<Download native app>

On the Get Postman for Windows page click on the <Download> button.
Select the Windows 64-bit option

Download to:  c:\temp\Postman-win64-7.0.7-Setup.exe

Click on the executable.

When done installing, click on the <Log in> button.

Once you have Postman installed you can issue a GET request to see what is in our database and issue a POST to add new records. Please note that if you are using nodemon and you modify any file in the express-demo folder, the server will restart and you will lose the data you may have inserted. This is especially true if you use cURL.

It is very important to always validate what a client sends to a server. In order to simplify the task we will use the npm joi module as illustrated by the following:

Google search:  npm joi

https://www.npmjs.com/package/joi

C:\Users\John\express-demo>npm install joi
npm WARN express-demo@1.0.0 No description
npm WARN express-demo@1.0.0 No repository field.

+ joi@14.3.1
added 5 packages from 1 contributor and audited 127 packages in 3.4s
found 0 vulnerabilities

OR

C:\Users\John\express-demo>npm install joi@13.1.0   <==== requested varsion npm WARN deprecated joi@13.1.0: This version has been deprecated in accordance with the hapi support policy (hapi.im/support). Please upgrade to the latest version to get the best features, bug fixes, and security patches. If you are unable to upgrade at this time, paid support is available for olde r versions (hapi.im/commercial). npm WARN deprecated hoek@5.0.4: This version has been deprecated in accordance with the hapi support policy (hapi.im/support). Please upgrade to the latest version to get the best features, bug fixes, and security patches. If you are unable to upgrade at this time, paid support is available for olde r versions (hapi.im/commercial). npm WARN express-demo@1.0.0 No description es\.staging\joi-b31aff3d (237ms) npm WARN express-demo@1.0.0 No repository field. + joi@13.1.0 added 1 package, updated 2 packages and audited 127 packages in 2.167s found 0 vulnerabilities C:\Users\John\express-demo>type package.json
{
  "name": "express-demo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.16.4",
    "joi": "^13.1.0"                                <==== installed
  }
}

In this case I installed the latest version and then decided to use a previous one. This is just an example of how you can switch versions of modules using npm. In general when developing new code, you should try to use the latest versions.

Once you have installed joi, then we should write a function to validate a patient. The function follows:

/*
 * function to validate patient requests
 */
function validatePatient(patient) {
  // **** define validation schema ****
  const schema = {
    name: Joi.string()
      .min(6)
      .required()
  };

  // **** validate the patient ****
  const result = Joi.validate(patient, schema);

  // **** return validation result ****
  return result;
}

You can then attempt to post a new patient not following the validation rules in order to experience the value of using joi as illustrated by the following set of edited attempts:

{
                            <====
}

"name" is required


{
	"name": ""              <====
}

"name" is not allowed to be empty


{
	"name": "JCC"           <====
}

"name" length must be at least 6 characters long


{
	"name": "John Canessa"  <====
}

{
    "id": 6,
    "name": "John Canessa"
}


[
    {
        "id": 1,
        "name": "James Bond"
    },
    {
        "id": 2,
        "name": "Money Penny"
    },
    {
        "id": 3,
        "name": "John Canessa"
    },
    {
        "id": 4,
        "name": "Jane Doe"
    },
    {
        "id": 5,
        "name": "John Doe"
    }
]

Following is code to insert a new patient:

/*
 * route and handler (post new patient)
 */
app.post("/api/patients", (req, res) => {
  // **** (using object destructuring) ****
  const { error } = validatePatient(req.body);
  if (error) return res.status(400).send(error.details[0].message);

  // **** set the patient data ****
  const patient = {
    id: patients.length + 1, // MongoDB would generate an ID
    name: req.body.name // patient name provided by caller (enable parsing of JSON objects)
  };

  // **** insert patient record into array (MongoDB) ****
  patients.push(patient);

  // ???? inform the user what is going on ????
  console.log(`patient.id: ${patient.id} patient.name ==>${patient.name}<==`);

  // **** send the patient record to the caller (caller needs to know patient ID) ****
  res.send(patient);
});

As was mentioned mentioned before, there are times in which we have the need to update information for a patient. The PUT request should be used for such task. Following is the code we use in our server to update patient information:

/*
 * route and handler (update specified patient record)
 */
app.put("/api/patients/id/:id", (req, res) => {
  // **** extract the patient ID ****
  const patientID = req.params.id;

  // ???? log the patient ID ????
  console.log(`patientID: ${patientID}`);

  // **** look up the patient in the database ****
  const patient = patients.find(p => p.id === parseInt(patientID));

  // **** patient not found ****
  if (!patient)
    return res.status("404").send(`patientID: ${patientID} NOT found`);

  // **** ****
  // const result = validatePatient(req.body);
  const { error } = validatePatient(req.body); // result.error

  // **** check for validation error ****
  if (error) return res.status(400).send(error.details[0].message);

  // **** update the patient record ****
  patient.name = req.body.name;

  // **** return the updated patient record to client ****
  res.send(patient);
});

Our server should allow the deletion of patients. As we previously discussed, deletion should be in delayed mode. A delete request should make the data unavailable, but it should not delete the actual data until a later time. In this case, we will delete data from our array as soon as the DELETE request is validated as illustrated by the following:

C:\Users\John>cd express-demo

C:\Users\John\express-demo>code .

C:\Users\John\express-demo>nodemon index.js
[nodemon] 1.18.11
[nodemon] to restart at any time, enter `rs`
[nodemon] watching: *.*
[nodemon] starting `node index.js`
storage server listening on port:  4444 ...

List:
[
    {
        "id": 1,
        "name": "James Bond"
    },
    {
        "id": 2,
        "name": "Money Penny"
    },
    {
        "id": 3,
        "name": "John Canessa"
    },
    {
        "id": 4,
        "name": "Jane Doe"
    },
    {
        "id": 5,
        "name": "John Doe"
    }
]

Delete:
{
    "id": 1,
    "name": "James Bond"
}

List:
[
    {
        "id": 2,
        "name": "Money Penny"
    },
    {
        "id": 3,
        "name": "John Canessa"
    },
    {
        "id": 4,
        "name": "Jane Doe"
    },
    {
        "id": 5,
        "name": "John Doe"
    }
]

Delete:
{
    "id": 4,
    "name": "Jane Doe"
}

List:
[
    {
        "id": 2,
        "name": "Money Penny"
    },
    {
        "id": 3,
        "name": "John Canessa"
    },
    {
        "id": 5,
        "name": "John Doe"
    }
]

The code to process patient deletions follows:

/*
 * route and handle (delete patient by id)
 */
app.delete("/api/patients/id/:id", (req, res) => {
  // **** extract the patient ID ****
  const patientID = req.params.id;

  // ???? log the patient ID ????
  console.log(`patientID: ${patientID}`);

  // **** look up the patient in the database (array) ****
  const patient = patients.find(p => p.id === parseInt(patientID));

  // **** patient ID not found ****
  if (!patient) return res.status("404").send(`patient ${patientID} NOT found`);

  // ???? ????
  console.log(`patient.id: ${patient.id} patient.name: ${patient.name}`);

  // **** find the patient index in the database (array) ****
  const index = patients.indexOf(patient);

  // ???? ????
  console.log(`index: ${index}`);

  // **** delete the patient from the database (array) ****
  patients.splice(index, 1);

  // **** return the patient info ****
  res.send(patient);
});

The entire code for this RESTful server is available in my GitHub repository. Please note that the node_modules folder is missing. You need to install the express-demo app and the folder and its contents (369 files in 91 folders) will be created. I deleted the folder and used the following to create and populate the node_modules folder:

C:\Users\John\npm-demo>npm install
npm WARN npm-demo@1.0.0 No description
npm WARN npm-demo@1.0.0 No repository field.

added 25 packages from 19 contributors and audited 40 packages in 5.152s
found 0 vulnerabilities

I will continue updating this code in future posts. Updates will go into consecutive repositories. I am doing this in order to be able to make changes that would break the current version.

If you have comments or questions regarding this post or any other post in this blog, or if you would like me to help in any phase in the SDLC (Software Development Life Cycle) of a product or service, please not hesitate and leave me a note below. Requests for help will remain private.

Keep on reading and experimenting. It is the best way to learn!

John

Follow me on Twitter:  @john_canessa

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.