HTTP Request and Response Parameter Mapping (Effect)

SkillAI & models

Guides your agent to map HTTP paths, queries, headers, and JSON bodies to typed parameters when building Effect-based Golem agents.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the HTTP Request and Response Parameter Mapping (Effect) skill

About this capability

Mapping HTTP path, query, header, and body values to Effect Golem agent parameters and mapping method results to responses. Use when defining request bindings, optional parameters, JSON bodies, response schemas, or unstructured HTTP inputs with @golemcloud/effect-golem.

What this skill tells your AI

The instructions your AI receives, as published by golemcloud/golem in golem-skills/skills/effect/golem-http-params-effect/SKILL.md and read by ahel’s review.

Effect Golem endpoints declare request bindings with the Http namespace and describe every method parameter and result with Effect Schema. The Golem host performs the HTTP parsing and response rendering; handlers receive typed values and return Effect values rather than reading or constructing raw HTTP requests and responses.

Load golem-add-http-endpoint-effect when the agent still needs a mount, endpoint declarations, an implementation import in src/main.ts, or an httpApi deployment.

Path Variables

Place constructor identity fields in the mount path and method fields in endpoint paths. Variable names must exactly match the corresponding id or input keys, including TypeScript casing:

import { Schema } from "effect";
import { defineAgent, Http, method } from "@golemcloud/effect-golem";

export const TaskAgent = defineAgent({
  name: "TaskAgent",
  mode: "durable",
  id: {
    taskName: Schema.String,
  },
  http: Http.mount("/api/tasks/{taskName}"),
  methods: {
    getItem: method({
      input: { itemId: Schema.String },
      success: Item,
      http: [Http.get("/items/{itemId}")],
    }),
  },
});

Every agent id field must appear in the mount path. This Effect SDK has no API for binding a mount header to a agent id field. Mount paths also cannot contain query parameters or catch-all variables.

An endpoint catch-all captures the remaining path and must be its final segment:

serveFile: method({
  input: { path: Schema.String },
  success: FileContent,
  http: [Http.get("/files/{*path}")],
}),
// GET .../files/docs/readme.md supplies "docs/readme.md" as path.

Query Parameters

Declare query bindings in the endpoint path with key={parameterName}. The HTTP key may differ from the TypeScript parameter name:

search: method({
  input: {
    query: Schema.String,
    minPrice: Schema.NullOr(Schema.Number),
    inStockOnly: Schema.NullOr(Schema.Boolean),
  },
  success: Schema.Array(Product),
  http: [
    Http.get(
      "/products/search?q={query}&min-price={minPrice}&in-stock-only={inStockOnly}",
    ),
  ],
}),

For query and header parameters, Schema.NullOr(T) represents an optional value: an omitted value is supplied as null. Keep path variables non-optional because a matching path always contains the segment.

Header Variables

The endpoint options map each HTTP header name to one method parameter name:

submitReport: method({
  input: {
    tenantId: Schema.String,
    report: Report,
  },
  success: Receipt,
  http: [
    Http.post("/reports", {
      headers: { "X-Tenant": "tenantId" } as const,
    }),
  ],
}),

Here tenantId comes from X-Tenant, while the unbound report parameter comes from the JSON body field named report. Header names are case-insensitive for duplicate detection. Query keys and all parameter names are case-sensitive.

A parameter may be bound only once within an endpoint. Do not bind the same parameter from two path positions, two query keys, two headers, or a mixture of those sources.

Schemas Allowed in Path, Query, and Headers

Route-bound values must use a string-bindable schema:

Effect SchemaParsed value
Schema.Stringstring
Schema.NumberJavaScript number / WIT f64
Schema.Booleanboolean
Schema.BigIntbigint / WIT s64
Schema.Literal(...) or a union of scalar literalsvalidated literal value
scalar template literal, refinement, brand, or transformationdecoded scalar value

For a string enum, use the Effect v4 union form:

const SortOrder = Schema.Union([
  Schema.Literal("ascending"),
  Schema.Literal("descending"),
]);

Wrap a scalar with Schema.NullOr(...) only for an optional query or header. Structs, tuples, records, unstructured values, multimodal values, nested collections, and non-empty arrays are not route-bindable.

Query and header bindings additionally accept Schema.Array(T) when T is one of the supported primitive, literal/string-enum, template-literal, refinement, brand, or transformation schemas listed above. The host collects repeated query instances and repeated raw header fields in arrival order:

collectionBindings: method({
  input: {
    tags: Schema.Array(Schema.String),
    scores: Schema.Array(Schema.Number),
  },
  success: Schema.Struct({
    tags: Schema.Array(Schema.String),
    scores: Schema.Array(Schema.Number),
  }),
  http: [
    Http.get("/bindings/collections?tag={tags}", {
      headers: { "X-Score": "scores" } as const,
    }),
  ],
}),

For example, ?tag=a&tag=b binds tags as ["a", "b"]; three raw X-Score fields bind three numbers. A single comma-separated header line is one value and is not split into collection items. Path variables remain scalar-only.

JSON Body Mapping

Use Http.post, Http.put, Http.del, Http.patch, or another bodyful helper when parameters must come from a body. Every parameter not bound to a path, query key, or header becomes a top-level field in the JSON object, using the exact TypeScript parameter name:

updateItem: method({
  input: {
    id: Schema.String,
    name: Schema.String,
    count: Schema.Number,
  },
  success: Item,
  http: [Http.put("/items/{id}")],
}),

