Auth & middlewares
There are two ways to shape a request before it goes out (and inspect what comes back):
- Declarative auth: pick bearer or API key, point it at a variable. No code.
- Scripts: TypeScript functions API Craft runs in a sandbox: custom auth (compute a
token dynamically) and middlewares (
before/afterhooks that mutate the request or the response).
Scripts share one small typed SDK and run inside the app: there is nothing to install to run them. See The script SDK below for editor autocomplete.
Declarative auth
API Craft supports two auth types out of the box: bearer tokens and API keys. Auth can be
set as an API-wide default (merged into new requests) or overridden on an individual instance. Use
{{variables}} so the actual secret comes from an environment,
never the committed file.
API defaults
Set a default once and it's applied when creating new requests:
# Bearer token
craftr api set auth bearer "{{apiKey}}" -a petstore-api
# API key (header or query). Petstore reads the `api_key` header
craftr api set auth api-key --in header --name api_key --value "{{apiKey}}" -a petstore-api
# Clear it
craftr api unset auth -a petstore-api
You can do the same in the app's API settings.
Per-request auth
An instance's auth field overrides the default. In the request editor's Auth tab, pick
the type and reference a variable for the secret.

On disk:
{ "type": "bearer", "token": "{{apiKey}}" }
{ "type": "apiKey", "in": "header", "name": "api_key", "value": "{{apiKey}}" }
The secret variable (apiKey) is declared in the environment schema as secret: true, so
its value lives in the gitignored *.secrets.env file.
Custom auth
When a static or {{variable}} token isn't enough (you need to call a login endpoint, sign a
request, or refresh a credential), set the auth type to custom and point it at a script.
The script is an exported function in the API's middlewares/ folder. It receives the request
before it's sent and mutates it in place:
// apis/petstore-api/middlewares/petstore-auth.ts
import type { BeforeRequestContext } from '@apicrafthq/script-sdk';
export async function fetchToken(ctx: BeforeRequestContext) {
let token = await ctx.cache.get<string>('token');
if (!token) {
const res = await ctx.http.post('https://petstore.example/auth', {
body: { key: ctx.var('apiKey') },
});
token = (res.body as { token: string }).token;
await ctx.cache.set('token', token);
}
ctx.request.headers['Authorization'] = `Bearer ${token}`;
}
On disk the request references it by name and export:
{ "type": "custom", "ref": { "name": "petstore-auth", "exportName": "fetchToken" } }
name is the file (without .ts), exportName the exported function inside it; they are
independent, so one file can hold several auth or middleware functions.
The context gives you ctx.var() to read the environment, ctx.http for auxiliary calls,
ctx.cache for a persistent key/value store shared across the project's scripts, and
ctx.logger for output. Full types are in The script SDK.
Middlewares
A middleware is a hook in the request pipeline:
beforeruns before the request is sent. Mutatectx.request(method, url, headers, query, body) in place.afterruns once the response is back.ctx.requestis read-only; mutatectx.response(status, statusText, headers, body).
Like custom auth, middlewares are exported functions living in the API's middlewares/ folder;
one file can export several:
// apis/petstore-api/middlewares/tracing.ts
import type { BeforeRequestContext, AfterResponseContext } from '@apicrafthq/script-sdk';
export async function addRequestId(ctx: BeforeRequestContext) {
ctx.request.headers['X-Request-Id'] = crypto.randomUUID();
}
export async function warnOnServerError(ctx: AfterResponseContext) {
if (ctx.response.status >= 500) {
ctx.logger.warn(`Petstore returned ${ctx.response.status} for ${ctx.request.url}`);
}
}
Attaching middlewares
A request carries an ordered list per phase. On disk:
{
"middlewares": {
"before": [{ "name": "tracing", "exportName": "addRequestId" }],
"after": [{ "name": "tracing", "exportName": "warnOnServerError" }]
}
}
In the app, use the middleware chain editors. You can attach a chain at four scopes:
- API / resource / operation defaults: seeded into new request instances (same model as auth defaults). Editing a default doesn't retro-fit existing instances.
- Request: the specific instance's own chain.
Within a phase, middlewares run top to bottom in the listed order; reorder them in the editor.
Execution rules
- Each middleware has a 5-second timeout. Exceeding it fails the request.
- A middleware that throws aborts the whole request: a
beforehook stops it from being sent; anafterhook fails the run after the response arrived. - Scripts only run in a trusted project. The first time you run a request that uses a script, API Craft asks you to approve the project.
Middlewares and custom auth run real code from the project. Only approve projects you trust; approval lets every script in the project execute.
The script SDK
Custom auth and middlewares import their types from @apicrafthq/script-sdk. This package is
types only: the runtime is built into API Craft, so you don't need to install anything to
run scripts. Install it only for editor autocomplete and type-checking while you write them.
The context passed to every script:
| Member | Available in | Purpose |
|---|---|---|
ctx.var(name) | all | Read a variable from the active environment. Typed from the env schema. |
ctx.http | all | HTTP client (get/post/…) for auxiliary calls: login, signing, polling. |
ctx.cache | all | Persistent key/value store, shared across the project's scripts. |
ctx.logger | all | debug/info/warn/error. Use this; console is not allowed. |
ctx.request | before / auth | The outgoing request. Mutable in before hooks. |
ctx.response | after | The response. Mutable in after hooks. |
console is rejected at load time: a script that calls console.log won't run. Use
ctx.logger instead.
Installing the types
When you create a script, API Craft generates a tsconfig.json and a .api-craft/env.d.ts (which
types ctx.var() from your environment schema) inside the API folder. Both are gitignored; they
are local editor tooling, not part of the project. Add the SDK the same way: as a dev-only
dependency, wherever your editor will find it.
TypeScript projects: add it as a dev dependency at the repo root:
npm install -D @apicrafthq/script-sdk
Your existing node_modules is above the API folder, so the editor resolves it with no extra
setup.
Everything else (a non-JS backend, or a standalone API Craft project): install it in the
apis/ directory so a stray package.json doesn't land at your repo root:
cd apis
npm install -D @apicrafthq/script-sdk
One install under apis/ covers every API beneath it. Gitignore the tooling so it stays local:
# apis/.gitignore
node_modules/
package.json
package-lock.json
This is purely a developer-experience step. Middlewares and custom auth run fine with nothing installed; the SDK is bundled into API Craft. Skip it and you only lose autocomplete.
Next
Chain requests together and assert on responses in writing workflows.