npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@arkveil/nest

v0.2.1

Published

NestJS SDK for Arkveil — declarative ABAC permission checks via decorators and guards.

Readme

@arkveil/nest

Installation

npm install @arkveil/nest
# or
yarn add @arkveil/nest
# or
pnpm add @arkveil/nest

Features

  • 🔒 Declarative Permission Checks - Use decorators to protect your endpoints
  • 🌐 Global Module - Configure once, use everywhere
  • 🔄 Async Configuration - Support for async configuration with dependency injection
  • 📡 Multi-Protocol Support - Works with HTTP, GraphQL, and WebSocket contexts
  • 🎯 Type-Safe - Full TypeScript support with type definitions

Quick Start

1. Configure the Module

Option A: Synchronous Configuration

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,
        email: req.user?.email,
        role: req.user?.role,
      }),
      getContextAttributes: (req) => ({
        ip: req.ip,
        userAgent: req.headers["user-agent"],
      }),
    }),
  ],
})
export class AppModule {}

Option B: Async Configuration

import { Module } from "@nestjs/common";
import { ConfigModule, ConfigService } from "@nestjs/config";
import { ArkveilModule } from "@arkveil/nest";

@Module({
  imports: [
    ConfigModule.forRoot(),
    ArkveilModule.forRootAsync({
      imports: [ConfigModule],
      useFactory: (configService: ConfigService) => ({
        serviceUrl: configService.get("ARKVEIL_SERVICE_URL"),
        apiKey: configService.get("ARKVEIL_API_KEY"),
        getUserAttributes: (req) => ({
          id: req.user?.id,
          email: req.user?.email,
          role: req.user?.role,
        }),
      }),
      inject: [ConfigService],
    }),
  ],
})
export class AppModule {}

2. Protect Your Endpoints

Use the @PermissionPoint decorator to protect your endpoints:

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

@Controller("articles")
export class ArticlesController {
  @Get()
  @PermissionPoint("content-service.article-read")
  getAllArticles() {
    return "List of articles";
  }

  @Post()
  @PermissionPoint("content-service.article-create")
  createArticle() {
    return "Article created";
  }

  @Delete(":id")
  @PermissionPoint("content-service.article-delete")
  deleteArticle() {
    return "Article deleted";
  }

  @Get("/admin")
  @PermissionPoint("content-service.admin-access")
  adminAction() {
    return "Admin content";
  }
}

Typed Codes & Attributes

Get autocomplete and compile-time checking for the code passed to @PermissionPoint (and for user/context attributes). Generate the file with the Arkveil CLI (arkveil generate typescript -o src/arkveil.generated.ts) and register it once via declaration merging:

// arkveil.generated.ts — generated by `arkveil generate typescript`
export type ArkveilCodes =
  "content-service.article-read" | "content-service.article-delete";

declare module "arkveil" {
  interface ArkveilCodeRegistry {
    codes: ArkveilCodes;
  }
  // ...also augments ArkveilUserRegistry / ArkveilContextRegistry
}

That's all — @PermissionPoint is now typed everywhere:

@PermissionPoint("content-service.article-delete") // ✅ autocompletes
@PermissionPoint("nope") // ❌ compile error

If you'd rather not augment globally, build a typed decorator from an explicit union instead:

import { createPermissionPoint } from "@arkveil/nest";
import type { ArkveilCodes } from "./arkveil.generated";

// Re-export this and use it in place of the built-in PermissionPoint.
export const PermissionPoint = createPermissionPoint<ArkveilCodes>();

Configuration Options

ArkveilModuleOptions

| Option | Type | Required | Description | | ---------------------- | ---------- | -------- | -------------------------------------------- | | serviceUrl | string | Yes | The URL of your Arkveil service | | apiKey | string | Yes | Your Arkveil API key | | version | string | No | API version (default: "v1") | | timeout | number | No | Request timeout in milliseconds | | retryAttempts | number | No | Number of retry attempts for failed requests | | logger | Logger | No | Custom logger instance | | getUserAttributes | Function | No | Extract user attributes from request | | getContextAttributes | Function | No | Extract context attributes from request | | onDenied | Function | No | Custom handler for denied access |

Advanced Usage

Custom User Attribute Extraction

ArkveilModule.forRoot({
  serviceUrl: "https://api.arkveil.com",
  apiKey: "your-api-key",
  getUserAttributes: (req) => ({
    // Custom logic to extract user attributes
    id: req.headers["x-user-id"] || req.user?.id,
    role: req.user?.role,
  }),
});

