Arkveil
SDK

NestJS SDK (@arkveil/nest)

Declarative ABAC permission checks for NestJS via a module, guard, and decorator.

Install

npm install @arkveil/nest

@arkveil/nest depends on the core arkveil package internally. Peer dependencies: @nestjs/common, @nestjs/core (^8^11), and reflect-metadata. @nestjs/graphql is an optional peer, imported lazily only when a GraphQL execution context is encountered.

Module setup

import { Module } from "@nestjs/common";
import { ArkveilModule } from "@arkveil/nest";

@Module({
  imports: [
    ArkveilModule.forRoot({
      serviceUrl: "https://api.arkveil.com",
      apiKey: "your-api-key",
      getUserAttributes: (req) => ({ id: req.user?.id }),
    }),
  ],
})
export class AppModule {}

ArkveilModule is a global module. ArkveilModuleOptions extends the core ArkveilParams — see the core SDK constructor options for the full list.

Async configuration

ArkveilModule.forRootAsync({
  imports: [ConfigModule],
  inject: [ConfigService],
  useFactory: (config: ConfigService) => ({
    serviceUrl: config.get("ARKVEIL_SERVICE_URL"),
    apiKey: config.get("ARKVEIL_API_KEY"),
  }),
});

The module provides and exports the core Arkveil client as an injectable, so you can inject it directly for row-level protection (see below).

Protecting routes

import { Controller, Get } from "@nestjs/common";
import { PermissionPoint } from "@arkveil/nest";

@Controller("articles")
export class ArticlesController {
  @Get("/admin")
  @PermissionPoint("content-service.article-delete")
  adminAction() {
    return "Protected content";
  }
}

@PermissionPoint(code) combines SetMetadata with UseGuards(PermissionPointGuard). The guard:

  1. Reads the permission point code from route metadata via Reflector.
  2. Throws ForbiddenException if no permission point is set.
  3. Extracts the request via getRequestFromContext (supports HTTP, GraphQL, and WebSocket execution contexts).
  4. Calls buildPermissionRequest and checkPermission on the injected Arkveil client.
  5. Throws ForbiddenException if the check fails or the request isn't granted.

Because the guard throws a standard ForbiddenException, you can handle denials with a normal NestJS exception filter:

@Catch(ForbiddenException)
export class ForbiddenExceptionFilter implements ExceptionFilter {
  catch(exception: ForbiddenException, host: ArgumentsHost) {
    // custom response shaping
  }
}

Typed permission points

If you don't want to use the global type registries (see Typed codes & attributes), bind a decorator to an explicit code union instead:

import { createPermissionPoint } from "@arkveil/nest";

type MyCodes = "content-service.article-delete" | "user-service.user-create";

const PermissionPoint = createPermissionPoint<MyCodes>();

Row-level data protection

Inject the core Arkveil client to call buildReadCondition and buildWriteChecks directly:

@Injectable()
export class PaymentsService {
  constructor(private readonly arkveil: Arkveil) {}

  async listForUser(user: ArkveilUser) {
    const { readCondition } = await this.arkveil.buildReadCondition({
      datasetCode: "billing.public.payments",
      user,
      context: {},
    });
    // ... use readCondition in your query
  }
}

See Row-level data protection for the full contract of buildReadCondition and buildWriteChecks.

Exports

import {
  ArkveilModule,
  PermissionPointGuard,
  PermissionPoint,
  createPermissionPoint,
  getRequestFromContext,
} from "@arkveil/nest";

import type { ArkveilModuleOptions } from "@arkveil/nest";

// Re-exported from the core `arkveil` package:
import {
  normalizeDatasetCode,
  substituteIds,
  IDS_PLACEHOLDER,
  METADATA_MISSING,
  MODE_UNAVAILABLE,
} from "@arkveil/nest";

On this page