DEV Community

Cover image for How to Build a Hospital Record App with NextJs and Strapi
Shada for Strapi

Posted on • Originally published at strapi.io

How to Build a Hospital Record App with NextJs and Strapi

Introduction

Hospital records help to provide information on the background health conditions of prospective patients. There is a need to have an easily retrievable and accessible means of getting this record. We will solve this case study using the Strapi Content Management System to access and store patient records in an application.

Goal

In this article, we will build a Record application focusing on Patient Records. The app will focus on managing patient records in relations using the Strapi CMS and will provide all records to any patient when a request is made. These records will be fetched from an API and managed in Strapi.

Prerequisites

To follow up on this tutorial, prior knowledge of NextJs and APIs is required.

What is Strapi?

Strapi is an open-source, headless Content Management System that was developed using the Nodejs Javascript framework. Strapi provides users with features to enable them to store, manipulate, and manage content across their application workflows. It's simple and easy to use, and with its administrative panel, users can easily monitor and manage their content.
What's more? With Strapi, we can integrate APIs that can provide and manage content stored on the Strapi CMS.

Setting up a Strapi Project

To set up our Strapi back-end, we will first create a directory for our project using the following code in CLI:

       mkdir recordapp
Enter fullscreen mode Exit fullscreen mode

This creates a folder for our project recordapp. Next, we move into this folder:

    cd recordapp
Enter fullscreen mode Exit fullscreen mode

Then, we install Strapi:

    npx create-strapi-app@latest record-backend --quickstart
Enter fullscreen mode Exit fullscreen mode

The command above sets up Strapi for our app with all required dependencies and creates a new folder record-backend for this.

Strapi Installation

Once the installation is complete, it will start up the server which you can view in your browser via the specified link. In your browser, you will have a result similar to the image below:

Sign up Page

Here, simply fill in the required details and create a user account to access your dashboard.

Strapi Dashboard

Here we will set up the collection for our application.

Before we create our Strapi collection for our data, we have to understand how our hospital record app should operate. The application is to be able to facilitate identifying specific patient records on query. We will use MeiliSearch to make looking up of content in each record easier. With this, we can easily search for and get results from our Strapi collection showing content that have data related to the search keyword.

To set up our content, we will start by defining the content we wish to store on Strapi. To do this: first click on the Create your first Content Type button.

Creating a Collection type

On the new page that opens up, click on Create new collection type.

Defining the collection type

On the next page, we get different content types we can create for our collection.

Adding content to the Collection

We will create fields for the patients. These fields will include: name, surname, age, blood_type, ailment, medicine, last_visit, allergies, next_of_kin, next_of_kin_contact, gender, and address. All of these fields will be text fields except the age and last visit which would be of type number and date respectively.

Patient Collection Contents

Above are all the fields for the patient record. After creation, click on the save button at the top right.

Building the Records App

With our content setup on Strapi CMS complete, the next thing we need to do is to build the front-end of our application. To do this, we will be using the NextJs framework which is an open-source Javascript framework developed on NodeJs to render applications on server-side as opposed to client-side rendering by ReactJs.

To install NextJs, enter the following command in your terminal:

      npx create-next-app hospitalrecords
Enter fullscreen mode Exit fullscreen mode

We will be using Tailwind CSS to scaffold our application. This can be installed in CLI with the following command:

    npm install -D tailwindcss postcss autoprefixer
    npx tailwindcss init -p
Enter fullscreen mode Exit fullscreen mode

This creates a project folder hospitalrecords in your current directory. The created folder has the NextJs framework installed and we will use it to scaffold our application. The images below show what our finished app will look like.

Landing Section:

Preview of landing section of the app

Search Section:

Search section of the hospital App

Patient Info section:

Patient info display section

Here is the tree structure of our application:

