Frontend Architecture
The frontend is a Next.js 16 application using the App Router, Apollo Client for GraphQL, custom TW* components with Tailwind CSS v4, and Zustand for client-side state.
Route Groups
The app/ directory uses Next.js route groups to segment by access level:
| Route Group | Path | Purpose |
|---|---|---|
(visitor) | /about, /jobs, /pricing, etc. | Public pages, no auth |
(shared) | /userprofile, /cvbuilder | Authenticated, role-agnostic |
company | /company/* | Employer-only (requires selectedCompanyId) |
jobseeker | /jobseeker/* | Job seeker pages |
auth | /auth/signin, /auth/signup | Authentication flows |
onboarding | /onboarding/* | First-time user setup |
admin | /admin/* | Super admin dashboard |
Provider Hierarchy
Providers are nested in app/providers.tsx. Order matters -- each layer depends on those above it:
WebSocketProvider is initialized with autoConnect={false}. Components that need WebSocket must call connect() explicitly.
State Management
| Layer | Tool | Scope |
|---|---|---|
| Server cache | Apollo Client (cache-and-network) | GraphQL query results |
| Auth state | React Context (UserAuthProvider) | JWT, user profile, role |
| Page settings | Zustand (usePageStore) | View mode, pagination, field visibility |
| Modal state | React Context (ModalContext) | Global modal open/close |
| Theme | React Context (ColorThemeProvider) | CSS variable injection |
Zustand Store (lib/pageStore.ts)
Persists page-specific preferences in localStorage under key aiqlick-page-store. State is keyed by userId:pathname so each user gets independent settings per page.
Stored settings include: view mode (grid/list/table), pagination offsets, field visibility toggles, and sort preferences.
Path Aliases
Configured in tsconfig.json. Key mappings:
| Alias | Maps to | Alias | Maps to |
|---|---|---|---|
@/* | ./ (root) | @graphql/* | ./graphql/operations/* |
@components/* | ./components/* | @apollo/* | ./graphql/apollo/* |
@hooks/* | ./lib/hooks/* | @utils/* | ./lib/utils/* |
@ux/* | ./components/ux/* | @reusable/* | ./components/reusable/* |
@contexts/* | ./contexts/* | @config/* | ./lib/config/* |
Build Pipeline
| Mode | Bundler | Command |
|---|---|---|
| Development | Turbopack | npm run dev (port 4000) |
| Production | Webpack | npm run build |
Layout Pattern
The app uses a flex-based overflow model to prevent double-scrollbar issues:
Environment Variables
| Variable | Purpose |
|---|---|
NEXT_PUBLIC_API_URL | Backend GraphQL endpoint (also AI gateway HTTP + voice WS path) |
NEXT_PUBLIC_GRAPHQL_WS_URL | Backend GraphQL subscriptions (also subscribeAi for AI streaming) |
NEXT_PUBLIC_IMAGE_BASE_URL | S3/CloudFront storage URL |
NEXT_PUBLIC_APP_URL | App base URL (metadata, redirects) |
Apollo Client Configuration
Defined in graphql/apollo/apolloClient.ts. The client uses a six-link chain:
authLink --> logLink (dev only) --> errorLink --> split(uploadLink | retryLink --> httpLink)
| Link | Purpose |
|---|---|
| authLink | Reads the JWT from localStorage and attaches it as Authorization: Bearer <token> |
| logLink | Logs GraphQL operation names and durations in development mode |
| errorLink | Catches 401 / UNAUTHENTICATED errors and redirects to /auth/signin with token cleanup |
| uploadLink | Custom multipart/form-data link for file uploads; bypasses retry to avoid timeout on large files |
| retryLink | Retries failed requests with exponential backoff |
| httpLink | Standard HTTP transport to the backend GraphQL endpoint |
| Setting | Value |
|---|---|
| Timeout | 90 seconds (APOLLO_TIMEOUT) |
| Default fetch policy | cache-and-network |
| Auth error redirect | /auth/signin |
Authentication Flow
- User submits credentials via the
signInmutation. - JWT token is stored in
localStorage. authLinkattaches the token to every subsequent GraphQL request.- A 5-hour inactivity timer triggers automatic logout.
- On 401 or
UNAUTHENTICATEDerror,errorLinkclears the token and redirects to sign-in.
GraphQL Operations
Operations are organized into 34+ feature directories under graphql/operations/:
graphql/operations/
admin/ ai/ aiAgent/ badge/
billingConfig/ buzzfeed/ candidate/ collaboratingPartners/
companies/ contacts/ credit/ cv/
dashboard/ interview/ job/ jobSeekerInvitations/
meetingInsights/ messaging/ notification/ payment/
pipeline/ promo-code/ role/ schema/
sharelist/ skills/ talentPool/ talents/
types/ user/ ...
Each directory contains queries, mutations, and subscriptions specific to that feature. Reusable fragments are centralized in graphql/operations/schema/ by entity (USER_FIELDS, COMPANY_FIELDS, JOB_FIELDS, TALENT_FIELDS, CANDIDATE_FIELDS, CV_FIELDS, etc.).
AI Operations via the NestJS Gateway
The frontend has no direct connection to background-tasks. Every AI operation (CV extraction, job parsing, agent chat, voice, document processing, etc.) routes through the NestJS gateway over the same Apollo client + WebSocket connection used by the rest of the backend. Three GraphQL roots cover everything:
| Root | Used for |
|---|---|
subscribeAi(input: AiInput!) | All streaming AI operations (10 ops registered server-side) |
queryAi(input: AiHttpInput!) | All AI HTTP queries (agent CRUD, conversation list, tool definitions, …) |
mutateAi(input: AiHttpInput!) | All AI HTTP mutations |
Helpers in lib/ai/aiGateway.ts (subscribeAiOp, aiGatewayRequest) absorb the envelope-unwrap boilerplate so each AI hook stays small. See NestJS AI Gateway for the full architecture, the operation registries, and how to add a new AI operation.
Deployment
Deployed via AWS Amplify with auto-build on push:
| Branch | Stage | Domain |
|---|---|---|
dev | Development | dev.aiqlick.com |
main | Production | www.aiqlick.com |
Secrets are fetched from AWS Secrets Manager during the Amplify preBuild phase via scripts/fetch-amplify-secrets.sh.