Table of Contents
- Introduction
- Why Modular and Microservice Architectures?
- Monorepo Setup with Turborepo
- Structuring Multiple Apps and Shared Packages
- Decoupling Frontend and Backend Logic
- Using tRPC for Type-Safe APIs
- GraphQL as a Universal Interface
- Direct DB Access in Server Components
- Sharing Logic and UI Across Services
- Authentication and Authorization Strategies
- Deployment Strategies for Multi-App Systems
- Conclusion
1. Introduction
Modern web apps are rarely monoliths. As your codebase grows, modular architecture and microservices help you:
- Keep features isolated
- Enable independent deployments
- Reuse shared components
- Scale teams and systems more efficiently
With Next.js (App Router) and tools like Turborepo, tRPC, GraphQL, and Prisma, you can build a production-grade microservice-oriented fullstack app inside a monorepo.
2. Why Modular and Microservice Architectures?
Benefits include:
- Separation of Concerns: Different teams can work on different parts (auth, billing, UI).
- Better CI/CD: Smaller units = faster builds.
- Independent Scaling: Backend services can scale independently from frontend apps.
- Shared Logic and UI: Use shared packages for validation, types, UI components, etc.
3. Monorepo Setup with Turborepo
Use Turborepo to manage monorepos efficiently.
bashCopyEditnpx create-turbo@latest
Directory structure:
vbnetCopyEditapps/
web/ → Next.js frontend
admin/ → Another Next.js app
api/ → Express or custom backend
packages/
ui/ → Reusable UI components
config/ → Shared Tailwind config
utils/ → Shared helpers, zod schemas, types
turbo.json
defines the build pipeline per app/package.
4. Structuring Multiple Apps and Shared Packages
Apps: Each folder under apps/
is an independently deployable app.
Packages: Shared code lives here. This can include:
ui
(React components, buttons, inputs)types
(TypeScript interfaces, zod schemas)lib
(API clients, database helpers)
You can import like so:
tsCopyEditimport { Button } from '@myorg/ui';
import { userSchema } from '@myorg/types';
Configure tsconfig.json
paths and set up project references.
5. Decoupling Frontend and Backend Logic
You can decouple by:
- Creating a separate
api/
backend app using Express, NestJS, or even another Next.js app - Using an RPC layer like
tRPC
or GraphQL - Making backend functions callable via server components
6. Using tRPC for Type-Safe APIs
tRPC lets you create type-safe API layers with zero client/server code duplication.
Setup:
In packages/trpc
:
tsCopyEdit// trpc/router.ts
import { initTRPC } from '@trpc/server';
const t = initTRPC.create();
export const appRouter = t.router({
greeting: t.procedure.query(() => 'Hello from tRPC!'),
});
export type AppRouter = typeof appRouter;
In frontend (SSR/server components):
tsCopyEdit// web/app/api/trpc/[trpc].ts
import { appRouter } from '@myorg/trpc';
import { createNextApiHandler } from '@trpc/server/adapters/next';
export default createNextApiHandler({ router: appRouter });
Client-side usage:
tsCopyEditconst { data } = trpc.greeting.useQuery();
7. GraphQL as a Universal Interface
For more flexibility and API federation, you can opt for GraphQL:
- Use Apollo Server in your backend.
- Use urql or Apollo Client in your frontend.
- Design a modular schema with multiple resolvers for user, product, orders, etc.
Example:
tsCopyEditquery {
user(id: "1") {
name
email
}
}
GraphQL also supports batching, subscriptions, and granular access control.
8. Direct DB Access in Server Components
You can skip the API altogether in some cases and access the database directly in server components.
With Prisma:
tsCopyEdit// app/page.tsx (server component)
import { db } from '@myorg/db';
export default async function Home() {
const users = await db.user.findMany();
return <UserList users={users} />;
}
This is performant and avoids unnecessary API calls — especially useful for internal tools and admin panels.
9. Sharing Logic and UI Across Services
Use packages like:
@myorg/ui
: Button, Modal, Input@myorg/validators
: Shared zod schemas@myorg/db
: Prisma client and models@myorg/auth
: Shared auth logic (e.g., JWT verification)
Enable TypeScript project references so everything compiles properly.
10. Authentication and Authorization Strategies
Each app can share auth logic from @myorg/auth
, which includes:
- JWT signing/verification
- Role-based access checks
- Middleware or higher-order components for route protection
You can even move common logic to server-only functions in shared packages.
11. Deployment Strategies for Multi-App Systems
Options:
- Vercel: Best for deploying multiple Next.js apps.
- Railway / Render: Great for deploying backend services.
- Docker Compose or Kubernetes: For advanced infra or self-hosted setups.
- Turborepo Remote Caching: Speeds up builds across CI/CD pipelines.
Each app can be deployed independently with its own CI pipeline.
12. Conclusion
Building modular fullstack apps and microservices with Next.js is no longer complicated. With App Router, server components, tRPC, GraphQL, and Prisma, you can decouple layers while keeping DX high and performance tight.
A solid monorepo setup ensures scalable development across multiple teams, apps, and services — without duplicating code.