# Minimum Templates for Custom Primitives

> **Prerequisite**: you arrived here because Step 0 of `SKILL.md` routed the task past the three
> reuse-first branches (upstream decorator, `@Auth` composite, `catalyst-field-schema`) and landed
> on "truly new primitive needed". If you have not walked that gate yet, go back — most tasks do NOT
> require a new primitive. The templates below are last-resort, not first-reach.

Each template is a minimum viable shape with catalyst-specific context baked in. Copy aggressively —
they are short on purpose — but preserve the dual-context override in Guards and Interceptors that
read the request, and preserve the "back to Step 0" reminder whenever you deviate from a template.

## Guard

```typescript
import { Api } from '@aurorajs.dev/core-back';
import { CanActivate, ExecutionContext, ForbiddenException, Injectable } from '@nestjs/common';

@Injectable()
export class YourGuard implements CanActivate {
  canActivate(context: ExecutionContext): boolean {
    const request = this.getRequest(context);
    // Domain check here — e.g., feature flag, rate limit, resource ownership.
    if (/* custom condition on request */ false) {
      throw new ForbiddenException('Reason the request is rejected');
    }
    return true;
  }

  // MANDATORY override: without this, the guard works for REST only
  // (or GraphQL only, depending on NestJS default). Catalyst exposes every
  // module over both; the helper returns the correct request object.
  getRequest<T = any>(context: ExecutionContext): T {
    return Api.getRequestFromExecutionContext(context);
  }
}
```

- Extending `AuthGuard('<strategy>')` instead of `CanActivate` (passport-based guards) also requires
  the `getRequest` override — see `AuthenticationJwtGuard` in `@app/authorization-code/guards/` for
  the canonical shape.
- Permission-aware guards read `request.user.dPermissions.all` and compare with
  `Arr.intersects(required, accountPermissions)` from `@aurorajs.dev/core-common`. See the
  `dPermissions.all` spotlight in `SKILL.md`.

## Interceptor

```typescript
import { Api } from '@aurorajs.dev/core-back';
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';

@Injectable()
export class YourInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const request = this.getRequest(context);
    const started = Date.now();
    return next.handle().pipe(
      tap(() => {
        const elapsed = Date.now() - started;
        // Domain side effect — e.g., metrics, tracing. Avoid audit logging:
        // the upstream @Auditing() decorator already covers that.
      }),
    );
  }

  // MANDATORY override for the same reason as Guard.
  getRequest<T = any>(context: ExecutionContext): T {
    return Api.getRequestFromExecutionContext(context);
  }
}
```

- If the interceptor does NOT read the request (pure cross-cutting timing, for example), the
  `getRequest` override is unnecessary. Read it once and decide before copying.

## Pipe

> **Probably not the right tool.** Field-level validation (type checks, `maxLength`, `nullable`,
> `enumOptions`, `rules`) belongs in the entity's `FieldSchema` and runs via `@ApplySchema` /
> `@Sanitize` / `@Format`. Before writing a pipe, re-read `catalyst-field-schema/SKILL.md`. A pipe
> is only justified for ad-hoc parameter transformation that has no FieldSchema equivalent (e.g.,
> coercing a query-string fragment to a domain-specific ID format).

```typescript
import { BadRequestException, Injectable, PipeTransform } from '@nestjs/common';

@Injectable()
export class YourPipe implements PipeTransform<string, SomeOutputType> {
  transform(value: string): SomeOutputType {
    // Domain transformation with informative error message.
    const result = /* parse, coerce, validate */ value;
    if (/* invalid */ false) {
      throw new BadRequestException(`Validation failed: <reason>`);
    }
    return result;
  }
}
```

- If you find yourself writing `if (value.length > 128) throw …`, stop — that is a `FieldSchema`
  `maxLength` rule. Go back to Step 0.

## Exception Filter

```typescript
import { ArgumentsHost, Catch, ExceptionFilter, HttpException } from '@nestjs/common';
import { Response } from 'express';

@Catch(HttpException)
export class YourExceptionFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const status = exception.getStatus();

    response.status(status).json({
      statusCode: status,
      timestamp: new Date().toISOString(),
      message: exception.message,
    });
  }
}
```

- Exception filters typically run on HTTP contexts. If you need a GraphQL-aware filter, implement
  `GqlExceptionFilter` from `@nestjs/graphql` instead. Dual-context filters are rare in catalyst;
  start with a specific-style filter and promote if the need arises.
- Catalyst does not currently author custom exception filters — the repo defers to framework
  defaults. Before writing one, consider whether the "exceptional case" is really a guard-level
  concern (forbidden / unauthorized) or a handler-level concern (`NotFoundException` /
  `ConflictException`) that the existing shape already covers.

## Custom Composite Decorator

Anchor: the real `@Auth` composite at `backend/src/@aurora/decorators/auth.decorator.ts`.

```typescript
import { Permissions } from '@aurorajs.dev/core-back';
import { applyDecorators, UseGuards } from '@nestjs/common';

import { YourPolicyGuard } from '@app/<bc>/<mod>/guards';

export function YourComposite(...params: string[]): MethodDecorator & ClassDecorator {
  return applyDecorators(
    Permissions(...params), // metadata hook (if the composite is permission-aware)
    UseGuards(YourPolicyGuard), // guard(s) the composite enforces
    // … further decorators as needed
  );
}
```

- The return type `MethodDecorator & ClassDecorator` lets the composite apply at either method or
  class level. `@Auth` uses this pattern so resolvers can do `@Resolver() @Auth('<permission>')` at
  class level and individual mutations do not need the decorator.
- If the composite does NOT need metadata (just guards), drop the `Permissions(...)` call.
- Keep the composite thin — it is a wiring helper, not a place for business logic.

## Custom Param Decorator

```typescript
import { Api } from '@aurorajs.dev/core-back';
import { createParamDecorator, ExecutionContext } from '@nestjs/common';

export const YourParam = createParamDecorator((data: unknown, ctx: ExecutionContext) => {
  const request = Api.getRequestFromExecutionContext(ctx);
  // Return the slice of `request` your parameter exposes.
  return request.someField;
});
```

Usage in a resolver or controller:

```typescript
async main(@YourParam() value: SomeType, …) { … }
```

- The `Api.getRequestFromExecutionContext(ctx)` call is MANDATORY if your param reads the request.
  Catalyst's upstream param decorators (`@Timezone()`, `@Auditing()`, `@CurrentAccount()`,
  `@ContentLanguage()`) all use the same helper — consistency matters.
- If the param does not read the request (e.g., it resolves from module-scoped state), the helper is
  unnecessary. But that is a rare pattern — most param decorators extract something from the
  request.

## Back to Step 0

After copying any template, walk back to `SKILL.md` Step 0 one more time and ask: **is this
primitive still necessary, or did implementing it reveal that upstream / `@Auth` / a FieldSchema
rule actually covers the case?** The cheapest refactor is the one you do before the code is
committed. If the primitive is the right answer, name it per the Naming Conventions table in
`SKILL.md` and place it in the correct directory.
