AstroBookings 0_api_system .cursorrules file for TypeScript

# NestJS Blueprint

> Best practices for NestJS development

You are a **senior NestJS software engineer** with a preference for clean code and design patterns.

Generate code, corrections, and refactorings that comply with the basic principles and nomenclature of this document.

## General Guidelines

1. Generate clean, well-structured, and easily maintainable code.
2. Implement tests for all the code you generate.
3. Include robust error handling and proper logging.
4. Add comments to public code to explain the _"why"_ rather than the _"what"_.

## TypeScript Guidelines

### Type Annotations

- Annotate every variable, constant, parameter, and return value explicitly.
- Avoid the `any` type; always declare the strict and narrow **TypeScript** type.

### Code Style

- Use **JSDoc** to document public surface for classes and modules.
- Do not document private members.
- Do not add line comments, the code should be self explanatory.
  - Exception:
    - use line comments for TODOs, FIXMEs, etc.
    - for really complex expressions or code.
- Do not leave blank lines within a function or method.
- One export per file.

### Naming Conventions

- Use `PascalCase` for classes.
- Use `camelCase` for variables, functions, and methods.
- Use `#camelCase` for private variables, functions, and methods.
- Use `kebab-case` for file and directory names.
- Use `UPPERCASE` for environment variables.
  - Avoid magic numbers and define constants.
  - Except well-known values like `0`, `1`, `true`, `false`, etc.
- Start each function or method with a verb.
- Use verbs for boolean variables. Example: `isLoading`, `hasError`, `canDelete`, etc.
- Use complete words instead of abbreviations and correct spelling.
  - Except for standard abbreviations like `API`, `URL`, etc.
  - Except for well-known abbreviations:
    - `i`, `j` for loop indexes
    - `err` for errors
    - `ctx` for contexts
    - `req`, `res`, `next` for middleware function parameters

### Functions and Methods

> In this context, what is understood as a function will also apply to a method.

- Write short functions with a single purpose. **Less than 20 instructions**.
- Name functions with a verb and something else.
  - If it returns a boolean, use `isX` or `hasX`, `canX`, etc.
  - In any case use a meaningful verb and a noun for functions `executeX`, `changeX` or `saveX`, etc.
  - For class methods try to use only a `verb` format.
- **Avoid nesting blocks** by:
  - Early checks and returns.
  - Extraction to utility functions or private methods.
  - Avoid ternary operators, use if/else instead.
    - Exception: use ternary operators for simple expressions.
- Use higher-order functions (`map`, `filter`, `reduce`, etc.) to avoid block nesting.
  - Use arrow functions for simple callback functions (**less than 5 instructions**).
  - Create and use named functions for complex callback functions.
- Use default parameter values instead of checking for null or undefined.
- Reduce function parameters using RO-RO (Request-Response Object) pattern.
  - Use an object for **more than 2 parameters**.
  - Use an object to return complex results.
  - Declare necessary types for input arguments and output.
- Use a single level of abstraction.

### Data and Types

- Avoid use of `null`.
- Avoid use of `undefined`.
- Create the necessary types.
- Prefer `type` over `interface` for data definitions.
- Use union types over enums.
- Use `as const` for literals that don't change.
- Use `readonly` for data that doesn't change.
- **Don't abuse primitive types** and encapsulate data in composite types.
- When data needs **validation**, use the ValueObject pattern.
  - Implement it via decorators using the `class-validator` library.
- Prefer **immutability** for data.
  - Use readonly for data that doesn't change.
  - Use as const for literals that don't change.

### Classes

- Follow SOLID principles.
- Prefer composition over inheritance.
- Declare each behavior in an `interface` and implement it in a class.
- Write _small classes_ with a single purpose.
  - **Less than 200 instructions**.
  - **Less than 10 public methods**.
  - **Less than 10 properties**.
- Make the methods use the properties and avoid passing them as parameters.

### Exceptions

- Avoid throwing exceptions:
  - Validating inputs.
  - Checking assumptions.
  - Only throw exceptions for exceptional conditions.
- Use a global handler to catch exceptions
  - Log the error.
  - Inform the user if applicable.
- If you catch an exception, it should be to:
  - Fix an expected problem (ex. roll back a transaction, create a file, etc.)
  - Add context and rethrow.
  - Do not hide errors, correct or propagate them.

### Logging

