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 libvirt, health monitoring, real-time communication, firewall configuration. 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/libvirt-node | Custom | Native Rust bindings for libvirt |
Custom Native Module
@infinibay/libvirt-node is a Node.js native addon built from Rust using NAPI with direct bindings to the libvirt C library for VM operations.
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 --> |Native Binding| IL[Integration Layer]
IL --> |libvirt| HV[Hypervisor]
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 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
- Native libvirt bindings
- 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, NWFilterXMLGeneratorService, LibvirtNWFilterService
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 libvirtConnection: LibvirtConnection, 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 libvirt 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
Libvirt
@infinibay/libvirt-node native bindings for VM operations:
- Connection: Single persistent connection to
qemu:///systemwith automatic reconnection - Domain operations:
create()(start),shutdown()(stop),destroy()(force stop),undefine()(delete), XML manipulation for configuration - NWFilter:
defineNWFilter()for firewall rules, automatic attachment to VM network interfaces
Implementation: backend/app/services/libvirt/, backend/app/utils/LibvirtConnectionPool.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 libvirt QEMU agent channel 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 (libvirt, 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"
LibvirtPool[Libvirt Connection Pool]
VirtIO[VirtIO Socket Watcher]
QEMU[QEMU Guest Agent]
end
subgraph "External Systems"
DB[(PostgreSQL)]
Hypervisor[Libvirt/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 --> LibvirtPool
FW --> LibvirtPool
LibvirtPool --> Hypervisor
VirtIO --> Guest
QEMU --> 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 LibvirtPool fill:#ec4899,stroke:#db2777,stroke-width:2px,color:#fff
style VirtIO fill:#ec4899,stroke:#db2777,stroke-width:2px,color:#fff
style QEMU fill:#ec4899,stroke:#db2777,stroke-width:2px,color:#fff
style DB fill:#8b5cf6,stroke:#7c3aed,stroke-width:2px,color:#fff
style Hypervisor 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