Saltar al contenido principal

Backend Architecture

The Ever Gauzy backend is built on NestJS, a progressive Node.js framework for building efficient, scalable server-side applications using TypeScript.

NestJS Foundation​

NestJS provides the architectural backbone with:

  • Dependency Injection (DI) β€” module-based inversion of control container
  • Decorators β€” declarative metadata for routes, guards, pipes, and more
  • Middleware Pipeline β€” composable request/response processing
  • Platform Agnostic β€” runs on Express or Fastify (Gauzy uses Express)

Module Organization​

The backend is organized into well-defined NestJS modules within packages/core/src/lib/:

Core Modules​

packages/core/src/lib/
β”œβ”€β”€ auth/ # Authentication (login, register, social OAuth)
β”œβ”€β”€ user/ # User management
β”œβ”€β”€ tenant/ # Tenant management and onboarding
β”œβ”€β”€ role/ # Role definitions (SUPER_ADMIN, ADMIN, EMPLOYEE, etc.)
β”œβ”€β”€ role-permission/ # Role-permission mappings
β”œβ”€β”€ organization/ # Organization CRUD and settings
β”œβ”€β”€ employee/ # Employee profiles, statistics, awards
β”œβ”€β”€ shared/ # Shared utilities, validators, base entities
β”œβ”€β”€ core/ # Core module registration
└── bootstrap/ # Application bootstrapping

Feature Modules​

packages/core/src/lib/
β”œβ”€β”€ time-tracking/ # Time logs, timesheets, activity tracking, screenshots
β”œβ”€β”€ tasks/ # Task management with statuses, priorities, sizes
β”œβ”€β”€ organization-project/ # Project management
β”œβ”€β”€ organization-sprint/ # Sprint management
β”œβ”€β”€ organization-project-module/ # Project modules
β”œβ”€β”€ invoice/ # Invoice generation and management
β”œβ”€β”€ expense/ # Expense tracking
β”œβ”€β”€ payment/ # Payment processing
β”œβ”€β”€ income/ # Income records
β”œβ”€β”€ candidate/ # Applicant tracking
β”œβ”€β”€ pipeline/ # Sales pipelines
β”œβ”€β”€ contact/ # Contact/lead management
β”œβ”€β”€ goal/ # Goals and objectives
β”œβ”€β”€ goal-kpi/ # Key Performance Indicators
β”œβ”€β”€ reports/ # Reporting and analytics
└── ... (148 modules total)

Infrastructure Modules​

packages/core/src/lib/
β”œβ”€β”€ database/ # Database connection and configuration
β”œβ”€β”€ graphql/ # GraphQL schema and resolvers
β”œβ”€β”€ health/ # Health check endpoints
β”œβ”€β”€ logger/ # Logging configuration
β”œβ”€β”€ i18n/ # Internationalization
β”œβ”€β”€ email-send/ # Email dispatch
β”œβ”€β”€ email-template/ # Email templates (Handlebars)
β”œβ”€β”€ export-import/ # Data export/import
β”œβ”€β”€ image-asset/ # Image/file management
β”œβ”€β”€ throttler/ # Rate limiting
β”œβ”€β”€ event-bus/ # Event bus integration
└── integration/ # Third-party integration base

CQRS Pattern​

The backend extensively uses Command Query Responsibility Segregation (CQRS):

Commands (Write Operations)​

// Command definition
export class CreateEmployeeCommand {
constructor(public readonly input: IEmployeeCreateInput) {}
}

// Command handler
@CommandHandler(CreateEmployeeCommand)
export class CreateEmployeeHandler
implements ICommandHandler<CreateEmployeeCommand>
{
constructor(private readonly employeeService: EmployeeService) {}

async execute(command: CreateEmployeeCommand): Promise<IEmployee> {
const { input } = command;
return this.employeeService.create(input);
}
}

Queries (Read Operations)​

// Query definition
export class FindEmployeesQuery {
constructor(public readonly options: FindManyOptions<Employee>) {}
}

// Query handler
@QueryHandler(FindEmployeesQuery)
export class FindEmployeesHandler implements IQueryHandler<FindEmployeesQuery> {
constructor(private readonly employeeService: EmployeeService) {}

async execute(query: FindEmployeesQuery): Promise<IPagination<IEmployee>> {
return this.employeeService.findAll(query.options);
}
}