...
 ┣ 📂pages
 ┃ ┣ 📂api
 ┃ ┃ ┗ 📜hello.js
 ┃ ┣ 📂Components
 ┃ ┃ ┣ 📜Cards.js
 ┃ ┃ ┣ 📜Data.js
 ┃ ┃ ┣ 📜Footer.js
 ┃ ┃ ┣ 📜Landing.js
 ┃ ┃ ┣ 📜Record.js
 ┃ ┃ ┗ 📜Topnav.js
 ┃ ┣ 📜index.js
 ┃ ┗ 📜_app.js
 ┣ 📂public
 ┃ ┣ 📜favicon.ico
 ┃ ┣ 📜hosrecords.png
 ┃ ┗ 📜vercel.svg
 ┣ 📂styles
 ┃ ┣ 📜globals.css
 ┃ ┗ 📜Home.module.css
 ┣ 📜.eslintrc.json
 ┣ 📜.gitignore
 ┣ 📜next.config.js
 ┣ 📜package-lock.json
 ┣ 📜package.json
 ┣ 📜postcss.config.js
 ┣ 📜README.md
 ┗ 📜tailwind.config.js
Enter fullscreen mode Exit fullscreen mode

Here, the index.js file contains all the components for the web page. In this file, we have:

    import styles from '../styles/Home.module.css'
    import Topnav from './Components/Topnav'
    import Landing from './Components/Landing'
    import Record from './Components/Record'
    import Cards from './Components/Cards'
    import Footer from './Components/Footer'
    export default function Home() {
      return (
        <div className="font-Roboto">
          <Topnav/>
          <Landing/>
          <Record/>
          <Cards/>
          <Footer/>
        </div>
      )
    }
Enter fullscreen mode Exit fullscreen mode

Taking the components in order, the Topnav component contains the website’s navigation bar and has the following code:

    import { React, useState } from "react";
    import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
    import { faBars } from "@fortawesome/free-solid-svg-icons";
    import { faTimes } from "@fortawesome/free-solid-svg-icons";
    function Topnav() {
      const [show, setShow] = useState(false);
      return (
        <header>
          <div
            className=" w-screen fixed flex items-center justify-center px-2 top-2 z-20"

          >
            <nav className=" container flex items-start py-4 mt-4 sm:mt-12 backdrop-blur-md relative">
              {/* <nav className=" z-20 fixed container flex items-start py-4 mt-4 sm:mt-12 backdrop-blur top-1 lg:left-20 lg:w-full md:left-0 sm:left-0"> */}
              <div className=" relative">
                <h1 className=" text-blue-600 font-bold text-2xl">
                  Hospital Records
                </h1>
              </div>
              <ul className="hidden sm:flex flex-1 justify-end items-center gap-12 text-slate-900 uppercase text-base font-medium md:py-1">
                <li className="cursor-pointer">
                  <a href="#Home">Home</a>
                </li>
                <li className=" cursor-pointer">
                  <a href="#Search">Search</a>
                </li>
                <li className=" cursor-pointer">
                  <a href="http://strapi.io" target="_blank" rel="noreferrer">About Strapi</a>
                </li>
              </ul>
              {show ? (
                <ul className="navigation sm:flex flex-1 md:hidden justify-end items-center gap-12 text-slate-900 uppercase text-xs md:py-1">
                  <li className="cursor-pointer">
                    <a href="#Home" onClick={() => setShow(!show)}>Home</a>
                  </li>
                  <li className=" cursor-pointer">
                    <a href="#Search" onClick={() => setShow(!show)}>Search</a>
                  </li>
                  <li className=" cursor-pointer">
                    <a href="http://strapi.io" target="_blank" rel="noreferrer" onClick={() => setShow(!show)}>About Strapi</a>
                  </li>
                </ul>
              ) : null}
              <div
                className="flex sm:hidden flex-1 justify-end z-40"
                onClick={() => setShow(!show)}
              >
                <FontAwesomeIcon
                  className=" text-2xl"
                  icon={show ? faTimes : faBars}
                />
              </div>
            </nav>
          </div>
        </header>
      );
    }
    export default Topnav;
Enter fullscreen mode Exit fullscreen mode

