Decorative background gradient
Back to Blog
NestJS MicroservicesPrisma MonorepoNestJS PostgreSQL

NestJS & Prisma Monorepo Architecture for Scalable Microservices

Structure a NestJS microservices monorepo with a shared Prisma schema, clean module boundaries, and production-grade PostgreSQL connection pooling.

NestJS & Prisma Monorepo Architecture for Scalable Microservices

Microservices solve real scaling and team-ownership problems, but they also introduce a new failure mode: duplicated database logic, drifting schemas, and connection pool exhaustion. NestJS combined with Prisma gives you a structured way to avoid all three — if you set up the monorepo correctly from day one.

This guide covers the parts that actually matter in production: monorepo layout, module boundaries, transactional writes, and connection pooling.

Step 1: Structure the Monorepo

Use a single monorepo with a shared database package instead of copy-pasting a Prisma schema into every service.

text

packages/database owns the schema and generated Prisma client:

prisma
ts

Every service imports from @myorg/database and gets the exact same generated client — no version drift, no duplicated model definitions.

Step 2: Wrap Prisma in a NestJS Module

Don't instantiate PrismaClient directly inside services — wrap it in an injectable NestJS provider so it's testable and lifecycle-managed.

ts
ts

Marking it @Global() means you only import PrismaModule once in AppModule, and every feature module can inject PrismaService without re-importing it.

Step 3: Keep Domain Logic Inside Its Own Module

Each microservice should expose a narrow, intentional interface — not its entire Prisma client — to the rest of the system.

ts

$transaction guarantees that if orderItem.createMany fails, the order.create is rolled back too — you never end up with an order that has no line items.

Step 4: Communicate Between Services with NestJS Microservices Transport

Instead of services calling each other over raw HTTP, use NestJS's microservices transporters for typed, structured request-response and event patterns.

ts
ts
ts

This decouples orders-service from needing to know anything about how notifications work — it just emits an event and moves on.

Step 5: Get Connection Pooling Right

This is where most teams get burned in production. Every running instance of every service opens its own Prisma connection pool against Postgres. If you have 3 services × 5 instances × a default pool size of 9, that's 135 connections — easily exceeding Postgres' default max_connections of 100.

text

For serverless or high-instance-count deployments, put PgBouncer in front of Postgres in transaction pooling mode, and point Prisma at PgBouncer instead of Postgres directly. This lets you run far more application instances than your database's raw connection limit would otherwise allow.

Key Takeaways

A shared Prisma schema package eliminates model drift across services, wrapping PrismaClient in a NestJS provider makes it testable and lifecycle-safe, $transaction keeps multi-step writes atomic, and disciplined connection pool sizing (with PgBouncer if needed) is what actually keeps a multi-service NestJS architecture stable under real production load.

Frequently Asked Questions

Should each microservice have its own database or share one?

Prefer a database-per-service when services genuinely own independent data. When services are tightly coupled and query overlapping data, a shared PostgreSQL instance with a single Prisma schema (and schema namespacing) is simpler to operate and still lets you split deployment later once boundaries are proven.

How do I avoid Prisma client duplication across services in a monorepo?

Put your schema.prisma and generated client in a shared package (e.g. packages/database) that every service depends on via workspace references. Run prisma generate once at the package level so all services use the same generated client version.

What connection pool size should I use for Prisma in production?

Start with Prisma's default pool size formula (num_physical_cpus * 2 + 1) per service instance, then multiply by your number of running instances to check it against Postgres' max_connections. For serverless or many-instance deployments, put PgBouncer in front of Postgres instead of scaling max_connections directly.

How do NestJS microservices communicate with each other?

NestJS ships built-in microservice transporters (TCP, Redis, NATS, Kafka, gRPC) accessed through ClientProxy, which gives you request-response and event-based patterns without writing raw HTTP clients. For simple setups, Redis or TCP transport is enough; for high-throughput event pipelines, Kafka or NATS scales better.

Working on something similar? Take a look at my services and case studies, or book a free call to talk about your idea.

Related Articles

Caching Strategies and Cache Invalidation — The Complete Guide
CachingSystem DesignPerformance

Caching Strategies and Cache Invalidation — The Complete Guide

A practical guide to caching strategies (cache-aside, write-through, write-behind) and the cache invalidation techniques that keep them from serving stale data.

September 8, 2026Read more →
Database Indexing and Read Replicas — A Practical Guide
DatabasePostgreSQLSystem Design

Database Indexing and Read Replicas — A Practical Guide

How to choose the right database indexes, avoid the ones that quietly hurt write performance, and scale reads with replicas without introducing replication lag bugs.

September 8, 2026Read more →
Load Balancing and Stateless Service Design — A Practical Guide
System DesignLoad BalancingScalability

Load Balancing and Stateless Service Design — A Practical Guide

How load balancers distribute traffic, why stateless services are what actually makes horizontal scaling work, and how to fix the sticky-session traps that quietly reintroduce state.

September 8, 2026Read more →
Session Stores and Database Connection Pooling Explained
BackendRedisDatabase

Session Stores and Database Connection Pooling Explained

Why in-memory sessions break horizontally scaled apps, how to move session state to Redis correctly, and how connection pooling keeps your database from falling over under concurrent load.

September 8, 2026Read more →
Vertical vs Horizontal Scaling: How to Choose and Implement Each
System DesignScalabilityArchitecture

Vertical vs Horizontal Scaling: How to Choose and Implement Each

A practical comparison of vertical and horizontal scaling — what each actually fixes, where each breaks down, and the architecture changes horizontal scaling requires that most guides skip.

September 8, 2026Read more →
AI Agent Guardrails and Safety — Preventing Prompt Injection and Runaway Actions
Ai Agent SafetyPrompt Injection DefenseAgent Guardrails

AI Agent Guardrails and Safety — Preventing Prompt Injection and Runaway Actions

Build practical guardrails for AI agents — prompt injection defenses, destructive-action confirmation, iteration caps, and permission scoping.

September 7, 2026Read more →

Trending Topics