DEV Community

Poitier Stringer
Poitier Stringer

Posted on

Deploying a Saas Platform with Multi-Tenant Architecture Part 1: Running the Applications

I recently received permission to deploy a version of an assessment platform I architected and developed for my old company Prairie Health. The company has since been acquired and this platform is no longer in use. This was the first application of this complexity I had ever shipped to production and I learned a lot while building it. I'm taking this opportunity to document the process of re-familiarizing myself with this platform and deploying it for anybody who may be curious about this sort of thing.

It's been a while since I even looked at it so I'll start by just running the applications. The product development and UX was done by my product manager Jinwoo Yu. I had the help of one other engineer building the physician's dashboard, Aleks Petrovic.

You can find the code for it here:
https://github.com/pstringe/mindvitals

Overview

The platform consists of 3 applications:

  • An API written in typescript using nest.js
  • An interface for taking assessments
  • A dashboard for administering and reviewing assessments as well as managing PHI.

Assessments can be administered via, email, text, or QR code. Each clinic stores assessment data and PHI in a separate database. There is a central auth database that associates users with the correct clinic.

Starting the API

The api may be started using the command:

nest start --watch
Enter fullscreen mode Exit fullscreen mode

When we run that command, we see this output:
Cannot Connect to database

Currently we are unable to connect to the database. I'll have to set up a new one since the last one was set up on the company's mongoDB account.

Starting up the dashboard

We start the dashboard using the following commands:

yarn install
yarn start
Enter fullscreen mode Exit fullscreen mode

Here's the login screen which can be accessed at localhost:3000 when we start up the application. We won't be able to start that up until the API is working.

Login Screen

Creating a new mongoDB instance

This is just for demonstration purposes, so I'll start with the free cluster.
Select Cluster

I'm just going to use user name and password authentication
Image description

We need to change the credentials in our config.staging.env file
Image description

For now, I'll add my IP. When I deploy the API, I will need to to add the IP address associated with the APP Engine instance.
Image description

Next, I'll grab the connection string that would be used for mongoDB compass, and update it with the password we just created
Image description

Now I'm going to restart the API and see what that does
Image descriptionIt appears we've successfully connected to the new database.

Creating a new user and organization

Next, I want to try logging in. Currently, there should be no users in the auth database which should be accessible under test. I'm going to try to find the postman collection that I made for this API. I think there is an endpoint for adding a user.
Image description

Here is an example payload for adding a user. There is a property, organization which corresponds to the clinic of the user. The data for each clinic is stored in a different database and this key is used to associate users with the correct one. Its presence here means I should add an organization before adding a user. I'll call it Test Clinic.
Image description

Our test clinic has been created with the following ID
Image description

Now that we have a clinic, we can use this ID to create a user:
Image description

We receive the following error:
Image description

It looks like the default route in the users controller is a get endpoint:

import {
  Body,
  Controller,
  Delete,
  Get,
  Param,
  Patch,
  Post,
} from '@nestjs/common'

import { CreateUserDto } from './dto/create-user.dto'
import { User } from './user.model'
import { UsersService } from './users.service'

@Controller('users')
export class UsersController {
  constructor(private readonly userService: UsersService) {}

  @Get()
  async getUsers(): Promise<User[]> {
    const users = await this.userService.getUsers()
    return users
  }

  @Get(':id')
  async getUserById(@Param('id') id: string): Promise<User> {
    const user = await this.userService.getUserById(id)
    return user
  }

  @Post('add')
  async addUser(@Body() userDto: CreateUserDto): Promise<User> {
    console.log('addUser')
    const user = await this.userService.addUser(userDto)
    return user
  }

  @Patch(':id')
  updateUser(@Param('id') id: string, @Body() userDto: CreateUserDto): any {
    const user = this.userService.updateUser(id, userDto)
    return user
  }

  @Delete('/:id')
  deleteUser(@Param('id') id: string): any {
    return this.userService.deleteUser(id)
  }
}

Enter fullscreen mode Exit fullscreen mode

We need to update the endpoint in the postman collection and rerun.
Image description

Now we have a user entity and an associated personnel entity. The difference between user an personnel is, a user exists in the auth database and is only used for authentication. The personnel entity exists within the clinic's database and is used for all other operations.

Now that we have a user record, we can try logging in.
Image description

Now we have access to the dashboard. As you can see, this user does not have access to any patient records. In the next article we will address this. Once we have some patient data, then we will work on sending out assessments and reviewing results.

Top comments (0)