Next, Landing.js is the website’s landing section:

    import React from "react";
    import Image from "next/image";
    function Landing() {
      return (
        <div>
          <section className=" relative" id="Home">
            {/* hero section */}
            <div className=" container flex flex-col-reverse lg:flex-row items-center gap-12 mt-14 lg:mt-28">
              {/* content */}
              <div className="flex flex-1 flex-col items-center lg:items-start">
                <h2 className=" text-3xl text-blue-600 md:text-4xl lg:text-5xl text-center lg:text-left mb-6">
                  Hospital Patient Records available on Search
                </h2>
                <p className=" text-lg text-center lg:text-left mb-6 text-gray-600">
                  A simple app mimicking digitalized hospital records. Built on
                  NextJs and powered by Strapi.
                </p>
                <div className=" flex justify-center flex-wrap gap-6">
                  <button className=" btn border-2 hover:bg-white hover:text-blue-600 hover:border-blue-600 hover:border-2">
                    <a href="#Search">Try it out</a>
                  </button>
                </div>
              </div>
              {/* image */}
              <div className=" flex justify-center flex-1 mb-10 md:mb-16 lg:mb-0 z-10">
                <Image
                  src="/hosrecords.png"
                  height={500}
                  width={500}
                  className=" w-5/6 h-5/6 sm:h-3/4 md:w-full md:h-full"
                  alt="stethoscope and record"
                />
              </div>
            </div>
            {/* container attachment shape */}
            <div className="hidden md:flex items-center justify-center overflow-hidden absolute h-96 w-2/4 top-14 right-0 lg:-bottom-28 lg:right-36 lg:w-2/5">
              <svg
                viewBox="0 0 500 500"
                xmlns="http://www.w3.org/2000/svg"
                width="450px"
                id="blobSvg"
              >
                <defs>
                  <linearGradient id="gradient" x1="0%" y1="0%" x2="0%" y2="100%">
                    <stop offset="0%" stop-color="rgb(37, 99, 235)"></stop>
                    <stop offset="100%" stop-color="rgb(196, 224, 229)"></stop>
                  </linearGradient>
                </defs>
                <path fill="url(#gradient)">
                  <animate
                    attributeName="d"
                    dur="8000ms"
                    repeatCount="indefinite"
                    values="M439.5,306Q444,362,396.5,392Q349,422,299.5,459.5Q250,497,211.5,440.5Q173,384,111,375Q49,366,54,308Q59,250,67.5,200Q76,150,106.5,102Q137,54,193.5,49.5Q250,45,300.5,60Q351,75,381.5,115.5Q412,156,423.5,203Q435,250,439.5,306Z;
                    M442.5,289.5Q387,329,378,393Q369,457,309.5,429.5Q250,402,188.5,432.5Q127,463,118.5,397Q110,331,103.5,290.5Q97,250,78,195Q59,140,100,100Q141,60,195.5,52Q250,44,293,72.5Q336,101,359,137.5Q382,174,440,212Q498,250,442.5,289.5Z;
                    M435.5,310Q457,370,412.5,412Q368,454,309,459Q250,464,188,464.5Q126,465,83,418Q40,371,56.5,310.5Q73,250,91,209.5Q109,169,134,130.5Q159,92,204.5,67Q250,42,310.5,41.5Q371,41,379.5,105.5Q388,170,401,210Q414,250,435.5,310Z;
                    M456,301Q426,352,388.5,388.5Q351,425,300.5,414Q250,403,200.5,412Q151,421,116.5,384Q82,347,79.5,298.5Q77,250,61.5,191Q46,132,105,117Q164,102,207,84.5Q250,67,288.5,92Q327,117,378,132Q429,147,457.5,198.5Q486,250,456,301Z;
                    M413,296.5Q411,343,388.5,397Q366,451,308,427Q250,403,194.5,422.5Q139,442,86.5,408.5Q34,375,47,312.5Q60,250,76,204.5Q92,159,119.5,115.5Q147,72,198.5,63.5Q250,55,292.5,79Q335,103,372.5,130Q410,157,412.5,203.5Q415,250,413,296.5Z;
                    M439.5,306Q444,362,396.5,392Q349,422,299.5,459.5Q250,497,211.5,440.5Q173,384,111,375Q49,366,54,308Q59,250,67.5,200Q76,150,106.5,102Q137,54,193.5,49.5Q250,45,300.5,60Q351,75,381.5,115.5Q412,156,423.5,203Q435,250,439.5,306Z
                    "
                    fill="url(#gradient)"
                  ></animate>
                </path>
              </svg>
            </div>
          </section>
        </div>
      );
    }
    export default Landing;
