Deploying a Saas Platform with Multi-Tenant Architecture Part 1: Running the Applications
Tags: writing
Published: 2023-02-12 20:22:33


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
```

When we run that command, we see this output:
![Cannot Connect to database](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qwe7em76ab2wr0mcpc2z.png)

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
```

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x9idv0o80p3h5830cmpy.png)


## Creating a new mongoDB instance
This is just for demonstration purposes, so I'll start with the free cluster.
![Select Cluster](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5yr9g8gbshrb9o33g9sk.png)

I'm just going to use user name and password authentication
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6a4p9ud5o7dmc1ulpi6a.png)

We need to change the credentials in our config.staging.env file
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0ob6ar7c729a1b8i5w98.png)

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lefvel286z7ys7xh7cf5.png)

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tl0h6579c3hvevy2y7mf.png)

Now I'm going to restart the API and see what that does
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2dnbvjqkn5fvsci7zuk4.png)It 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/asc3hcrbt2a0d2fm52si.png)

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/02676up573okk1cunk4z.png)

Our test clinic has been created with the following ID
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yxhwi6t766ddp40l3say.png)

Now that we have a clinic, we can use this ID to create a user:
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f1irq1wflib7rfk598ex.png)

We receive the following error:
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/caxtgdw93gkjqdcpub7y.png)

It looks like the default route in the users controller is a get endpoint:
```ts
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)
  }
}

```

We need to update the endpoint in the postman collection and rerun.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ixm6ojcecj4xioerjnby.png)

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z3nk2q14zlo3vo8tase0.png)

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.