The request body is:

{ "name": "Widget", "count": 5 }

The body is always an object keyed by method parameter name. A single unbound parameter decision: Schema.String expects { "decision": "approved" }, not the bare JSON string "approved". Likewise, input: { item: Item } expects { "item": { ... } }; the fields of the Item struct are not flattened into the top-level body.

Http.get and Http.head are bodyless, so every parameter of those methods must be bound from the path, query, or headers. Prefer the standard verb helpers over Http.custom(...) for standard methods so the SDK can apply its bodyless validation.

Structured Data and JSON

Use schemas that the SDK can lower to WIT:

Effect SchemaJSON representation
Schema.Stringstring
Schema.Numbernumber
Schema.Booleanboolean
Schema.Array(T)array
Schema.Struct({ ... })object with the declared field names
Schema.Tuple([...])array
Schema.NullOr(T)value or null
union of string literalsstring enum value
tagged uniontagged variant object

Use Schema.Struct for fixed object fields. Open-ended Schema.Record(...) index signatures are not supported by the pinned Effect SDK's WIT schema compiler. Keep transformations and refinements serializable through their encoded schemas.

HTTP Response Mapping

Declare the HTTP outcome through the method's success and optional error schemas:

Method contract and handler resultHTTP response
success: Schema.Void, return Effect.void204, empty body
success: T, return Effect<T>200, JSON T
success: UnstructuredText(...), return inline text200, plain text
success: UnstructuredBinary(...), return inline bytes200, raw bytes
success: Schema.NullOr(T), return T200, JSON T
success: Schema.NullOr(T), return null404, empty body
declare error: E, return Effect.fail(E)500, JSON E

Use Schema.NullOr(T) for ordinary not-found behavior. Use a declared error schema and Effect.fail(...) for expected typed failures. Defects such as Effect.die(...) are unexpected invocation failures and may be retried; do not use them to select an HTTP status.

The SDK does not expose a raw response builder or per-error status mapping. Do not return an Effect Platform HttpServerResponse or invent response-header/status helpers.

For plain text or binary output, set method success to Unstructured.UnstructuredText(...) or Unstructured.UnstructuredBinary(...) and return an inline TextReferenceValue or BinaryReferenceValue. Inline text uses text/plain plus its optional language metadata; inline binary uses the value's declared MIME type. Unstructured success values cannot be combined with a typed method error. Load golem-add-http-endpoint-effect for complete value examples.

Malformed Bound Input

The host rejects malformed route-bound input before invoking the handler. The response is 400 Bad Request, has a JSON content type, and has this stable shape:

{ "code": "REQUEST_VALUE_PARSING_FAILED", "errors": ["descriptive message"] }

The stable codes distinguish common cases:

CaseCode
Missing required query or header valueREQUEST_MISSING_VALUE
Malformed scalar or unparsable collection itemREQUEST_VALUE_PARSING_FAILED
More than one value for a scalar bindingREQUEST_TOO_MANY_VALUES

Do not duplicate this parsing in the handler or replace these host responses with custom response construction.

Unstructured Request Bodies

The Effect SDK exposes top-level unstructured method parameters under the Unstructured namespace:

import { Effect, Schema } from "effect";
import { Http, method, Unstructured } from "@golemcloud/effect-golem";

const upload = method({
  input: {
    payload: Unstructured.UnstructuredBinary({
      restrictions: [{ mimeType: "image/png" }],
    }),
  },
  success: Schema.Number,
  http: [Http.post("/upload")],
});

// In the implementation:
const uploadHandler = ({
  payload,
}: {
  payload: Unstructured.BinaryReferenceValue;
}) =>
  Effect.succeed(payload._tag === "inline" ? payload.val.data.byteLength : -1);

Use Unstructured.UnstructuredText({ restrictions: [{ languageCode: "en" }] }) for a restricted text input, or omit restrictions to accept any declared text language or binary MIME type. An unstructured value cannot bind to path, query, or headers; keep it as the only body parameter.

method.success accepts either an ordinary Effect Schema or one top-level UnstructuredBinary(...) / UnstructuredText(...) element specification. Unstructured values cannot be nested inside Schema.Struct or combined into a multimodal success value.

Authenticated Principal

The authenticated caller is an Effect Context service, not a method parameter or HTTP binding:

import { Effect } from "effect";
import { Principal } from "@golemcloud/effect-golem";

const currentCaller = Effect.gen(function* () {
  return yield* Principal.Principal;
});

Enable authentication with Http.mount(path, { auth: true }) or an endpoint auth option, then read Principal.Principal inside the handler for the current invocation. Load golem-add-http-auth-effect for deployment security configuration and principal variants.

Key Constraints

  • Import Effect APIs from effect and Golem APIs from @golemcloud/effect-golem.
  • Match every placeholder and header target to an exact input or id key.
  • Use Schema.NullOr(...) and null for optional query/header values and 404 success results.
  • Keep GET/HEAD parameters fully bound and bodyful request bodies as named JSON objects.
  • Return Effects from handlers; do not use plain values, async handlers, or raw HTTP middleware.
  • Do not use @golemcloud/golem-ts-sdk decorators or invent APIs from another language SDK.

Signals

GitHub stars
2k
Forks
211
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
golem-http-params-effect
Source
github.com/golemcloud/golem