Enter fullscreen mode Exit fullscreen mode

The Records component contains the search bar for patient records.

    import React from "react";

    function Record(props) {
      return (
        <div>
          <section className=" bg-gray-100 py-20 mt-20 lg:mt-60" id="Search">
            <div className="sm:w-3/4 lg:w-5/12 mx-auto">
              <h2 className=" text-3xl text-blue-600 text-center font-bold">
                Search for Records
              </h2>
              <p className=" text-gray-600 text-center mt-4">
                Enter a random name in the search box below and see what records pop
                up....
              </p>
              <div className=" flex flex-col sm:flex-row gap-6 mt-8">
                <input
                  type="text"
                  placeholder="Enter patient name"
                  className=" focus:outline-none flex-1 px-2 py-3 rounded-md text-black border-2 border-blue-600"
                ></input>
                <button className=" btn flex-1 border-2 hover:bg-white hover:text-blue-600 hover:border-blue-600 hover:border-2">
                  Search
                </button>
                {/* grid collection */}
              </div>
            </div>
          </section>
        </div>
      );
    }
    export default Record;
Enter fullscreen mode Exit fullscreen mode

Card.js is where the fetched data from Strapi will be displayed to the user:

    import {React, useState} from 'react';
    import Data from './data';
    function Cards() {
      const [data, moreData] = useState(false)
      const pull_data =(dat)=>{
        moreData(dat)
        console.log(dat)
      }
      return <div className=' relative'>
      <div className=" container grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-16 max-w-screen-lg mt-8 py-12">
          <div className=' flex flex-col rounded-md shadow-2xl'>
              <div className=' p-6 flex flex-col items-center'>
                  <h1 className=' text-4xl font-black text-blue-600 uppercase'>M</h1>
                  <h3 className=' mt-6 mb-2 text-xl'>Samuel Harrison</h3>
                  <hr className=' w-2/5 border-b border-blue-600'></hr>
                  <div className=' flex p-6'>
                  <button className=" btn hover:bg-white hover:text-blue-600 border-2 hover:border-blue-600 hover:border-2">
                  More Info
                </button>
                  </div>
              </div>
          </div>
          <div className=' flex flex-col rounded-md shadow-2xl'>
              <div className=' p-6 flex flex-col items-center'>
                  <h1 className=' text-4xl font-black text-blue-600 uppercase'>F</h1>
                  <h3 className=' mt-6 mb-2 text-xl'>Anita Florence</h3>
                  <hr className=' w-2/5 border-b border-blue-600'></hr>
                  <div className=' flex p-6'>
                  <button className=" btn hover:bg-white hover:text-blue-600 border-2 hover:border-blue-600 hover:border-2">
                  More Info
                </button>
                  </div>
              </div>
          </div>
          <div className=' flex flex-col rounded-md shadow-2xl'>
              <div className=' p-6 flex flex-col items-center'>
                  <h1 className=' text-4xl font-black text-blue-600 uppercase'>M</h1>
                  <h3 className=' mt-6 mb-2 text-xl'>James Micheal</h3>
                  <hr className=' w-2/5 border-b border-blue-600'></hr>
                  <div className=' flex p-6'>
                  <button className=" btn hover:bg-white hover:text-blue-600 border-2 hover:border-blue-600 hover:border-2"
                  onClick={()=> moreData(true)}
                  >
                  More Info
                </button>
                  </div>
              </div>
          </div>
      </div>
      {/* More info on Patient Data */}
      <Data open= {data} func={pull_data}/>
      </div>
    }
    export default Cards;
Enter fullscreen mode Exit fullscreen mode