Controller Integration​

@Controller("employee")
@UseGuards(TenantPermissionGuard)
export class EmployeeController {
constructor(
private readonly commandBus: CommandBus,
private readonly queryBus: QueryBus,
) {}

@Post()
@Permissions(PermissionsEnum.EMPLOYEES_EDIT)
async create(@Body() entity: CreateEmployeeDTO): Promise<IEmployee> {
return this.commandBus.execute(new CreateEmployeeCommand(entity));
}

@Get()
@Permissions(PermissionsEnum.EMPLOYEES_VIEW)
async findAll(
@Query() options: PaginationParams,
): Promise<IPagination<IEmployee>> {
return this.queryBus.execute(new FindEmployeesQuery(options));
}
}

Guard Architecture​

Guards enforce security at the controller level. They execute in order:

1. Authentication Guard (AuthGuard)​

Validates the JWT token and attaches the authenticated user to the request:

@UseGuards(AuthGuard('jwt'))

The @Public() decorator bypasses authentication for specific endpoints (e.g., registration, login).

2. Tenant Permission Guard (TenantPermissionGuard)​

Combines tenant resolution with permission checking:

  • Resolves the tenant from the authenticated user
  • Sets RequestContext.currentTenantId()
  • Validates the user has the required permissions
@UseGuards(TenantPermissionGuard)

3. Role Guard (RoleGuard)​

Restricts access based on user roles:

@Roles(RolesEnum.SUPER_ADMIN, RolesEnum.ADMIN)
@UseGuards(RoleGuard)

4. Permission Guard (PermissionGuard)​

Fine-grained permission checking:

@Permissions(PermissionsEnum.EMPLOYEES_EDIT)
@UseGuards(PermissionGuard)

Guard Hierarchy​

SUPER_ADMIN
└── ADMIN
└── DATA_ENTRY
└── EMPLOYEE
└── CANDIDATE
└── VIEWER

Service Layer​

Services contain the core business logic and data access patterns:

Base Service​

Most services extend CrudService<T>:

@Injectable()
export class EmployeeService extends CrudService<Employee> {
constructor(
@InjectRepository(Employee)
private readonly employeeRepository: Repository<Employee>,
) {
super(employeeRepository);
}

// CrudService provides: findAll, findOneByIdString, create, update, delete
// Custom methods add domain-specific logic
}

Tenant-Aware Services​

Services extending TenantAwareCrudService automatically scope all queries by tenantId:

@Injectable()
export class ProjectService extends TenantAwareCrudService<OrganizationProject> {
// All findAll/findOne/create/update/delete operations
// are automatically filtered by the current tenant
}

Request Context​

The RequestContext provides a thread-safe way to access request-scoped data anywhere in the application:

// Get current tenant
const tenantId = RequestContext.currentTenantId();

// Get current user
const userId = RequestContext.currentUserId();
const user = RequestContext.currentUser();

// Get current organization
const orgId = RequestContext.currentOrganizationId();

// Get current role
const roleId = RequestContext.currentRoleId();

API Documentation​

Swagger (OpenAPI)​

The API automatically generates Swagger documentation:

  • URL: http://localhost:3000/swg
  • JSON Spec: http://localhost:3000/swg-json
  • API Docs (Compodoc): http://localhost:3000/docs

API Versioning​

The API uses URL-based versioning:

  • Current: /api/ (v1 implicit)
  • All endpoints are prefixed with /api/

Error Handling​

The platform uses NestJS exception filters with standard HTTP exceptions:

throw new NotFoundException("Employee not found");
throw new BadRequestException("Invalid input");
throw new ForbiddenException("Insufficient permissions");
throw new UnauthorizedException("Token expired");

Custom exceptions extend HttpException with structured error responses.

Middleware Pipeline​

Request β†’ Logger Middleware β†’ Auth Guard β†’ Tenant Guard β†’ Permission Guard
β†’ Validation Pipe β†’ Controller β†’ Command/Query Handler β†’ Service
β†’ Response Interceptor β†’ Response