Skip to content

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

shell
bunx shadcn@latest add @krak-stack/service-cloudflare-ai-gateway

Complete Layer

ts
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

ts
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 URL
  • AI_GATEWAY_API_KEY - Cloudflare API token with Workers AI Read permission
  • AI_GATEWAY_ID - AI Gateway ID sent in cf-aig-gateway-id
Dependencies
Packages and shadcn components required by this registry item.