It contains a child component Data that completely displays a selected patient’s data. Data.js contains the following lines of code:

    import { React, useState } from "react";
    function Data(props) {
      // const [newData, setNewData] = useState(null);
      // props.func(newData)
      const handleclick = () => {
          props.func(false)
      };
      console.log(props.open);
      return (
        <div>
          {props.open ? (
            <div className=" absolute h-full flex justify-center items-center w-full top-0 backdrop-blur-md flex-col">
              <button
                className="btn hover:bg-white hover:text-blue-600 border-2 hover:border-blue-600 hover:border-2 mb-1"
                onClick={() => handleclick()}
              >
                Close
              </button>
              <div className=" relative py-3 px-6 bg-white font-Roboto font-medium shadow-2xl">
                <h1 className=" text-center text-3xl font-extrabold mb-5">M</h1>
                <h3 className=" mb-1">Name: John Doe</h3>
                <p className=" mb-1">Age: 25</p>
                <p className=" mb-1">Blood Type: O</p>
                <p className=" mb-1">Ailment: Cancer</p>
                <p className=" mb-1">Medicine: Aspirin</p>
                <p className=" mb-1">Last visit: 12/12/12</p>
                <p className=" mb-1">Allergies: None</p>
                <p className=" mb-1">Next of Kin: Isaac Doe</p>
                <p className=" mb-1">Next of Kin Contact: 0700000</p>
                <p className=" mb-1">1, Main Street, Dublin, Ireland</p>
              </div>
            </div>
          ) : null}
        </div>
      );
    }
    export default Data;
Enter fullscreen mode Exit fullscreen mode

Currently, the Card and Data components contain hard-coded values but these will be replaced as we proceed with building the application.

And finally, there’s the Footer.js Component with the following code:

    import React from 'react';
    function Footer() {
      return <footer className=' bg-blue-600 py-8 flex items-center justify-center mt-10'>
        <div>
            <h3 className=' text-white font-medium text-xl'>Made with StrapiCMS and NextJs</h3>
        </div>
      </footer>;
    }
    export default Footer;
Enter fullscreen mode Exit fullscreen mode

Next in the tree above are the style sheets. The global.module.css file has been slightly modified to make use of Tailwind styles.

    @tailwind base;
    @tailwind components;
    @tailwind utilities;
    @import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap');
    .btn{
      @apply shadow-md py-3 px-6 rounded-md transition duration-300 text-white bg-blue-600 outline-none 
    }
    .navigation{
      text-transform: uppercase;
      opacity: 0;
    }
      @media screen and (max-width: 640px){
        .navigation{
          @apply  absolute h-screen bg-white z-30 w-screen left-0 flex items-center justify-center flex-col gap-44 uppercase font-bold text-2xl top-0 transition-opacity ease-in opacity-100
        }
      }

    html,
    body {
      padding: 0;
      margin: 0;
      font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
        Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
        scroll-behavior: smooth;
    }
    a {
      color: inherit;
      text-decoration: none;
    }
    * {
      box-sizing: border-box;
    }

There is also a custom font defined here which was used in the application. Also, I’ve made changes to the properties of the `container`Tailwind class, and added the font imported above in the `tailwind.config.js` file:

    module.exports = {
      content: [
        "./pages/**/*.{js,ts,jsx,tsx}",
        "./components/**/*.{js,ts,jsx,tsx}",
      ],
      theme: {
        extend: {},
        fontFamily: {
          Roboto: ["Roboto, sans-serif"]
        },
        container: {
          center: true,
          padding: "1rem",
          screens: {
            lg: "1124px",
            xl: "1124px",
            "2xl": "1124px",
          },
        },
      },
      plugins: [],
    }
Enter fullscreen mode Exit fullscreen mode

You can run the application with the npm run dev command in CLI to get a result similar to the images above.

Building the Patients API

