Back to portfolio

Ticket Platform API

Centralized platform for management, assignment, and resolution of technical support tickets.

Backend NestJS RBAC Ticketing Microservice

Ticket Platform API is a robust backend solution built with NestJS and TypeScript, designed to orchestrate customer service workflows end to end. It implements a hierarchical RBAC system (User, Agent, Admin), a state machine for ticket lifecycle, real-time chat via WebSocket (Socket.IO), an invitation system with conflict resolution, internal messaging for staff, attachment management with soft-delete, and a timeline of 30 event types for complete auditability. Designed with a "Security by Default" approach, guaranteeing full traceability and automated audit flows.

50+
Endpoints
8
Models
13
Migrations
21 spec files
Tests

Screenshots coming soon

1. Architecture

Controllers (HTTP Layer)

Thin HTTP layer handling HTTP concerns, parameter extraction, guard application, and Swagger decorators. All logic is delegated to services.

Services (Business Logic)

Business logic layer with rich domain rules, transaction management, and event logging. Each module has its own service (TicketsService, MessagesService, etc.).

Prisma (Data Access)

Data access layer with Prisma v7 using @prisma/adapter-pg. PrismaModule is @Global() for availability across all modules without explicit import.

2. Features

System Core

Modular Architecture with NestJS

Each domain (tickets, messages, participants, invitations, attachments, events, categories, users) has its own module with Controller, Service, DTOs, and tests. Structured dependency injection under SOLID principles.

State Machine

Role-controlled state transitions: Staff can OPEN→IN_PROGRESS→RESOLVED→CLOSED and reopen. Users can only OPEN→CLOSED. Each transition generates an audit event with JSON payload.

Advanced Hierarchical RBAC

Three roles (USER, AGENT, ADMIN) with global guards: JwtAuthGuard, ActiveUserGuard, RolesGuard. TicketAccessPolicy centralizes all access decisions. Dual roles: external JWT vs local internalRole.

Ticket Number Generation

TCK-YYYY-NNNNN format with race condition handling: retry mechanism (up to 3 attempts) that catches P2002 unique constraint violations and regenerates the number.

Collaboration & Messaging

Real-Time Chat (WebSocket)

Socket.IO with per-ticket rooms (ticket:{number}). Broadcast events: new-message, message-updated, message-deleted, new-attachment, attachment-deleted, ticket-event. Independent WS authentication with access verification.

Messaging System

Public and internal messages (staff only). Pagination, visibility filters. Posting on closed ticket auto-reopens it. Soft-delete with content erasure for privacy.

Invitation System

Full workflow: create, accept, reject, cancel. Permission flags (can_edit, can_comment). Conflict resolution: if user was already added by other means, invitation is auto-cancelled.

File Attachment Management

Upload, validation, and administration of documents linked to messages. Soft-delete with URL erasure for future async cleanup (S3). Filter by messageId.

Security & Audit

Event Timeline (30 types)

Every mutation generates an immutable TicketEvent with JSON payload. 30 event types covering everything: creation, messages, assignments, status changes, priority, category, permissions, invitations, locks.

Soft-Delete with Content Erasure

Messages: content erased + deleted_at preserved. Attachments: deleted_at + deleted_by, URL preserved for async cleanup. Users: is_active=false, never physically deleted.

Lock Mechanisms

Three independent locks: priority_locked (AGENT/ADMIN), category_locked (AGENT/ADMIN), assignment_locked (ADMIN only). Prevent changes to locked field even by participants with edit permissions.

Staff Auto-Enrollment

When staff (AGENT/ADMIN) acts on a ticket they're not participating in, they're automatically added as participants via ensureStaffParticipant(). Ensures audit trail completeness.

Infrastructure

Full Observability

Prometheus metrics: http_requests_total, http_request_duration_seconds, tickets_total, messages_total. Structured logging with Pino and automatic sensitive header redaction. Exception filters for Prisma and general errors.

Swagger/OpenAPI Documentation

All endpoints documented with @ApiOperation, @ApiResponse, @ApiParam. Bearer auth configured globally. Available at /api/docs.

Exhaustive Testing Suite

21 spec files covering controllers, services, guards, and configuration. Unit tests with Prisma mocks, guards overridden in controller tests. Jest v30 with ts-jest configuration.

Docker & Deployment

Multi-stage Dockerfile with Node.js Alpine. Docker Compose with external "backend" network. Automatic prisma migrate deploy at startup. Environment variables from .env.

3. Tech Stack

TypeScript
NestJS
PostgreSQL
Docker
Prisma Prisma
Jest

4. API Reference

Module Endpoints
Tickets 18
Participants 4
Invitations 7
Messages 7
Attachments 5
Events 2
Categories 9
Users 9
Health 2

5. Design Decisions

JIT User Sync

Users are not pre-registered. On every JWT validation, the system checks if the external UID exists locally and creates it if not. This decouples identity management from the ticket platform, allowing AuthService to be the single source of truth for authentication.

Event Visibility System

Events can be public (visible to all participants) or personal (visible only to explicitly tagged users and staff). PARTICIPANT_PERMISSIONS_CHANGED is the only personal event type, ensuring permission changes are only visible to the affected user.

Auto-Reopen on Message

When a message is posted to a CLOSED ticket, the ticket is automatically reopened (status→OPEN, closed_at→null) with a STATUS_CHANGED event. This ensures tickets don't get lost if a client responds after closure.

Dual Role System

Users have two role systems: JWT role (from external auth: USER/ADMIN) and internalRole (from local DB: USER/AGENT/ADMIN). The internalRole is the authority for all authorization decisions, allowing a finer-grained model (AGENT) than the external identity provider.