RSC Module Separation
When using RSC, maintain strict module boundaries to prevent Node.js code from leaking into client bundles.
The Problem
Section titled “The Problem”// BAD: Static import pulls database into client bundleimport { db } from '@/api/database';
export async function createUser(input) { return db.user.create({ data: input });}The Solution
Section titled “The Solution”1. Type-Only Imports
Section titled “1. Type-Only Imports”'use server';
// GOOD: Type-only import is stripped at build timeimport type { User } from '@/api/schemas/user';
export async function getUser(id: string): Promise<User> { const { db } = await import('@/api/database'); return db.user.findUniqueOrThrow({ where: { id } });}2. Dynamic Imports
Section titled “2. Dynamic Imports”'use server';
export async function createUser(input: CreateUserInput) { // GOOD: Dynamic import at runtime const { db } = await import('@/api/database'); return db.user.create({ data: input });}3. Procedure Bridge
Section titled “3. Procedure Bridge”'use server';
export async function createUser(input: CreateUserInput) { const { executeProcedureDirectly } = await import('@veloxts/web/server'); const { userProcedures } = await import('@/api/procedures/users');
return executeProcedureDirectly(userProcedures.procedures.createUser, input);}Best Practices
Section titled “Best Practices”- Server actions: Use
'use server'directive - Database imports: Always dynamic
- Type imports: Use
import type - Procedure calls: Use
executeProcedureDirectlybridge
Next Steps
Section titled “Next Steps”- Server Actions - Action patterns
- tRPC Bridge - Procedure calls