For the purpose of this tutorial, we will be building a simple API to handle our patient’s data. For this, we will be making use of Node.js, express.js and a JSON file.
First, we will create a new folder called api handler . This file will be where we will build and set up our API. Next we will create a JSON file containing our data. The file will be called patients.json and will contain the following:

    [
      {
        "name": "John",
        "surname": "Doe",
        "age": "25",
        "bloodtype": "O",
        "ailment": "Cancer",
        "medicine": "Aspirin",
        "last_visit": "2020-06-01",
        "allergies": "None",
        "next_of_kin": "Isaac Doe",
        "next_of_kin_contact": "07000000",
        "gender": "male",
        "address": "1, Main Street, Dublin, Ireland"
      },
      {
        "name": "Newton",
        "surname": "Dannet",
        "age": "46",
        "bloodtype": "AB",
        "ailment": "none",
        "medicine": "Aspirin",
        "last_visit": "2020-03-12",
        "allergies": "None",
        "next_of_kin": "Clover Dannet",
        "next_of_kin_contact": "071111111",
        "gender": "male",
        "address": "1, Main Street, Dublin, Ireland"
      },
      {
        "name": "Grace",
        "surname": "Dommy",
        "age": "64",
        "bloodtype": "A",
        "ailment": "Diabetes",
        "medicine": "Aspirin",
        "last_visit": "2020-01-01",
        "allergies": "None",
        "next_of_kin": "Anny Dommy",
        "next_of_kin_contact": "0722222",
        "gender": "female",
        "address": "1, Main Street, Dublin, Ireland"
      },
      {
        "name": "Henry",
        "surname": "Derry",
        "age": "19",
        "bloodtype": "AB",
        "ailment": "none",
        "medicine": "none",
        "last_visit": "2021-09-01",
        "allergies": "None",
        "next_of_kin": "Ben Derry",
        "next_of_kin_contact": "07333333",
        "gender": "male",
        "address": "1, Main Street, Dublin, Ireland"
      },
      {
        "name": "Fred",
        "surname": "Eddy",
        "age": "28",
        "bloodtype": "B",
        "ailment": "none",
        "medicine": "Aspirin",
        "last_visit": "2022-01-011",
        "allergies": "None",
        "next_of_kin": "Eddy Edd",
        "next_of_kin_contact": "07444444",
        "gender": "male",
        "address": "1, Main Street, Dublin, Ireland"
      },
      {
        "name": "Stella",
        "surname": "Morico",
        "age": "14",
        "bloodtype": "O",
        "ailment": "none",
        "medicine": "none",
        "last_visit": "2021-05-23",
        "allergies": "Peanuts",
        "next_of_kin": "Ella Morico",
        "next_of_kin_contact": "07555555",
        "gender": "female",
        "address": "1, Main Street, Dublin, Ireland"
      }
    ]
Enter fullscreen mode Exit fullscreen mode

Above is the structure of our sample patient data. Here, we have six patients in total with their corresponding records. For our API, we will require the Express.js framework. This can installed via CLI with the following bash code:

    npm install express --save
Enter fullscreen mode Exit fullscreen mode

The above command installs Express.js and adds it to the dependencies. With this installed, we can now set up the server. Create a new file called server.js in the working directory and add the following code to it:

    var express = require('express');
    var app = express();
    var fs = require('fs');

    app.use(function(req, res, next) {
        res.header("Access-Control-Allow-Origin", "*");
        res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
        res.header("Access-Control-Allow-Headers", "x-access-token, Origin, X-Requested-With, Content-Type, Accept");
        next();
      });

    app.get("/fetchpatients", function(req, res) {
        fs.readFile(__dirname + "/" + "patients.json", 'utf8', function(err, data) {
            console.log(data);
            res.end(data);
        })
    })

    var server = app.listen(8081, function() {
        var host = server.address().address;
        var port = server.address().port;
        console.log("Listening at http://%s:%s", host, port);
    });
Enter fullscreen mode Exit fullscreen mode

The above code sets up our server. It makes use of the file system module to access the JSON file we earlier created for the patient data. The server is set to listen on port 8081. We can run this with the following command:

    node server.js
Enter fullscreen mode Exit fullscreen mode

You will get a message saying: Listening at http://:::8081. You can navigate to the port 8081 in your browser via http://localhost:8081/fetchpatients to view the JSON response.

API Response

With this, our API is complete and we can now link it to Strapi to manage the relationship between the data.

Integrating the App with Strapi

With the API setup complete, we will proceed by first adding data from the API to our Strapi collection. Then, we will integrate milisearch into the application to fetch data and add search functionality.

First, we will install and make use of Axios to perform our fetch and post requests. we can install this with the following command in CLI

    npm install axios
Enter fullscreen mode Exit fullscreen mode

