Turuq Onboarding

Backend III

This section covers the essential backend technologies and architecture you'll be working with when using the NestJS framework. Review these documents to understand our server-side infrastructure and development practices for the Courier Integration Service.

Overview

  • NestJS is a progressive Node.js framework for building efficient, reliable, and scalable server-side applications. It is built with and fully supports TypeScript.
  • NestJS uses Express under the hood by default (or optionally Fastify), but provides a higher-level abstraction that enforces a structured, opinionated architecture inspired by Angular.

Why choose NestJS?

  • First-class TypeScript support with decorators and metadata reflection.
  • Structured, modular architecture that scales cleanly for large applications.
  • Powerful built-in dependency injection (DI) container for loosely coupled services.
  • Rich ecosystem of official modules (config, validation, queues, schedulers, etc.).
  • Excellent support for REST APIs, GraphQL, WebSockets, and microservices.

Core Concepts

Modules

NestJS applications are organized into Modules. Every app has at least one root module (AppModule) and can have any number of feature modules. Modules define providers, controllers, and imports/exports to encapsulate related functionality.

@Module({
  imports: [DatabaseModule],
  controllers: [OrdersController],
  providers: [OrdersService],
  exports: [OrdersService],
})
export class OrdersModule {}

Controllers

Controllers handle incoming HTTP requests and return responses. They use decorators like @Get(), @Post(), @Param(), and @Body() to define routes and extract request data.

Providers & Services

Providers (typically Services) contain the business logic and are injected into controllers and other services via NestJS's DI system. Annotate a class with @Injectable() to make it a provider.

Decorators

NestJS makes heavy use of TypeScript decorators to attach metadata to classes and methods. Common decorators include @Controller(), @Injectable(), @Module(), @Get(), @Post(), @Body(), @Param(), @Query(), and @UseGuards().

Routing & Middleware

  • Routes are defined declaratively using method decorators on controller methods.
  • Middleware in NestJS can be applied globally, per-module, or per-route via the configure() method in a module or using app.use().
  • NestJS Guards handle authentication and authorization checks before a route handler runs.
  • Interceptors wrap route handlers for cross-cutting concerns like logging, response transformation, and caching.
  • Pipes handle input validation and transformation (e.g., parsing route params, validating DTOs).

Validation

  • We use the class-validator and class-transformer libraries with NestJS's ValidationPipe to declaratively validate incoming request bodies using DTOs (Data Transfer Objects).
@Post()
async create(@Body() createOrderDto: CreateOrderDto) { ... }
  • Apply @IsString(), @IsNumber(), @IsEnum(), and other decorators on DTO class properties for automatic validation.

Authentication & Authorization

  • NestJS Guards implement the CanActivate interface and are used to protect routes.
  • In the Courier Integration Service we use Clerk as our third-party authentication provider via a custom ClerkAuthGuard that implements CanActivate. The guard verifies the incoming request's session token against Clerk's backend SDK before allowing access.
  • Apply the guard on a per-controller or per-route basis using @UseGuards(ClerkAuthGuard). In the CIS, every protected controller is guarded at the controller class level.
  • Clerk requires CLERK_PUBLISHABLE_KEY and CLERK_SECRET_KEY to be set in your environment — these are loaded by @nestjs/config and validated at startup.

Check out the Clerk Documentation for more detail on the backend SDK and token verification.

Configuration & Environment

  • The @nestjs/config module provides a ConfigService for type-safe access to environment variables, supporting .env files and custom configuration namespaces.

Error Handling

  • NestJS has built-in Exception Filters that catch errors and transform them into structured HTTP responses.
  • Throw built-in exceptions like NotFoundException, BadRequestException, or UnauthorizedException directly from services or controllers.
  • Create custom exception filters by implementing the ExceptionFilter interface for fine-grained error handling.

Testing

NestJS is designed with testability in mind and the CIS test suite reflects this thoroughly. Tests live inside __tests__/ directories co-located with their modules and are discovered via the *.spec.ts glob pattern.

Test runner & toolchain

  • Jest is the test runner (npm run test). Additional scripts: test:watch, test:cov (coverage), and test:e2e.
  • ts-jest transpiles TypeScript spec files on the fly — no separate build step required.
  • @nestjs/testing provides Test.createTestingModule() to spin up an isolated NestJS module context per test suite.

Unit test pattern

Each spec bootstraps a minimal TestingModule that includes only the controller or service under test, with all dependencies replaced by jest.fn() mocks:

const module: TestingModule = await Test.createTestingModule({
  controllers: [ShipmentController],
  providers: [
    { provide: ShipmentService, useValue: mockShipmentService },
    { provide: SchedulerRegistry, useValue: mockSchedulerRegistry },
  ],
})
  .overrideGuard(ClerkAuthGuard)          // bypass auth in tests
  .useValue({ canActivate: () => true })
  .compile();

Handling Clerk auth in tests

Because every production controller uses @UseGuards(ClerkAuthGuard), tests call .overrideGuard(ClerkAuthGuard).useValue({ canActivate: () => true }) on the module builder to short-circuit the guard without needing real Clerk credentials.

Fake timers

The CIS uses @nestjs/schedule for batch-flush timeouts. Tests control this with Jest's fake timer API:

jest.useFakeTimers();   // in beforeEach
jest.runAllTimers();    // advance timers to trigger timeout callbacks
await Promise.resolve(); // flush the microtask queue after timer fires
jest.useRealTimers();  // in afterEach

Spying on private logger

NestJS controllers expose a Logger instance as a private field. Tests reach it directly to assert on log output:

const loggerSpy = jest.spyOn(controller['logger'], 'error');
expect(loggerSpy).toHaveBeenCalledWith(expect.stringContaining('Error...'));

Cleanup

jest.clearAllMocks() is called in afterEach to reset call counts and return values between tests, preventing state leakage.

In-memory database (integration tests)

For tests that need real MongoDB behaviour, the project ships mongodb-memory-server, which spins up an ephemeral in-process MongoDB instance — no external database required.

Performance & Deployment

  • NestJS supports Fastify as an alternative HTTP adapter for higher throughput.
  • Applications are built with nest build and deployed as standard Node.js processes.
  • Works seamlessly with Docker and containerized deployment strategies.