Backend Architecture
Complete architecture overview of Infinibay backend - Node.js, Express, Apollo GraphQL, Prisma ORM, and PostgreSQL
Backend Architecture
Introduction
Backend orchestration layer for Infinibay VDI platform. GraphQL API for frontend, VM lifecycle via Infinization (QEMU), health monitoring, real-time communication, firewall configuration via nftables. Layered architecture with separation of concerns and type safety.
Technology Stack
| Category | Technology | Version | Purpose |
|---|---|---|---|
| Runtime | Node.js | 18+ | JavaScript runtime environment |
| Language | TypeScript | 5.3 | Type-safe development |
| Web Framework | Express | 4.18.2 | HTTP server and middleware |
| GraphQL Server | Apollo Server | 4.3.0 | GraphQL API with TypeScript type generation |
| Schema Generation | TypeGraphQL | 2.0 | Decorator-based schema definition |
| Database | PostgreSQL | 12+ | Relational database |
| ORM | Prisma | 6.16.3 | PostgreSQL ORM |
| Real-time | Socket.io | 4.5.4 | WebSocket communication |
| Authentication | jsonwebtoken | 9.0.2 | JWT token generation/validation |
| Password Hashing | bcrypt | 5.1.0 | Password storage |
| Task Scheduling | node-cron | 3.2.1 | Cron job execution |
| Logging | Winston | 3.8.2 | Log rotation |
| Virtualization | @infinibay/infinization | Custom | TypeScript QEMU command builder |
| Firewall | nftables | System | Network filtering via nftables |
Infinization Module
@infinibay/infinization is a TypeScript library that builds and spawns QEMU processes directly, bypassing libvirt for VM management. This provides more control and simplifies the architecture. Firewall rules are managed via nftables.
Architecture Overview
graph TD
Client[Client Applications] --> PL[Presentation Layer]
PL --> |GraphQL/REST| SL[Service Layer]
SL --> |Business Logic| DAL[Data Access Layer]
DAL --> |Prisma ORM| DB[(PostgreSQL)]
SL --> |TypeScript| IL[Integration Layer]
IL --> |Infinization| HV[QEMU/KVM]
IL --> |nftables| FW[Firewall]
IL --> |VirtIO Socket| GA[Guest Agent]
style Client fill:#64748b,stroke:#475569,stroke-width:2px,color:#fff
style PL fill:#0ea5e9,stroke:#0284c7,stroke-width:2px,color:#fff
style SL fill:#f59e0b,stroke:#d97706,stroke-width:2px,color:#000
style DAL fill:#22c55e,stroke:#16a34a,stroke-width:2px,color:#fff
style IL fill:#ec4899,stroke:#db2777,stroke-width:2px,color:#fff
style DB fill:#8b5cf6,stroke:#7c3aed,stroke-width:2px,color:#fff
style HV fill:#64748b,stroke:#475569,stroke-width:2px,color:#fff
style FW fill:#64748b,stroke:#475569,stroke-width:2px,color:#fff
style GA fill:#64748b,stroke:#475569,stroke-width:2px,color:#fff
1. Presentation Layer
- Apollo Server with TypeGraphQL
- Express routes for uploads and health checks
- Socket.io real-time updates
- JWT authentication
2. Service Layer
- 30+ domain services (VM, health, firewall, events)
- Business logic orchestration
3. Data Access Layer
- Prisma ORM with type-safe queries
- Model lifecycle hooks
- DataLoader for N+1 prevention
4. Integration Layer
- Infinization (TypeScript QEMU command builder)
- nftables for firewall
- VirtIO socket + QEMU Guest Agent
- Connection pooling
Application Bootstrap Flow
sequenceDiagram
participant Main as index.ts
participant Express
participant Apollo
participant SocketIO as Socket.io
participant Events as Event System
participant Health as Health Monitoring
participant VirtIO as VirtIO Watcher
participant Crons
Main->>Main: 1. checkGpuAffinity()
Main->>Express: 2. Create Express + HTTP server
Main->>Express: 3. configureServer (CORS, timeouts, body parser)
Main->>Express: 4. configureRoutes (health, ISO upload, etc.)
Main->>VirtIO: 5. Create VirtioSocketWatcher (early for context)
Main->>Apollo: 6. Create and start Apollo Server
Main->>Express: 7. Mount GraphQL middleware
Main->>SocketIO: 8. Initialize Socket.io + auth
Main->>Events: 9. Create EventManager
Events->>Events: 10. Register resource managers (VM, Dept, Firewall, User, App)
Main->>Health: 11. Initialize health queue + background service
Main->>VirtIO: 12. Start VirtioSocketWatcher
VirtIO-->>SocketIO: 13. Forward metrics to Socket.io
Main->>Crons: 14. Start cron jobs
Main->>Express: 15. Listen on port 4000
GraphQL Layer
Apollo Server Configuration
const schema = await buildSchema({
resolvers, // 20+ resolver classes
emitSchemaFile: path.resolve(__dirname, '../schema.graphql'),
authChecker
})
return new ApolloServer({
schema,
csrfPrevention: true,
cache: 'bounded',
formatError: (error) => { /* Normalize error codes */ }
})
Features: TypeGraphQL schema generation, auto-generated app/schema.graphql for frontend codegen, custom auth checker for RBAC, CSRF prevention, bounded cache, error formatting (UNAUTHORIZED, FORBIDDEN, NOT_FOUND).
Context Creation
InfinibayContext created per GraphQL request:
export interface InfinibayContext {
req: Request
res: Response
user: SafeUser | null // Authenticated user (excludes password/token)
prisma: PrismaClient
setupMode: boolean
eventManager?: EventManager
virtioSocketWatcher?: VirtioSocketWatcherService
auth?: AuthenticationMetadata
userHelpers?: UserValidationHelpers
}
Auth flow: JWT token extracted from Authorization: Bearer <token> header → verified with TOKENKEY → user fetched from database → SafeUser + metadata added to context.
Resolver Organization
Resolvers in backend/app/graphql/resolvers/: UserResolver, MachineResolver, DepartmentResolver, FirewallResolver, HealthResolver, MaintenanceResolver, ApplicationResolver, TemplateResolver, ISOResolver, NetworkResolver, SnapshotResolver, PackageResolver.
TypeGraphQL example:
@Resolver()
export class MachineResolver {
@Query(() => Machine, { nullable: true })
@Authorized()
async machine(@Arg('id') id: string, @Ctx() context: InfinibayContext) {
return context.prisma.machine.findUnique({ where: { id } })
}
@Mutation(() => Machine)
@Authorized('ADMIN')
async createMachine(@Arg('input') input: CreateMachineInputType, @Ctx() context: InfinibayContext) {
return vmOperationsService.createMachine(input, context)
}
}
Authorization
- JWT tokens (user ID, email, role, expiration)
- Roles: USER, ADMIN, SUPER_ADMIN
@Authorized()decorator: any authenticated user@Authorized('ADMIN'): ADMIN or SUPER_ADMIN- Auth checker in
utils/authChecker.tsvalidates context.user and role - Fallback auth if context auth fails
Service Layer
VM Operations: VMOperationsService, MachineLifecycleService, SnapshotService
Health Monitoring: VMHealthQueueManager, BackgroundHealthService, BackgroundTaskService, VMRecommendationService
Firewall: FirewallManager, FirewallRuleService, NftablesService
Maintenance: MaintenanceService
Real-time Events: EventManager, SocketService, resource-specific event managers
Guest Communication: VirtioSocketWatcherService, QemuGuestAgentService
Package Management: DirectPackageManager
Unattended Installation: UnattendedWindowsManager, UnattendedUbuntuManager, UnattendedRedHatManager
Infrastructure: NetworkService, ISOService, LibvirtConnectionPool, DataLoaderService
Service Patterns
All services use: constructor injection, async/await, try-catch error handling, event emission for real-time updates, idempotent operations where possible.
export class FirewallManager {
constructor(private prisma: PrismaClient, private nftablesService: NftablesService, private eventManager: EventManager) {}
async createFirewallRule(ruleSetId: string, input: CreateFirewallRuleInput): Promise<FirewallRule> {
await this.validateRule(input)
const rule = await this.prisma.firewallRule.create({ data: { ...input, ruleSetId } })
await this.eventManager.dispatchEvent('firewall', 'create', { id: rule.id })
return rule
}
}
Data Access Layer
Prisma ORM
const prisma = new PrismaClient()
applyModelsCallbacks(prisma) // Lifecycle hooks
export default prisma
Type-safe queries, connection pooling, migrations, extensions for lifecycle hooks.
Model Callbacks
Prisma extensions provide lifecycle hooks (afterCreate, afterUpdate, afterDelete):
prisma.$extends({
query: {
machine: {
async afterCreate({ result }) {
await createVMFirewall(result)
await eventManager.vmCreated(result)
}
}
}
})
Use cases: automatic firewall setup, real-time event emission, cascade cleanup.
DataLoader Pattern
Prevents N+1 queries by batching and caching:
this.machineLoader = new DataLoader(async (ids) => {
const machines = await prisma.machine.findMany({ where: { id: { in: ids } } })
return ids.map(id => machines.find(m => m.id === id))
})
Real-time Communication
Socket.io
export class SocketService {
private io: Server
private userConnections: Map<string, string> = new Map()
initialize(httpServer: HttpServer): void {
this.io = new Server(httpServer, { cors: { origin: '*' } })
this.io.use(async (socket, next) => {
const user = await verifyToken(socket.handshake.auth.token)
if (user) {
socket.data.userId = user.id
this.userConnections.set(user.id, socket.id)
next()
} else {
next(new Error('Unauthorized'))
}
})
this.io.on('connection', (socket) => {
socket.join(`user:${socket.data.userId}`)
socket.on('disconnect', () => this.userConnections.delete(socket.data.userId))
})
}
emitToRoom(room: string, event: string, data: any): void {
this.io.to(room).emit(event, data)
}
}
JWT authentication on connection, user-specific rooms, room-based broadcasting (vm:{vmId}, department:{deptId}).
Event Management
Observer pattern with resource-specific managers. EventManager dispatches to VmEventManager, DepartmentEventManager, FirewallEventManager, etc. Each manager calculates target users and broadcasts via SocketService.
Event types: create, update, delete, power_on, power_off, suspend, resume, health_check, maintenance_completed, etc.
Background Processing
| Cron Job | Schedule | Purpose |
|---|---|---|
| UpdateVmStatus | Every 30 seconds | Sync VM power states from QEMU processes to database |
| UpdateGraphicsInformation | Every 1 minute | Refresh SPICE/VNC connection details (port, password, protocol) |
| ProcessHealthQueue | Every 1 minute | Execute queued health checks (disk space, updates, defender, etc.) |
| ScheduleOverallScans | Daily at 2 AM | Schedule comprehensive health scans for all VMs |
| MetricsWatchdog | Every 5 minutes | Monitor metrics collection, detect stale data, create alerts |
| CleanupOrphanedHealthTasks | Daily at 3 AM | Remove orphaned or stale health check tasks |
| ProcessMaintenanceQueue | Every 1 minute | Execute scheduled maintenance tasks (cleanup, defrag, updates) |
External Integrations
Infinization (VM Management)
@infinibay/infinization TypeScript library for VM operations:
- QEMU Process Management: Direct QEMU process spawning without libvirt abstraction
- Command Building: Type-safe QEMU command line construction
- VM Operations: Start, stop, pause, resume, delete VMs via process control
- Configuration: VM parameters translated to QEMU arguments
Implementation: infinization/ (separate package)
nftables (Firewall)
Network filtering is managed via nftables:
- NftablesService: Manages VM firewall chains
- Per-VM chains: Each VM gets its own nftables chain
- Department inheritance: VM rules inherit from department rules
Implementation: infinization/src/network/NftablesService.ts
VirtIO Socket
Watches /var/run/infinibay/**/*.sock for guest agent connections. JSON command/response protocol, keep-alive monitoring (30s ping, 90s timeout), automatic metrics collection and storage, event emission for real-time updates.
QEMU Guest Agent
Fallback communication via QEMU guest agent when VirtIO socket unavailable.
Configuration
Environment Variables
DATABASE_URL="postgresql://user:password@localhost:5432/infinibay"
PORT=4000
ALLOWED_ORIGINS="http://localhost:3000"
TOKENKEY="your-secret-jwt-key"
DEBUG_AUTH=0
ALLOW_INSECURE_JWT_FALLBACK=0
NODE_ENV=production
Required: DATABASE_URL, TOKENKEY. Optional: PORT (default 4000), ALLOWED_ORIGINS (default *), DEBUG_AUTH (default 0), ALLOW_INSECURE_JWT_FALLBACK (default 0, dev only - enables insecure JWT fallback), NODE_ENV (default production).
Server Configuration
CORS (configurable origins), body parser (100GB limit for ISO uploads), 1-hour timeout for large uploads, static file serving (7-day cache).
Routes
GET /health- Health checkPOST /isoUpload- ISO file upload (multipart)GET /infiniservice/:platform/:filename- Guest agent binary distributionGET /api/wallpapers- Wallpaper API
Error Handling & Logging
ErrorHandler logs errors to database with severity (Critical/High/Medium/Low) and emits real-time notifications.
Winston logger with daily rotation (14-day retention). Debug module for development: DEBUG=infinibay:* npm run dev.
Security
JWT token validation, bcrypt password hashing (10 rounds), RBAC via @Authorized decorator, CSRF prevention, CORS configuration, Prisma parameterized queries, input validation, SafeUser excludes sensitive data, Socket.io JWT auth, static file security.
Performance
DataLoader prevents N+1 queries, Prisma selective includes and indexing, connection pooling (database), bounded Apollo cache, background processing for heavy operations, VirtIO sockets for fast guest communication, static file caching (7 days).
Mermaid Diagrams
Component Architecture
graph TD
subgraph "Presentation Layer"
GQL[Apollo GraphQL Server]
REST[REST Endpoints]
WS[Socket.io WebSocket]
end
subgraph "Service Layer"
VM[VM Operations Service]
Health[Health Monitoring Service]
FW[Firewall Manager]
Maint[Maintenance Service]
Events[Event Manager]
end
subgraph "Data Access Layer"
Prisma[Prisma ORM]
Callbacks[Model Callbacks]
DataLoader[DataLoader]
end
subgraph "Integration Layer"
Infinization[Infinization Service]
Nftables[nftables]
VirtIO[VirtIO Socket Watcher]
QEMUAgent[QEMU Guest Agent]
end
subgraph "External Systems"
DB[(PostgreSQL)]
QEMU[QEMU/KVM]
Guest[Guest Agent]
end
GQL --> VM
GQL --> Health
GQL --> FW
GQL --> Maint
REST --> VM
VM --> Prisma
Health --> Prisma
FW --> Prisma
Maint --> Prisma
VM --> Events
Health --> Events
FW --> Events
Maint --> Events
Events --> WS
Prisma --> DB
Callbacks --> Events
DataLoader --> Prisma
VM --> Infinization
FW --> Nftables
Infinization --> QEMU
VirtIO --> Guest
QEMUAgent --> Guest
Health --> VirtIO
style GQL fill:#0ea5e9,stroke:#0284c7,stroke-width:2px,color:#fff
style REST fill:#0ea5e9,stroke:#0284c7,stroke-width:2px,color:#fff
style WS fill:#0ea5e9,stroke:#0284c7,stroke-width:2px,color:#fff
style VM fill:#f59e0b,stroke:#d97706,stroke-width:2px,color:#000
style Health fill:#f59e0b,stroke:#d97706,stroke-width:2px,color:#000
style FW fill:#f59e0b,stroke:#d97706,stroke-width:2px,color:#000
style Maint fill:#f59e0b,stroke:#d97706,stroke-width:2px,color:#000
style Events fill:#f59e0b,stroke:#d97706,stroke-width:2px,color:#000
style Prisma fill:#22c55e,stroke:#16a34a,stroke-width:2px,color:#fff
style Callbacks fill:#22c55e,stroke:#16a34a,stroke-width:2px,color:#fff
style DataLoader fill:#22c55e,stroke:#16a34a,stroke-width:2px,color:#fff
style Infinization fill:#ec4899,stroke:#db2777,stroke-width:2px,color:#fff
style Nftables fill:#ec4899,stroke:#db2777,stroke-width:2px,color:#fff
style VirtIO fill:#ec4899,stroke:#db2777,stroke-width:2px,color:#fff
style QEMUAgent fill:#ec4899,stroke:#db2777,stroke-width:2px,color:#fff
style DB fill:#8b5cf6,stroke:#7c3aed,stroke-width:2px,color:#fff
style QEMU fill:#64748b,stroke:#475569,stroke-width:2px,color:#fff
style Guest fill:#64748b,stroke:#475569,stroke-width:2px,color:#fff
Request Flow
sequenceDiagram
participant Client
participant Apollo as Apollo Server
participant Resolver
participant Service
participant Prisma
participant DB as PostgreSQL
participant Events as EventManager
participant Socket as Socket.io
Client->>Apollo: GraphQL Request + JWT
Apollo->>Apollo: Verify JWT
Apollo->>Apollo: Create Context (user, prisma, eventManager)
Apollo->>Resolver: Execute Query/Mutation
Resolver->>Resolver: Check @Authorized
Resolver->>Service: Call Service Method
Service->>Prisma: Database Query
Prisma->>DB: SQL Query
DB-->>Prisma: Result
Prisma-->>Service: Typed Result
Service->>Events: Emit Event (create/update/delete)
Events->>Socket: Broadcast to Connected Clients
Socket-->>Client: Real-time Update
Service-->>Resolver: Service Result
Resolver-->>Apollo: GraphQL Response
Apollo-->>Client: JSON Response
For detailed information about specific components:
- Database Schema - Complete database model documentation
- GraphQL API - GraphQL queries and mutations reference