After installation we can use this to fetch our JSON response from our API. Back in the index.js file, add the following code:

    ...
    import axios from 'axios';
    import { useEffect } from 'react'

    export default function Home() {
      const url = "http://localhost:8081/fetchpatients"
      useEffect(() => {
        const fetchData = async () => {
          const result = await axios.get(url);
          console.log(result)
        };
        fetchData();
      }, []);
    ...
Enter fullscreen mode Exit fullscreen mode

The code above uses axios to fetch the data from our API running on port 8081. It then runs an asynchronous function that awaits the fetch request and logs it in the browser console.

Adding Data to Strapi
To allow access to the Strapi collection we have to make changes to the user roles. To do this, navigate in the dashboard to the settings pane on the left navigation menu. Select Roles under Users and Permissions. Click on Public, select the patient-name collection and check all checkboxes.

Setting up Permissions for the Collection

Finally, click on the Save button at the top-right to save these changes.

Back within our index.js file, we will make modifications to send our API data to our Strapi Collection.

    ...
    import { useEffect, useState } from "react";
    ...
    export default function Home() {
    ...
    const url2 = "http://localhost:1337/api/patient-names";
    var response;
    const [sent,dataSent] = useState(false);
     useEffect(() => {
        const fetchData = async () => {
          const result = await axios.get(url);
          response = result.data;
          console.log(response);
          sendData();
        };
        fetchData();
      }, []);

    const sendData = async () => {
        if (sent==false) {
          try {
            response.forEach((response) => {
              // console.log(response.name);
              axios.post(url2, {
                data: {
                  name: response.name,
                  surname: response.surname,
                  age: response.age,
                  blood_type: response.bloodtype,
                  ailment: response.ailment,
                  medicine: response.medicine,
                  last_visit: response.last_visit,
                  allergies: response.allergies,
                  next_of_kin: response.next_of_kin,
                  next_of_kin_contact: response.next_of_kin_contact,
                  gender: response.gender,
                  address: response.address,
                },
              });
            });
          } catch (error) {
            console.log(error);
          }
          dataSent(true)
        } else {
          console.log("data already uploadedd")
        }

      };
Enter fullscreen mode Exit fullscreen mode

The code above stores our API response in a variable called response. A function sendData() is then called. This function maps through the response from the API and for each response, it sends the corresponding data to Strapi. At the end, if we view our Strapi Content-manager we will see six entries in our Patient name collection.

New entries added to our Collection

We can view the content of each entry by clicking on them. We get a window displaying each of the fields and their values.

Opening an individual Patient Data in the Collection

Integrating Meilisearch
To use Meilisearch locally, we will download and run an instance of it. This can be downloaded from Meilisearch Local. Opening the downloaded application shows a terminal with the Meilisearch instance hosted on local host:

Opening a Meilisearch instance

If we navigate in the browser to the specified URL, we can see the Meilisearch interface:

Viewing the instance of Meilisearch in the browser

To make use of Meilisearch from within our application, we would need to install it via CLI with the following command:

    npm install meilisearch
Enter fullscreen mode Exit fullscreen mode

Back in the records.js file, we will add an import for the meilisearch module.

    import MeiliSearch from "meilisearch";
Enter fullscreen mode Exit fullscreen mode

Then we create a client and set the host to the Meilisearch instance URL

    const [initialValue, setInitialValue] = useState("");
      const [query, setQuery] = useState([])
      const client = new MeiliSearch({
        host: "http://127.0.0.1:7700/",
        apiKey: "masterKey",
      });
      const index = client.index("patient");
      const search = async () => {
        const documents = await axios.get(
          "http://localhost:1337/api/patient-names"
        );
        var result = documents.data.data;
        let response = await index.addDocuments(result);
        console.log(response);
      };
      const handlesearch = async () => {
        const search = await index.search(initialValue);
        console.log(search);
        console.log(search.hits);
      };
      search();
Enter fullscreen mode Exit fullscreen mode

The code above instantiates a client for our data. An index is created using this client and data is fetched from the Strapi collection and stored in it. The results are added to the index and displayed in the console. The index.search method is used to find strings in the data and it’s what we will implement in our search functionality. In this case, it is searching for the string “Stella”. Note that if the index.search method takes an empty string "" then all data will be displayed as results.

Next, we will need to pass the results of our search query to the Cards components to display only the patients that match the search query. In the handlesearch function, we add:

    ...
    props.func(search.hits)
Enter fullscreen mode Exit fullscreen mode

Then, we will get this props in the parent component Index.js with the following code:

    const [getQuery, setQuery] = useState([])
      const pull_data =(dat)=>{
        setQuery(dat)
      }
Enter fullscreen mode Exit fullscreen mode

While also passing the function as a props to the Records component.

    <Record func={pull_data}/>
Enter fullscreen mode Exit fullscreen mode

With this we get the results of the search query from the Records component. We can then pass these results to the Cards component and display them.

    <Cards results={getQuery}/>
Enter fullscreen mode Exit fullscreen mode

Next, we iterate over the number of results and display the corresponding data. To do this, add the following to the Cards.js file

    function Cards({results}) {
    ...
    return (
        <div className=" relative">
          <div className=" container grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-16 max-w-screen-lg mt-8 py-12">
            {results.map((item, index) => (
              <div className=" flex flex-col rounded-md shadow-2xl" key={index}>
                <div className=" p-6 flex flex-col items-center">
                  <h1 className=" text-4xl font-black text-blue-600 uppercase">
                    {item.attributes.gender == "male" ? "M" : "F"  }
                  </h1>
                  <h3 className=" mt-6 mb-2 text-xl">{item.attributes.name}</h3>
                  <hr className=" w-2/5 border-b border-blue-600"></hr>
                  <div className=" flex p-6">
                    <button className=" btn hover:bg-white hover:text-blue-600 border-2 hover:border-blue-600 hover:border-2" onClick={()=> moreData(true)}>
                      More Info
                    </button>
                  </div>
                </div>
              </div>
            ))}
    ...
Enter fullscreen mode Exit fullscreen mode

The above code displays the cards with the corresponding data, and the number of displayed cards changes based on the search input.

Patients Records from our Strapi Collection

If we were to search for a particular name, you only get that card displayed.

Using the search functionality to display specific data

With Meilisearch, we can search for any data pertaining to a patient. This could be age, next of kin or even a health condition.

Finally, we need to display the entire patient data when the user clicks on the more info button. We will need to pass the data from the clicked card to the Data.js component. We will first create a variable for this:

    const [fullData, getFullData] = useState([]);
Enter fullscreen mode Exit fullscreen mode

Then, use the More info button to change the value of this state by modifying its onClick event-listener as follows:

     onClick={() => {
      moreData(true)
      getFullData(item)
    }}
Enter fullscreen mode Exit fullscreen mode

We can now pass the fullData as props to the Data component.

    <Data open={data} func={pull_data} info={fullData}/>
Enter fullscreen mode Exit fullscreen mode

To display this data in the Data.js file, we will make the following modifications:

    ...
    <div className=" relative py-3 px-6 bg-white font-Roboto font-medium shadow-2xl">
                <h1 className=" text-center text-3xl font-extrabold mb-5">M</h1>
                <h3 className=" mb-1">Name: {props.info.attributes.name}</h3>
                <p className=" mb-1">Age: {props.info.attributes.age}</p>
                <p className=" mb-1">Blood Type: {props.info.attributes.blood_type}</p>
                <p className=" mb-1">Ailment: {props.info.attributes.ailment}</p>
                <p className=" mb-1">Medicine: {props.info.attributes.medicine}</p>
                <p className=" mb-1">Last visit: {props.info.attributes.last_visit}</p>
                <p className=" mb-1">Allergies: {props.info.attributes.allergies}</p>
                <p className=" mb-1">Next of Kin: {props.info.attributes.next_of_kin}</p>
                <p className=" mb-1">Next of Kin Contact: {props.info.attributes.next_of_kin_contact}</p>
                <p className=" mb-1">Address: {props.info.attributes.address}</p>
              </div>
    ...
Enter fullscreen mode Exit fullscreen mode

With this, we have the correct data displayed when we click on the More info button.

Conclusion

In this tutorial, we learned about the Strapi CMS and how we can use it to build a hospital records application. We also learned how to make use of a search functionality to manage our data.

Resources

The repository containing all the code used in this tutorial can be found here

Top comments (0)