This is a client-server web application for managing a list of patients.
The application provides CRUD operations for working with the patients' database. It allows you to:
- View a list of all patients
- Create new patient entries
- Edit patient information
- Delete patient records
- Validate patient data
- Search and filter patients in the table
- Backend: Clojure, Leiningen, Ring, next.jdbc, PostgreSQL
- Frontend: ClojureScript, Reagent, Re-Frame
- Deployment: Docker, Kubernetes, CircleCI, Git
- Docker
- Java 17
- Leiningen
- Node.js v19.6
- Kubernetes (e.g., Minikube)
- Install the required dependencies.
- Run the Docker Compose file from the
Docker
folder:docker-compose -f Docker/docker-compose-postgres.yml up
- Install the project dependencies:
lein deps
- Start the backend:
lein run
- Start the frontend:
lein run -m shadow.cljs.devtools.cli --npm watch re-frisk-embedded
The application reads its settings from environment variables. You can set them locally or in a container. Default values are provided in the Docker/.env
file.
The application runs on port 8000 by default: http://localhost:8000/
.
- Build the container locally using
Docker/Dockerfile
. - Run the project in a container from a local build:
Docker/docker-compose-build-local.yml
.
- Configure CircleCI to use
.circleci
. - After the build, the container is uploaded to DockerHub.
Run the project in a container with the DockerHub image: Docker/docker-compose-build-remote.yml
.
Install Minikube and run: kubectl apply -f kubernetes.yaml
.
To run tests, use lein test
. Integration tests are used, so a database connection is required.
- Patient data is stored in a format compatible with HL7 FHIR.
- Patient data is stored as a JSONB type field in the database.
This section provides an overview of the project structure, explaining the purpose of key files and folders.
├── Docker # Docker configuration files
│
├── resources # Application resources
│ ├── migrations # Database migration files
│ └── public # Static files for frontend
│ ├── css # CSS styles
│ └── js # JavaScript files
│
├── src # Source code directory
│ ├── clj # Backend Clojure source code
│ │ └── patients # Backend application modules
│ └── cljs # Frontend ClojureScript source code
│ └── patients # Frontend application modules
│ ├── components # Reusable components
│ │ └── table # Table component
│ └── pages # Application pages
│ └── patient # Patient page
│
├── test # Test code directory
│ ├── clj # Backend Clojure tests
│ └── cljs # Frontend ClojureScript tests
│
├── deployment.yaml # Kubernetes deployment configuration
├── package.json # Node.js dependencies
├── project.clj # Clojure project configuration
└── shadow-cljs.edn # ClojureScript build configuration
This section provides documentation for the API, which uses the RPC (Remote Procedure Call) approach and the EDN (Extensible Data Notation) format for data exchange. The documentation includes available methods, request and response formats, and expected behavior.
The API provides CRUD operations for managing patient data in the database. It allows users to create, read, update, and delete patient records. The API has a single endpoint: /rpc
and receives POST
requests only.
The request should be formatted as follows:
{:method <method-name> :params {<parameter-key> <parameter-value> ...}}
- : The name of the method to be called (as a keyword).
- : The parameter key (as a keyword).
- : The corresponding value for the parameter.
{:method :get-patient :params {:patient-identifier "a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6"}}
The response will be formatted as follows:
{:status <status-code> :data {<data-key> <data-value> ...}}
- : status code for the response (as a keyword).
- : The data key (as a keyword).
- : The corresponding value for the data key.
{:status :ok :data {:patient {:identifier "a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6" :name "John Doe" ...}}}
The following status codes are used in the API responses:
:ok
: The operation was successful.:error
: There was an error during the operation.
200 OK
: The request was successful, and the response contains the requested data (or error with extra data).
Errors can occur for various reasons, such as invalid data or server-side issues. When an error occurs, the response will include a description of the error. The error response will be formatted as follows:
{:status :error :data {:text <error-message>}}
- : A description of the error (as a string).
{:status :error :data {:text "Invalid patient identifier format."}}
In case of an error, the API will return an appropriate HTTP status code and error message to help diagnose the issue.
The following methods are available:
:status
:list-patients
:get-patient
:create-patient
:delete-patient
:update-patient
For each method, detailed descriptions of the parameters, expected request and response formats, and error handling are provided below.
Purpose: Check the backend status and ensure it's up and running. Parameters: None. Response format: A map containing a :message key with the value "Backend is up and running".
Purpose: Retrieve a list of all patients. Parameters: None. Response format: A map containing a :patients key, with the value being a vector of maps representing individual patients.
Purpose: Retrieve a single patient by their identifier. Parameters: :patient-identifier - UUID of the patient to retrieve. Response format: A map containing a :patient key with the value being a map representing the requested patient.
Purpose: Create a new patient with the provided data. Parameters: :patient-data - A map containing the patient data (name, age, etc.). Response format: A map containing a :patient-identifier key with the value being the UUID of the newly created patient.
Purpose: Delete a patient by their identifier. Parameters: :patient-identifier - UUID of the patient to delete. Response format: A map containing only the :status key with the value :ok.
Purpose: Update an existing patient's data. Parameters:
- :patient-identifier - UUID of the patient to update.
- :patient-data - A map containing the updated patient data (name, age, etc.). Response format: A map containing a :patients key with the value being a map representing the updated patient data.
Patient record has the following structure:
:patient/identifier
: UUID (optional) -- added automatically:patient/name
: A collection of maps with the following keys::name/use
: One of "usual", "official", "temp", "nickname", "anonymous", "old", "maiden":name/text
: Non-empty string:name/family
: Non-empty string:name/given
: Collection of non-empty strings (the first string is used as the patient's first name):name/prefix
: Collection of non-empty strings (optional):name/suffix
: Collection of non-empty strings (optional):name/period
: A map with the following keys (optional)::period/start
: A valid date in "yyyy-MM-dd" format (optional):period/end
: A valid date in "yyyy-MM-dd" format (optional)
:patient/address
: A collection of maps with the following keys::address/use
: One of "home", "work", "temp", "old", "billing":address/type
: One of "postal", "physical", "both":address/text
: Non-empty string:address/line
: Non-empty string:address/city
: Non-empty string:address/country
: Non-empty string:address/postal-code
: Non-empty string (optional):address/state
: Non-empty string (optional):address/district
: Non-empty string (optional):address/period
: A map with the following keys (optional)::period/start
: A valid date in "yyyy-MM-dd" format (optional):period/end
: A valid date in "yyyy-MM-dd" format (optional)
:patient/gender
: One of "male", "female", "other", "unknown":patient/birth-date
: A valid date in "yyyy-MM-dd" format:patient/insurance-number
: A 16-digit non-empty string
Please note that all keys, unless specified as optional, are required.
Developed by Sergey Sizov (mailto:[email protected]). For questions or suggestions, contact via email or GitHub repository.