Layers · Reference
Cloudflare AI Gateway
An Effect service that configures the OpenAI client for Cloudflare AI Gateway with optional per-request metadata.
Last updated October 8, 2026
Overview
service-cloudflare-ai-gateway is a copy/paste Effect service for Cloudflare's OpenAI-compatible REST API. It reads AI_GATEWAY_BASE_URL, AI_GATEWAY_API_KEY, and AI_GATEWAY_ID, then applies bearer authorization and the required cf-aig-gateway-id header.
Use CloudflareAIGateway.layer when no custom metadata is needed. Use CloudflareAIGateway.layerWith(metadata) to attach up to five string, number, or boolean values through cf-aig-metadata. Use the userId metadata key for Cloudflare User Insights attribution.
Install
bunx shadcn@latest add @krak-stack/service-cloudflare-ai-gatewayComplete Layer
import { OpenAiClient } from "@effect/ai-openai";
import { Config, Context, Effect, Layer } from "effect";
import {
FetchHttpClient,
HttpClient,
HttpClientRequest,
} from "effect/http";
export type CloudflareAIGatewayMetadata = Readonly<
Record<string, string | number | boolean>
>;
export class CloudflareAIGateway extends Context.Service<CloudflareAIGateway>()(
"CloudflareAIGateway",
{
make: Effect.gen(function* () {
const apiUrl = yield* Config.String("AI_GATEWAY_BASE_URL");
const apiKey = yield* Config.Redacted("AI_GATEWAY_API_KEY");
const gatewayId = yield* Config.String("AI_GATEWAY_ID");
return { apiUrl, apiKey, gatewayId };
}),
},
) {
static readonly baseLayer = Layer.effect(this, this.make);
private static readonly clientLayer = (
metadata?: CloudflareAIGatewayMetadata,
) =>
Layer.unwrap(
Effect.map(this, ({ apiKey, apiUrl, gatewayId }) =>
OpenAiClient.layer({
apiKey,
apiUrl,
transformClient: (client) =>
HttpClient.mapRequest(client, (request) => {
const gatewayRequest = HttpClientRequest.setHeader(
request,
"cf-aig-gateway-id",
gatewayId,
);
return metadata
? HttpClientRequest.setHeader(
gatewayRequest,
"cf-aig-metadata",
JSON.stringify(metadata),
)
: gatewayRequest;
}),
}),
),
).pipe(
Layer.provide(FetchHttpClient.layer),
Layer.provide(this.baseLayer),
);
static readonly layer = this.clientLayer();
static readonly layerWith = (metadata: CloudflareAIGatewayMetadata) =>
this.clientLayer(metadata);
}Usage
import { OpenAiLanguageModel } from "@effect/ai-openai";
import { Layer } from "effect";
import { CloudflareAIGateway } from "@/services/cloudflare-ai-gateway";
const model = OpenAiLanguageModel.model("openai/gpt-5.6-luna", {
reasoning: { effort: "low" },
}).pipe(
Layer.provide(
CloudflareAIGateway.layerWith({
userId: "user-id",
organizationId: "organization-id",
}),
),
);Third-party models must use Cloudflare's provider-qualified provider/model form, such as openai/gpt-5.6-luna. Workers AI models use the @cf/author/model form.
Environment Variables
AI_GATEWAY_BASE_URL- Cloudflare OpenAI-compatible REST API base URLAI_GATEWAY_API_KEY- Cloudflare API token with Workers AI Read permissionAI_GATEWAY_ID- AI Gateway ID sent incf-aig-gateway-id