Skip to main content

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

ComponentDetail
FrameworkNestJS 11 with Apollo Server 4
APIGraphQL (auto-generated schema at schema.gql, 320+ types)
ORMPrisma 6 with PostgreSQL 16 (135 models)
QueuesBull backed by Redis for async job processing
PaymentsStripe integration for subscriptions, credit packs, and invoicing
Port4001 (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.

SubscriptionDescriptionFiltering
notificationCreatedDelivers new notifications as they are createdBy userId
unreadCountChangedUpdates the unread notification badge countBy userId, optional companyId
logStreamLive system log streaming for diagnosticsSuper admin only
messageSentNew message in a conversation threadBy conversation participants
messageUpdatedMessage edited or status changedBy 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:

CategoryExamples
UserAccount creation, profile updates, authentication events
BillingSubscription changes, credit purchases, payment failures
CollaborationCompany membership invitations, role changes, sharing
TalentTalent pool updates, CV processing completion, profile changes
RecruitmentCandidate status changes, job updates, pipeline modifications
MeetingScheduling 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

ScheduleJobPurpose
Daily at 2:00 AMCredit replenishmentReplenish subscription-based credits on a 30-day cycle
Daily at 9:00 AMSubscription expiry warningsNotify users at 7, 3, and 1 days before expiration
Daily at 9:00 AMDaily digestSend aggregated daily activity summaries via email
Monday at 9:00 AMWeekly digestSend weekly activity summaries via email
Every hourInterview remindersSend reminders at 24h, 6h, and 1h before interviews
Every hourMeeting remindersSend reminders at 24h, 6h, and 1h before meetings
Every hourSlot offer expirationExpire unclaimed interview slot offers
1st of month at 3:00 AMEnterprise invoice generationGenerate usage-based invoices for POSTPAID companies
Daily at 10:00 AMEnterprise overdue checkBlock 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):

  1. MailService.queueMail() creates an Email record with PENDING status and enqueues a message with the emailId onto SQS.
  2. SqsMailConsumer processes SQS messages and sends the email via the configured transport (natively AWS SES, or SMTP as a fallback).
  3. The database record is updated to SENT or FAILED based on the outcome.

Calendar invitation templates with RSVP links are used for meeting and interview scheduling emails.

Key Modules

ModuleResponsibility
AuthJWT issuance, validation, password reset
CompanyOrganization CRUD, member management, RBAC
Job ManagementJob postings, status transitions, company role assignment
CandidateApplication tracking, status progression, bulk operations
PipelineWorkflow definition, stage management, candidate positioning
TalentTalent pool management, profile storage, external sharing
ContactsContact directory and organization
Notification SystemEvent listeners, multi-channel delivery, user preferences
PaymentStripe webhooks, subscriptions, credit packs, invoices, promo codes
MeetingScheduling, Jitsi room management, calendar invitations
AdminPlatform 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

EndpointPurpose
/healthLiveness check for load balancer routing
/healthzAlternative health endpoint
/graphqlIntrospection query used by CI/CD to verify schema availability

Testing

CommandScopeNeeds DB
npm run test:unitUnit tests only (*.spec.ts)No
npm run test:safeUnit + mocked integration (run manually before commit; also runs in CI)No
npm run test:integrationReal database testsYes (RUN_REAL_INTEGRATION_TESTS=true)
npm run test:e2eEnd-to-end testsYes (RUN_REAL_E2E_TESTS=true + E2E_API_URL)
npm test -- path/to/file.spec.tsSingle fileDepends 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 on SERVICE_PORT=14001 (fails commit if the app can't reach Application started successfully within 60 s). Tests run in CI, not pre-commit — see Testing Guide.