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.ts validates 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:///system with 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 check
  • POST /isoUpload - ISO file upload (multipart)
  • GET /infiniservice/:platform/:filename - Guest agent binary distribution
  • GET /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: