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:
- Reads the permission point code from route metadata via
Reflector. - Throws
ForbiddenExceptionif no permission point is set. - Extracts the request via
getRequestFromContext(supports HTTP, GraphQL, and WebSocket execution contexts). - Calls
buildPermissionRequestandcheckPermissionon the injectedArkveilclient. - Throws
ForbiddenExceptionif 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";