Skip to main content
Issue · AuthorizationTypeScript · NestJS · gRPCBuilt on Permify
A typed front-end for Permify

Authorization, written like the rest of your code.

Define your schema in TypeScript, push it from the CLI, and protect routes with a single decorator. One config file feeds your app, your scripts, and your CI — with no .perm drift to maintain.

$pnpm add @permify-toolkit/core @permify-toolkit/nestjscopy
In the box
3 packages
Setup
~ 5 minutes
Boilerplate
0 gRPC stubs
Type safety
end-to-end
01  Why this exists

A friendlier wrapper for the parts of Permify you'd rather not write twice.

/01
Schema, in TypeScript
Entities, relations, and permissions defined with a tiny DSL. IDE autocomplete, compile-time checks, and no .perm file to keep in sync.
core
/02
NestJS, in one decorator
Drop in PermifyModule, mark a controller with @CheckPermission(), and the guard handles AND / OR evaluation for you.
nestjs
/03
CLI for the boring parts
Push schemas, seed relationships, diff against the live tenant. The CLI reads your config — no flags, no extra scripts, no drift.
cli
/04
One config, three consumers
permify.config.ts is the single source of truth. Your runtime, the CLI, and your tests all read the same file.
core
/05
No gRPC paperwork
createPermifyClient() wires connectivity. checkPermission() and writeRelationships() hide the stubs.
core
/06
Production-ready defaults
TLS, auth tokens, custom timeouts, env-driven config, and async NestJS module init — without re-reading the gRPC docs.
prod
02  The five-minute tour

Define, push, decorate. That's the whole loop.

i.

Write your schema in TypeScript

Skip the .perm file. defineConfig gives you typed entities, relations, and permission expressions in the same project as your app code.

Configuration →
permify.config.tscopy
import { defineConfig, schema, entity,
  relation, permission } from "@permify-toolkit/core";

export default defineConfig({
  tenant: "t1",
  client: { endpoint: "localhost:3478", insecure: true },
  schema: schema({
    user: entity({}),
    document: entity({
      relations: { owner: relation("user"),
                    viewer: relation("user") },
      permissions: {
        edit: permission("owner"),
        view: permission("viewer or owner"),
      },
    }),
  }),
});
Terminalcopy
$ permify-toolkit schema push
 Pushed schema · tenant="t1" · v=17

$ permify-toolkit relationships seed \
    --file ./data/relationships.json
 Seeded 42 relationships in 240ms

$ permify-toolkit schema diff
  ~ document · permission "view"
    - owner
    + viewer or owner
ii.

Push it from your terminal

No flags for tenants, endpoints, or auth tokens — the CLI reads your config. diff shows you what's about to change before you run push.

CLI reference →
iii.

Protect a route in one line

Add @CheckPermission() with the resource, action, and a function to extract the resource id. The guard does the rest.

NestJS docs →
documents.controller.tscopy
@Get(":id")
@UseGuards(PermifyGuard)
@CheckPermission({
  resource: "document",
  action: "view",
  resourceId: (req) => req.params.id,
})
findOne(@Param("id") id: string) {
  return this.documentsService.findOne(id);
}
03  What's in the box

Three small packages. Pick what you need.

@permify-toolkit/coreSDK

The schema DSL, a typed Permify client, and a shared config loader. Use this on its own if you don't run NestJS.

$pnpm add @permify-toolkit/corecopy
Docs →
@permify-toolkit/nestjsframework

A NestJS module, a guard, and the @CheckPermission() decorator with AND / OR logic.

$pnpm add @permify-toolkit/nestjscopy
Docs →
@permify-toolkit/clicli

Schema push, relationship seeding, and a few other chores. Reads the same permify.config.ts.

$pnpm add @permify-toolkit/clicopy
Docs →

If it saves you an afternoon, that's the whole point.

Originally built to stop re-writing the same Permify wiring across NestJS projects. Open source, MIT, and shaped by issues from people using it.

"Permify is great, but using it from a Node app often ends up being more work than it should be. permify-toolkit is what I wish I had from the start."
— Nikhil · author