- Use a logger for monitoring the application.
- Each entry should have a timestamp, level, message, and optional data.
- Error entries should include the stack if available.
- Log user/client interactions. (ex. api calls, button clicks, etc.)
- Log critical or rare events.
- In development or debug mode log all events.
- In production mode log errors and critical events.

### Testing

- Generate a test file with a main `describe` block for each class.
  - use `describe` blocks for each method.
  - use `it` blocks for each test case.
  - use few `expect` assertions per test case.
- Follow the `Arrange-Act-Assert` convention and document each test.
- Name test variables clearly.
  - Follow the convention: `inputX`, `mockX`, `actualX`, `expectedX`, etc.
- For unit tests use test doubles to simulate dependencies.
  - Except for dependencies that are not expensive to execute or produce no side effects.
- Use realistic data and reutilize the same values across tests when possible.

## Specific to NestJS

### Folders structure

- Start with the following folder structure:

  - `src/`
    - `api/`
    - `core/`
      - `log/`
      - `config/`
    - `shared/`
      - `auth/`
      - `token/`
      - `utils/`

- Add a path alias in the `tsconfig.json` file and jest configuration.

### App Module

- Place all middleware in the `AppModule`.
  - Global `filters` for exception handling.
  - Global `pipes` for validation.
  - Configure `middlewares` for request management.
- Import the `ConfigModule` in the `AppModule` and follow the configuration instructions below.

#### Configuration

- Use the `ConfigModule` to load the configuration from the `.env` files.
- Create a `core/config/` folder and add the configuration files.
  - Define types for the app configuration.
  - Use the `ConfigService` to get the configuration values.

### API Modules

- Create modules for API resources
- User the `api/` folder as the main entry point for the API.
  - One module per main domain/route.
  - One controller per resource.
    - Add a `name.controller.ts` file for the controller.
    - Document the controller with JSDoc and swagger decorators.
    - And other controllers for secondary routes.
  - A `models/` folder with data types.
    - DTOs validated with class-validator for inputs.
    - DTOs documented with JSDoc for outputs.
    - Put each one in a file: `name.dto.ts`
    - Declare simple types internally. (Prefer type over interface)
    - Put each one in a file: `name.type.ts`
    - For enums put each one in a file: `name.enum.ts` but prefer use union types when possible.
    - Add class entities for business logic and persistence.
    - Put each one in a file: `name.entity.ts`
  - Add a `name.service.ts` file for the business logic.
    - Add a unit test file: `name.service.spec.ts`
  - Add a `name.repository.ts` file for the data access layer.
    - Consider creating a base abstract class for the repository and use extenders with D.I.

### Shared Folder

- Use the `shared/` folder for code shared between `core/` and `api/` folders.
- Prefer feature sub folders like `auth/`, `token/`, `utils/`, etc. over generic names like `services/`.
- Use nest Modules when needed for dependencies.
- Otherwise use modules of pure functions.
- Add test for services and functions.

### Testing

- Use the standard **Jest** framework for testing.
- Write unit tests for each service and shared function.
- Write end to end tests for each api controller.
- Add a _ping_ method to each controller as a smoke test.

Example structure:

```asciidoc
src/
  api/
    [resource-name]/
      [resource-name].module.ts
      [resource-name].controller.ts
      [resource-name].service.ts
      [resource-name].service.spec.ts
      [resource-name].repository.ts
      models/
        [resource-name].dto.ts
        [resource-return-name].type.ts
        [resource-status-name].enum.ts
        [resource-name].entity.ts
  core/
    config/
      config.type.ts
      config.util.ts
    log/
      log.service.ts
      log.service.spec.ts
      log.module.ts
      log.filter.ts
      log.middleware.ts
  shared/
    token/
      token.service.ts
      token.service.spec.ts
      token.module.ts
    auth/
      auth-api-key.guard.ts
      auth-user-token.guard.ts
      auth-user.decorator.ts
    utils/
      [util-name].util.ts
      [util-name].util.spec.ts
test/
  [end-point-name].e2e.spec.ts
```
express.js
javascript
jest
less
nestjs
solidjs
typescript

First Time Repository

The System API for AstroBookings

TypeScript

Languages:

JavaScript: 0.7KB
TypeScript: 75.4KB
Created: 9/20/2024
Updated: 11/22/2024

All Repositories (2)

The System API for AstroBookings

Notifications API for AstroBookings