Skip to content

Full Stack Multi-Tenant Example App in NestJS using Prisma and PostgreSQL. Demonstrates Request Scoped, Durable Request Scoped, and AsyncLocalStorage based implementations

Notifications You must be signed in to change notification settings

moofoo/nestjs-prisma-postgres-tenancy

 
 

Repository files navigation

nestjs-prisma-postgres-tenancy

Branches

branch main - Request scoped providers

branch durable - Durable request scoped providers (scoped to tenant id)

branch async-hooks - Singleton providers using AsyncLocalStorage to manage session state per request

Initial Setup

(make sure ports 5432 and 80 are free and docker is running)

yarn setup

This script performs the following:

  • pull node, nginx, postgres and playwright images used by app
  • create tenancy_example_network network (needs to be external to run playwright tests)
  • create tenancy_example_db_data volume
  • create custom-node:latest image (see Dockerfile.node)
  • start database service (this creates schema and inserts test data, see db directory)
  • run 'yarn' command
  • build prismaclient and session-opts packages on host (see packages directory)
  • build frontend and backend images (see apps directory)
  • stop compose project (stops db service)

see setup.sh

Running

docker compose up -d

App should then be accessible at http://localhost.

Login form shows instructions for signing in as different tenants/users

For example, to log in as user 2 of tenant 3:

  • username: t3 user2
  • password: user

Admin login:

  • username: t6 admin
  • password: admin

Once logged in you will see data from the 'Patients' table, which will be filtered as per the Postgres RLS policy.

You can see Prisma Metrics json output at http://localhost/nest/stats

Tests

While compose project is running,

yarn test

see test.sh

This will run playwright with the following playwright.config.ts:

import {defineConfig} from "@playwright/test";

export default defineConfig({
  testDir: "./tests",
  fullyParallel: true,
  workers: 50,
  repeatEach: 50,
  reporter: "html",
  use: {
    trace: "on-first-retry",
    bypassCSP: true,
  },
});

The 50 value for repeatEach and worker means the test (there's only one) runs 50 times in parallel. The test simply authenticates with the backend using a randomly chosen tenant/user and checks the validity of the Patients json returned by GET http://localhost/nest/patients.

Docker Notes

Follow these steps when adding app dependencies:

1 - Add the dependencies

yarn workspace add APP_NAME DEPENDENCY (or yarn workspace add -D ... for dev deps)

for example,

yarn workspace backend add bcrypt

2 - Stop/Remove/Build the service where dependencies change

docker compose rm -s -f backend && docker compose build backend

3 - Restart the proxy service (explained below) and re-up

docker compose kill proxy && docker compose up -d

As unintuitive as the above may seem, removing the service before building the container reliably updates node_modules dependencies correctly while (apparently) not touching the build cache. In other words, this method is much much faster than running docker compose build --no-cache, while also dealing with the annoying dependency issues that normally necessitate the usage of --no-cache and other cache-busting flags.

Sort of like a faster and more reliable version of this sequence:

docker compose stop backend && docker compose up -d --build --force-recreate -V backend

Note that if you aren't setting static ip addresses for your services, restarting the proxy service will sometimes be necessary (if it was running while you removed/built/restarted the given service)

One could make a shell script like this, to simplify things:

#!/bin/bash
docker compose rm -s -f $1 && docker compose build $1 && docker compose kill proxy && docker compose up -d

Prisma Resources

Postgres Resources

NestJS Resources

NGINX

Please be aware that this is a "toy" app meant to demonstrate the given programming concepts/techniques. It does NOT implement security best-practices and isn't intended to be representative of production-ready code

About

Full Stack Multi-Tenant Example App in NestJS using Prisma and PostgreSQL. Demonstrates Request Scoped, Durable Request Scoped, and AsyncLocalStorage based implementations

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published