Backend Service
The backend is the core API server for the AIQLick platform, handling authentication, business logic, billing, notifications, and all primary GraphQL operations.
Technology
| Component | Detail |
|---|---|
| Framework | NestJS 11 with Apollo Server 4 |
| API | GraphQL (auto-generated schema at schema.gql, 320+ types) |
| ORM | Prisma 6 with PostgreSQL 16 (135 models) |
| Queues | Bull backed by Redis for async job processing |
| Payments | Stripe integration for subscriptions, credit packs, and invoicing |
| Port | 4001 (production: https://api.aiqlick.com/graphql) |
Authentication
All authenticated endpoints use the @UseGuards(JwtAuthGuard) decorator. The currently authenticated user is injected via the @CurrentUser() decorator, which provides the full User entity to resolver methods.
@UseGuards(JwtAuthGuard)
@Resolver(() => Job)
export class JobResolver {
@Query(() => [Job])
async jobs(@CurrentUser() user: User) {
// user is injected from JWT
}
}
JWT tokens are issued on login and validated on every request. The frontend sends them in the Authorization header as a Bearer token.
GraphQL API
Schema Generation
The schema is auto-generated from resolver decorators. Running npm run schema:generate regenerates schema.gql from the codebase. The schema covers 320+ types across all platform features.
Subscriptions
Real-time data is delivered via the graphql-ws WebSocket protocol, with Redis pub/sub as the transport layer between server instances.
| Subscription | Description | Filtering |
|---|---|---|
notificationCreated | Delivers new notifications as they are created | By userId |
unreadCountChanged | Updates the unread notification badge count | By userId, optional companyId |
logStream | Live system log streaming for diagnostics | Super admin only |
messageSent | New message in a conversation thread | By conversation participants |
messageUpdated | Message edited or status changed | By conversation participants |
Event-Driven Notification System
The backend uses @nestjs/event-emitter for internal domain events. When significant actions occur (candidate status change, interview scheduled, payment processed), domain events are dispatched and handled by specialized listeners.
Action --> @nestjs/event-emitter --> Listener --> Bull Queue --> Delivery (IN_APP, EMAIL, PUSH)
6 notification listeners in src/notification-system/listeners/ handle a total of 80 event types across these categories:
| Category | Examples |
|---|---|
| User | Account creation, profile updates, authentication events |
| Billing | Subscription changes, credit purchases, payment failures |
| Collaboration | Company membership invitations, role changes, sharing |
| Talent | Talent pool updates, CV processing completion, profile changes |
| Recruitment | Candidate status changes, job updates, pipeline modifications |
| Meeting | Scheduling confirmations, reminders, insight availability |
Plan Limit Guards
Feature access is enforced at the resolver level using the @CheckPlanLimit decorator:
@UseGuards(JwtAuthGuard)
@CheckPlanLimit('maxJobs')
@Mutation(() => Job)
async createJob(...) { }
Plan limits use the convention: -1 for unlimited, 0 for disabled, and any positive number for a specific limit.
Scheduled Jobs
| Schedule | Job | Purpose |
|---|---|---|
| Daily at 2:00 AM | Credit replenishment | Replenish subscription-based credits on a 30-day cycle |
| Daily at 9:00 AM | Subscription expiry warnings | Notify users at 7, 3, and 1 days before expiration |
| Daily at 9:00 AM | Daily digest | Send aggregated daily activity summaries via email |
| Monday at 9:00 AM | Weekly digest | Send weekly activity summaries via email |
| Every hour | Interview reminders | Send reminders at 24h, 6h, and 1h before interviews |
| Every hour | Meeting reminders | Send reminders at 24h, 6h, and 1h before meetings |
| Every hour | Slot offer expiration | Expire unclaimed interview slot offers |
| 1st of month at 3:00 AM | Enterprise invoice generation | Generate usage-based invoices for POSTPAID companies |
| Daily at 10:00 AM | Enterprise overdue check | Block access for companies with past-due enterprise invoices |
Mail System
Email delivery is managed through an Amazon SQS queue (aiqlick-mail-*) and AWS SES (or Nodemailer SMTP fallback):
MailService.queueMail()creates anEmailrecord withPENDINGstatus and enqueues a message with theemailIdonto SQS.SqsMailConsumerprocesses SQS messages and sends the email via the configured transport (natively AWS SES, or SMTP as a fallback).- The database record is updated to
SENTorFAILEDbased on the outcome.
Calendar invitation templates with RSVP links are used for meeting and interview scheduling emails.
Key Modules
| Module | Responsibility |
|---|---|
| Auth | JWT issuance, validation, password reset |
| Company | Organization CRUD, member management, RBAC |
| Job Management | Job postings, status transitions, company role assignment |
| Candidate | Application tracking, status progression, bulk operations |
| Pipeline | Workflow definition, stage management, candidate positioning |
| Talent | Talent pool management, profile storage, external sharing |
| Contacts | Contact directory and organization |
| Notification System | Event listeners, multi-channel delivery, user preferences |
| Payment | Stripe webhooks, subscriptions, credit packs, invoices, promo codes |
| Meeting | Scheduling, Jitsi room management, calendar invitations |
| Admin | Platform analytics (15+ stat queries), user management, RBAC admin, system configuration |
External Sharing
The backend supports sharing resources externally via UUID-based tokens (double UUID format, 7-day default expiry). Shareable resources include talent pools, user profiles, talents, shared talent lists, pipelines, and CV collections.
Each share record tracks token (unique), isActive, expiresAt, and accessCount.
Health Checks
| Endpoint | Purpose |
|---|---|
/health | Liveness check for load balancer routing |
/healthz | Alternative health endpoint |
/graphql | Introspection query used by CI/CD to verify schema availability |
Testing
| Command | Scope | Needs DB |
|---|---|---|
npm run test:unit | Unit tests only (*.spec.ts) | No |
npm run test:safe | Unit + mocked integration (run manually before commit; also runs in CI) | No |
npm run test:integration | Real database tests | Yes (RUN_REAL_INTEGRATION_TESTS=true) |
npm run test:e2e | End-to-end tests | Yes (RUN_REAL_E2E_TESTS=true + E2E_API_URL) |
npm test -- path/to/file.spec.ts | Single file | Depends on test type |
- Guard mocking:
.overrideGuard(JwtAuthGuard).useValue({ canActivate: () => true })for resolver tests. - Timeout: 40 seconds per test.
- Coverage: Collected from
src/**/*.(t|j)s. - Pre-commit hook: Runs Prisma generate →
yarn build→ schema sync (if GraphQL surface changed) → dev-boot probe onSERVICE_PORT=14001(fails commit if the app can't reachApplication started successfullywithin 60 s). Tests run in CI, not pre-commit — see Testing Guide.