Skip to content

Project Architecture

Generated: 2026-01-04 Version: 1.0

NexisChat is a multi-tenant WhatsApp management platform built as a monorepo with the following high-level architecture:

┌─────────────────────────────────────────────────────────────────────────────┐
│ Client Layer │
├─────────────────┬────────────────────┬─────────────────┬───────────────────┤
│ Client App │ Subscription App │ Landing Page │ Docs │
│ (TanStack) │ (Next.js) │ (Astro) │ (Fumadocs) │
│ :3000 │ :3001 │ :4321 │ :3002 │
└────────┬────────┴─────────┬──────────┴────────┬────────┴────────┬──────────┘
│ │ │ │
│ tRPC │ tRPC │ Static │ Static
│ │ │ │
▼ ▼ │ │
┌────────────────────────────────────┐ │ │
│ API Server │ │ │
│ (Hono + tRPC 11) │◄─────────┘ │
│ :8787 │ │
├────────────────────────────────────┤ │
│ • Account Management │ │
│ • Folders/Templates/Tags │ │
│ • Subscription Logic │ │
│ • Auth (Better Auth) │ │
└────────────┬───────────────────────┘ │
│ │
│ REST │
▼ │
┌────────────────────────────────────┐ │
│ WhatsApp Web Server │ │
│ (Elysia + Bun) │ │
│ :8788 │ │
├────────────────────────────────────┤ │
│ • WhatsApp Web.js Sessions │ │
│ • Messaging API │ │
│ • WAHA API Abstraction │ │
└────────────┬───────────────────────┘ │
│ │
│ │
▼ ▼
┌────────────────────────────────────┐ ┌────────────────────────────────┐
│ PostgreSQL │ │ CDN/Static │
│ (Drizzle ORM) │ │ (Vercel/Cloudflare) │
│ • Users, Subscriptions │ │ • Landing assets │
│ • Accounts, Folders │ │ • Documentation │
│ • Templates, Tags │ │ │
└────────────────────────────────────┘ └────────────────────────────────┘
NexisChat/
├── apps/
│ ├── client/ # Main chat client (TanStack Start)
│ ├── server/ # API server (Hono + tRPC)
│ ├── whatsapp-web-server/ # WhatsApp integration (Elysia)
│ ├── subscription/ # Subscription portal (Next.js)
│ ├── landing/ # Marketing site (Astro)
│ └── docs/ # Documentation (Fumadocs)
├── packages/
│ ├── ui/ # Shared component library
│ ├── i18n/ # Internationalization (Paraglide)
│ ├── eslint-config/ # Shared ESLint configs
│ └── typescript-config/ # Shared TypeScript configs
└── _bmad/ # AI agent configuration
apps/client/src/lib/trpc.ts
const trpc = createTRPCProxyClient<AppRouter>({
links: [
httpBatchLink({
url: `${import.meta.env.VITE_SERVER_URL}/trpc`,
headers: () => ({
Authorization: `Bearer ${getAccessToken()}`,
}),
}),
],
});
// Usage in component
const { data: accounts } = trpc.getAccounts.useQuery();
await trpc.account.addAccount.mutate({ name: 'Work', ... });
apps/server/src/services/whatsapp.ts
const wahaClient = new WahaApiClient({
baseUrl: process.env.WHATSAPP_SERVER_URL,
apiKey: process.env.WHATSAPP_API_KEY
})
// Send message
await wahaClient.sendText({
phoneNumber: account.phoneNumber,
chatId: '5511999999999@c.us',
text: 'Hello!'
})
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Client │ │ Server │ │ WorkOS │ │ Database │
└────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │ │
│ 1. Login │ │ │
│─────────────────►│ │ │
│ │ 2. Auth URL │ │
│ │─────────────────►│ │
│ 3. Redirect │ │ │
│◄─────────────────│◄─────────────────│ │
│ │ │ │
│ 4. Code │ │ │
│─────────────────►│ │ │
│ │ 5. Token │ │
│ │─────────────────►│ │
│ │ 6. User Info │ │
│ │◄─────────────────│ │
│ │ │ │
│ │ 7. Create/Update Session │
│ │────────────────────────────────────►│
│ │ │ │
│ 8. Session │ │ │
│◄─────────────────│ │ │
│ │ │ │
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Client │ │ Server │ │ WA Server│ │ WhatsApp │
└────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │ │
│ 1. Start │ │ │
│─────────────────►│ │ │
│ │ 2. Start │ │
│ │─────────────────►│ │
│ │ │ 3. Init Client │
│ │ │─────────────────►│
│ │ │ 4. QR Code │
│ │ 5. QR Code │◄─────────────────│
│ 6. Show QR │◄─────────────────│ │
│◄─────────────────│ │ │
│ │ │ │
│ [User scans QR with phone] │ │
│ │ │ │
│ │ │ 7. Auth Success │
│ │ │◄─────────────────│
│ │ 8. Connected │ │
│ 9. Ready │◄─────────────────│ │
│◄─────────────────│ │ │
│ │ │ │
User types message
┌───────────────────┐
│ Client (React) │ MessageInput component
└─────────┬─────────┘
│ tRPC mutation (planned)
┌───────────────────┐
│ Server │ Validates, logs, routes
└─────────┬─────────┘
│ REST API
┌───────────────────┐
│ WhatsApp Server │ WAHA API layer
└─────────┬─────────┘
│ whatsapp-web.js
┌───────────────────┐
│ WhatsApp Web │ Browser automation
└─────────┬─────────┘
Message sent
User clicks "Upgrade"
┌───────────────────┐
│ Subscription App │ Pricing page
└─────────┬─────────┘
│ Redirect to Creem
┌───────────────────┐
│ Creem │ Payment processing
└─────────┬─────────┘
│ Redirect with signature
┌───────────────────┐
│ Server │ Verify signature, create subscription
└─────────┬─────────┘
│ Update database
┌───────────────────┐
│ PostgreSQL │ subscriptions table
└───────────────────┘
// Global state via React Context
contexts/
├── AuthContext # User session, tokens
├── AccountContext # Selected WhatsApp account
├── ConversationContext # Selected chat, messages
├── SubscriptionContext # Plan limits, features
└── ThemeContext # Dark/light mode
// Server state via TanStack Query (tRPC)
hooks/
├── useAccounts() # tRPC query wrapper
├── useFolders() # tRPC query wrapper
├── useTemplates() # tRPC query wrapper
└── useSubscription() # tRPC query wrapper
// Server state via tRPC + React Query
// Local state via React hooks
// Form state via react-hook-form
// Toast state via sonner
  • WorkOS AuthKit: Primary SSO provider
  • Better Auth: Fallback/alternative auth
  • Session tokens: HttpOnly cookies
  • JWT validation: Server-side on every request
  • Resource ownership: All queries filter by userId
  • Subscription limits: Middleware checks plan features
  • Rate limiting: Per-user API quotas (planned)
  • Input validation: Zod schemas on all inputs
  • SQL injection: Prevented by Drizzle ORM parameterization
  • XSS: React’s automatic escaping + CSP headers
  • CORS: Strict origin allowlist
Terminal window
# Start all services
pnpm dev
# Individual services
pnpm --filter client dev # :3000
pnpm --filter server dev # :8787
pnpm --filter whatsapp-web-server dev # :8788
pnpm --filter subscription dev # :3001
  • Client: Vercel (static + serverless)
  • Server: Cloudflare Workers or Railway
  • WhatsApp Server: Railway (needs persistent connections)
  • Database: Neon PostgreSQL
  • CDN: Cloudflare
apps/client/src/instrument.server.mjs
import * as Sentry from '@sentry/react'
Sentry.init({
dsn: process.env.SENTRY_DSN,
environment: process.env.NODE_ENV,
tracesSampleRate: 0.1
})
// Server: similar setup with @sentry/node
  • Development: Console + pretty printing
  • Production: Structured JSON logs
  • Log levels: debug, info, warn, error
  1. Message Queue: Redis/BullMQ for async message processing
  2. WebSocket: Real-time updates via Socket.io or Soketi
  3. Cache Layer: Redis for session/query caching
  4. Search: Meilisearch for message search
  5. Media Storage: S3-compatible object storage