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
| API | Role |
|---|---|
definePolicy({version: "v1", decide}) | Typed single-entry definition |
ctx | Sanitized adapter, identity, resource and request context |
request.method/path/headers/query/body | Bounded logical request view |
request.edit() | Transactional request changes |
api.allow/deny/reply/patch/rateLimit | Return a typed decision |
api.match, api.hasTag | Whitelisted decision helpers |
| Adapter helpers | Available only through the registered adapter contract |
Exported allow/deny/reply/patch/rateLimit | Pure 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
| Failure | What to inspect |
|---|---|
| Type error | Definition shape and the installed Policy contract |
| Restricted import/global error | Remove Node APIs/host SDK calls; express the decision through the whitelisted interfaces |
| Build succeeds, fixture fails | Expected decision, sanitized request context and mutation assertions |
| Publication conflicts | Current Draft/Published revisions; review instead of overwriting |
| Live request denied unexpectedly | Access 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.