APIZDocs

APIZ guide

Policy SDK

Use @apiz/policy-sdk to author typed request decisions. This package provides contract types, request/host interfaces, pure decision builders and a restricted TypeScript build command. Policy code runs in APIZ's goja evaluator after compilation; it is not a Node program or an HTTP management client.

Install

Use Node 24 and install the APIZ CLI. Confirm apiz is available on your PATH, then create a project and install from npm:

mkdir my-policy
cd my-policy
npm init -y
npm install --save-dev @apiz/policy-sdk
npm pkg set 'scripts.policy:build=apiz policy build --project .'
npm pkg set 'scripts.policy:test=apiz policy test --project . --explain'
mkdir fixtures

The npm scripts add the package's apiz-policy builder to PATH. You do not need Go, the APIZ source repository or the Client SDK. Build uses the installed APIZ CLI locally. Fixture tests run through your APIZ Control Plane and require a signed-in CLI context with permission to test Policies; they do not call the upstream service.

Tutorial: Create, Build And Test A Policy

Create these files in your my-policy project:

my-policy/
├── package.json
├── apiz-policy.json
├── policy.ts
└── fixtures/
    ├── allow.json
    └── deny.json

1. Define the decision

Save as policy.ts:

import { definePolicy } from "@apiz/policy-sdk";

export default definePolicy({
  version: "v1",
  decide(_ctx, api, request) {
    return request.method === "GET"
      ? api.allow("read_method")
      : api.deny("read_only");
  },
});

This deliberately small General API example allows GET and denies other methods. It is not a general read-only rule for protocols that use POST for reads.

2. Describe the project

Save as apiz-policy.json:

{
  "api_version": "apiz.io/v1",
  "kind": "PolicyProject",
  "metadata": {
    "name": "Read-only example",
    "description": "Allow GET and deny other methods."
  },
  "config": {
    "type": "scripted",
    "adapter_scope": "general"
  },
  "code": {
    "language": "typescript",
    "entry": "policy.ts"
  },
  "fixtures": [
    {
      "file": "fixtures/allow.json"
    },
    {
      "file": "fixtures/deny.json"
    }
  ]
}

3. Add positive and negative fixtures

Save as fixtures/allow.json:

{
  "metadata": {
    "key": "allow",
    "name": "Allow GET",
    "enabled": true,
    "adapter_id": "general"
  },
  "request": {
    "method": "GET",
    "path": "/items"
  },
  "expectation": {
    "decision": "allow",
    "reason": "read_method"
  }
}

Save as fixtures/deny.json:

{
  "metadata": {
    "key": "deny",
    "name": "Deny POST",
    "enabled": true,
    "adapter_id": "general"
  },
  "request": {
    "method": "POST",
    "path": "/items"
  },
  "expectation": {
    "decision": "deny",
    "reason": "read_only"
  }
}

Fixtures contain synthetic requests, never credentials. The expected decision and reason ensure the tests check behavior instead of only successful execution.

4. Build and execute the tests

Build locally:

npm run policy:build

Configure your deployment and sign in before running fixture tests:

apiz context set sdk-tutorial --server https://current-host
apiz context use sdk-tutorial
apiz login
apiz team list

Select the Team containing your API resources with apiz team use YOUR_TEAM_ID, replacing YOUR_TEAM_ID with its actual ID. Then run:

npm run policy:test

Expect a successful build and both fixtures passing: GET allows with read_method, POST denies with read_only. Build performs type checking, restricted-source checking, bundling, size/source-map checks and authoritative goja compilation. Test executes the fixtures; build success alone only establishes loadability.

5. Publish and verify

These commands do not publish, enable or attach the Policy. Follow the Scripted Policy tutorial for publication review, expected revisions, published tests and attachment, using my-policy as your project directory. Policy Studio in the Web Console uses the same types and authoritative compilation workflow.

Case: A Small Allow/Deny Rule

import { definePolicy } from "@apiz/policy-sdk";

export default definePolicy({
  version: "v1",
  decide(_ctx, api, request) {
    if (request.method !== "GET") {
      return api.deny("read_only");
    }
    return api.allow("read_method");
  },
});

This example only tests HTTP method, so it is not a universal read-only policy for protocols that use POST for reads. Choose the actual adapter and request model. Add fixtures for an allowed GET, denied write, malformed input and resource scope. An allow result does not skip mandatory Policies or final adapter validation.

Case: Change A Request

For a General API Policy, use the bounded request editor rather than modifying host objects or reaching into credentials:

import { definePolicy } from "@apiz/policy-sdk";

export default definePolicy({
  version: "v1",
  decide(_ctx, api, request) {
    const edit = request.edit();
    edit.headers.set("x-agent-workflow", "review");
    edit.then("allow");
    return api.patch("workflow_header", edit.commit());
  },
});

Reserved/authentication fields remain protected. APIZ revalidates accepted mutations before forwarding. Policies do not receive upstream responses and cannot rewrite streaming responses. Use the forbidden-mutation fixture to observe fail-closed behavior for a reserved field.

Case: Reply Or Select A Rate Limit

api.reply(reason, {status, headers, body}) returns a bounded synthetic response. api.rateLimit(reason, profileID) selects a configured rate-limit profile; it does not give JavaScript access to counters or Redis. Configure the profile first using the rate-limit guide, then use its actual ID and add fixtures for each branch.

Author API Reference

APIRole
definePolicy({version: "v1", decide})Typed single-entry definition
ctxSanitized adapter, identity, resource and request context
request.method/path/headers/query/bodyBounded logical request view
request.edit()Transactional request changes
api.allow/deny/reply/patch/rateLimitReturn a typed decision
api.match, api.hasTagWhitelisted decision helpers
Adapter helpersAvailable only through the registered adapter contract
Exported allow/deny/reply/patch/rateLimitPure builders useful in author tests

The generated schema is schema/v1; package version and Policy contract version are separate concepts. Do not infer runtime support from a TypeScript type alone.

What Cannot Run In A Policy

No networking, filesystem, environment access, timers, async work or arbitrary npm imports. @apiz/sdk/management remains forbidden even when both packages are installed together. Host key management belongs in your application, not in a Policy. Source/runtime failures deny access rather than falling back to allow.

Diagnose And Verify

FailureWhat to inspect
Type errorDefinition shape and the installed Policy contract
Restricted import/global errorRemove Node APIs/host SDK calls; express the decision through the whitelisted interfaces
Build succeeds, fixture failsExpected decision, sanitized request context and mutation assertions
Publication conflictsCurrent Draft/Published revisions; review instead of overwriting
Live request denied unexpectedlyAccess Logs and applied Policy order, adapter compatibility and final validation

Never put real credentials in fixtures. After enabling and attaching a published Policy, verify representative allowed and denied requests in Access Logs. See Policy fixtures and the full publication tutorial for those workflows.