Adding Context Attributes

ArkveilModule.forRoot({
  serviceUrl: "https://api.arkveil.com",
  apiKey: "your-api-key",
  getContextAttributes: (req) => ({
    ip: req.ip,
    userAgent: req.headers["user-agent"],
    timestamp: new Date().toISOString(),
    organizationId: req.user?.organizationId,
  }),
});

Custom Denied Handler

ArkveilModule.forRoot({
  serviceUrl: "https://api.arkveil.com",
  apiKey: "your-api-key",
  onDenied: (req, res) => {
    // Custom logic when access is denied
    res.status(403).json({
      error: "Access Denied",
      message: "You do not have the required permissions",
      requestId: req.id,
    });
  },
});

GraphQL Support

The @PermissionPoint decorator works seamlessly with GraphQL resolvers:

import { Resolver, Query, Mutation } from "@nestjs/graphql";
import { PermissionPoint } from "@arkveil/nest";

@Resolver()
export class ArticleResolver {
  @Query(() => [Article])
  @PermissionPoint("content-service.article-read")
  articles() {
    return this.articleService.findAll();
  }

  @Mutation(() => Article)
  @PermissionPoint("content-service.article-create")
  createArticle(@Args("input") input: CreateArticleInput) {
    return this.articleService.create(input);
  }
}

Using the Guard Directly

If you need more control, you can use the guard directly:

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

@Controller("articles")
@UseGuards(PermissionPointGuard)
export class ArticlesController {
  @Get()
  getAllArticles() {
    return "List of articles";
  }
}

Error Handling

The SDK throws standard NestJS exceptions:

  • ForbiddenException - When the permission point is missing, the check is denied, or the check fails (fail-closed)

You can handle these using NestJS exception filters:

import {
  ExceptionFilter,
  Catch,
  ArgumentsHost,
  ForbiddenException,
} from "@nestjs/common";

@Catch(ForbiddenException)
export class ForbiddenExceptionFilter implements ExceptionFilter {
  catch(exception: ForbiddenException, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse();

    response.status(403).json({
      statusCode: 403,
      message: "Access Denied",
      timestamp: new Date().toISOString(),
    });
  }
}

How It Works

  1. The @PermissionPoint decorator marks an endpoint with a permission action ID
  2. When a request comes in, the PermissionPointGuard intercepts it
  3. The guard extracts user information from the request
  4. It sends a permission check request to the Arkveil service
  5. If permission is granted, the request proceeds; otherwise, a ForbiddenException is thrown

Request Flow

Request → @PermissionPoint Decorator → PermissionPointGuard → Arkveil Service → Permission Check → Endpoint Handler

Row-level data protection

The module provides the core Arkveil client, so you can inject it and use the data-protection methods — buildReadCondition (a SQL condition to AND into your SELECTs) and buildWriteChecks (a boolean statement to run inside a mutation's transaction):

import { Injectable } from "@nestjs/common";
import { Arkveil } from "arkveil";

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

  async listPayments(user: UserAttributes) {
    const { readCondition } = await this.arkveil.buildReadCondition({
      datasetCode: "billing.public.payments",
      user,
      context: {},
      alias: "p",
    });
    return this.db.query(`SELECT * FROM payments p WHERE ${readCondition}`);
  }
}

See the arkveil core README for the full contract, including when the write check must run relative to CREATE/UPDATE/DELETE, the {{ids}} template helper, and the fail-closed semantics.

Best Practices

  1. Always configure getUserAttributes - This is how user identity and attributes reach the permission check
  2. Use meaningful action IDs - Follow a consistent naming pattern (e.g., service.resource.action)
  3. Add context attributes - Include relevant information like IP, organization, etc.
  4. Handle exceptions gracefully - Use exception filters for better error handling
  5. Test permissions - Write unit tests for your permission logic

Troubleshooting

User attributes are empty in the permission check

Make sure your authentication middleware/guard runs before the Arkveil guard so that req.user is populated, and that getUserAttributes reads from it:

ArkveilModule.forRoot({
  // ...
  getUserAttributes: (req) => ({ id: req.user?.id }),
});

"Permission check failed"

Check that:

  • Your Arkveil service URL is correct
  • Your API key is valid
  • The action ID exists in your Arkveil configuration

GraphQL context issues

Make sure your GraphQL module is configured to pass the request:

GraphQLModule.forRoot({
  context: ({ req }) => ({ req }),
});

License

MIT