HttpApiSchema unstable
Attaches HTTP API metadata to Effect Schema values.
This module is the schema-side bridge for HttpApi endpoint builders, generated clients, and OpenAPI support. It does not define routes or perform IO. Instead, the helpers annotate schemas so the surrounding HTTP API tooling can choose response status codes, content types, body codecs, multipart handling, and no-body response behavior.
Constructors
Creates a void schema with the given HTTP status code. This is used to represent empty responses with a specific status code.
See
- NoContent for the predefined 204 no content schema.
Signature
declare function Empty(code: number): VoidStreamNdjson
unstableCreates a schema for a stream of newline-delimited JSON elements.
Details
contentType defaults to application/x-ndjson and maxElementBytes to 1
MiB. maxBytes has no default. Pass a list of content types to accept
aliases such as application/jsonl. The first entry is the default.
Signature
declare const StreamNdjson: <S extends Constraint>(options: { readonly contentType?: string | readonly Array<string>; readonly maxBytes?: Input; readonly maxElementBytes?: Input; readonly schema: S;}) => StreamNdjson<S>Example Declaring an NDJSON request payload
import { Schema } from "effect"import { HttpApiEndpoint, HttpApiSchema } from "effect/http-api"
const Event = Schema.Struct({ id: Schema.Number })const payload = HttpApiSchema.StreamNdjson({ schema: Event, maxBytes: "100 MiB" })
// The handler receives a Stream of Eventsconst importEvents = HttpApiEndpoint.post("importEvents", "/events", { payload, success: Schema.Number})
importEvents.path // => "/events"payload.contentType // => "application/x-ndjson"payload.maxElementBytes // => 1048576nCreates a Server-Sent Events streaming success response schema.
Details
StreamSse is response-only. Endpoint construction rejects it in payload.
Signature
declare const StreamSse: { <Events extends EventCodec, Error extends Constraint = Never>(options: { readonly contentType?: string; readonly error?: Error; readonly events: Events; }): StreamSse<Events, Error, Events["Type"]>; <Data extends Constraint, Error extends Constraint = Never>(options: { readonly contentType?: string; readonly data: Data; readonly error?: Error; }): StreamSse<SseEventFromData<Data>, Error, Data["Type"]>;}StreamUint8Array
unstableCreates a streaming Uint8Array body schema.
Details
contentType defaults to application/octet-stream. Pass a list when the
same stream is sent under several media types. The first entry is the
default response type, and payloads are accepted under every entry.
maxBytes only applies to request payloads and has no default.
Signature
declare const StreamUint8Array: (options?: { readonly contentType?: string | readonly Array<string>; readonly maxBytes?: Input;}) => StreamUint8ArraywithHeaders
unstableConstructs a WithHeaders response value from a body and headers.
Details
The returned value is branded so servers and clients can detect it exactly, including in mixed success unions. The same shape is used on both sides: a value received from a client can be returned from another handler unchanged.
See WithHeaders for an example that constructs a schema and its corresponding response value.
Signature
declare const withHeaders: <A, H>(options: { readonly body: A; readonly headers: H;}) => withHeaders<A, H>Encoding
asFormUrlEncoded
unstableMarks a schema as an application/x-www-form-urlencoded payload or response.
Details
The schema's encoded side must be a record of strings.
Gotchas
Fields are flat on the server. A field that encodes to a nested object is
sent as bracket keys that the server does not read back; use an explicit
codec such as Schema.fromJsonString(...) for structured values.
Signature
declare function asFormUrlEncoded(options?: { readonly contentType?: string;}): <S extends Top>(self: S) => S["Rebuild"]Marks a schema as a JSON payload / response.
Signature
declare function asJson(options?: { readonly contentType?: string;}): <S extends Top>(self: S) => S["Rebuild"]asMultipart
unstableMarks a schema as a multipart payload.
See
- asMultipartStream for a multipart stream payload.
Signature
declare function asMultipart(options?: Options): <S extends Top>(self: S) => asMultipart<S>asMultipartStream
unstableMarks a schema as a multipart stream payload.
See
- asMultipart for a buffered multipart payload.
Signature
declare function asMultipartStream(options?: Options): <S extends Top>(self: S) => asMultipartStream<S>asNoContent
unstableMarks a schema as a no-content response while preserving a decoded client value.
Details
The server encodes the response as void; generated clients call decode to
produce the schema's decoded value when the response has no body.
See
Signature
declare function asNoContent<S extends Constraint>(options: { readonly decode: LazyArg<S["Type"]>;}): (self: S) => asNoContent<S>Renders an error class as an RFC 9457 problem.
Details
The server encodes the error as an application/problem+json body carrying
type, title and the status of the class. Generated clients decode a body
with the same type back into an instance by calling decode, which
defaults to constructing the class without arguments; a body with a
different type is rejected.
See
- asNoContent for the empty-body equivalent.
Signature
declare function asProblem<S extends Constraint & () => S["Type"]>(options: { readonly decode?: LazyArg<S["Type"]>; readonly title: string; readonly type?: string;}): (self: S) => asProblem<S>Example A built-in error as a problem
import { HttpApiEndpoint, HttpApiError, HttpApiSchema } from "effect/http-api"
const user = HttpApiEndpoint.get("user", "/user", { error: HttpApiError.NotFound.pipe(HttpApiSchema.asProblem({ title: "Not Found" }))})// A NotFound failure responds with 404 and// { "type": "about:blank", "title": "Not Found", "status": 404 }Marks a schema as a text payload / response.
Details
The schema encoded side must be a string.
Signature
declare function asText(options?: { readonly contentType?: string;}): <S extends Top & { readonly Encoded: string;}>(self: S) => S["Rebuild"]asUint8Array
unstableMarks a schema as a binary payload / response.
Details
The schema encoded side must be a Uint8Array.
Signature
declare function asUint8Array(options?: { readonly contentType?: string;}): <S extends Top & { readonly Encoded: Uint8Array;}>(self: S) => S["Rebuild"]encodeToWithHeaders
unstableEncodes a schema as a { body, headers } pair, folding response headers into
an opaque domain type such as an error class.
Details
The encoded side is the pair of the body schema and the headers fields; the Type stays the source schema's Type. The body schema is authoritative for everything wire-level: status, content type, and response encoding resolve from the body schema's annotations.
The mappings are pure total functions: validation lives in the body and header schemas, the mappings only reshape valid data.
Streams used as the body schema turn mid-stream transport errors into defects on the client. Stream responses should use WithHeaders, which preserves the body stream's error channel in the generated client.
See
- WithHeaders for the structural wrapper recommended for success responses, including streams.
Signature
declare function encodeToWithHeaders<S extends Top, Body extends Top, Headers extends Fields>(options: { readonly body: Body; readonly headers: Headers;}, transformation: { readonly decode: (value: View<{ readonly body: Body; readonly headers: Struct<Headers>; }>) => S["Type"]; readonly encode: (value: S["Type"]) => View<{ readonly body: Body; readonly headers: Struct<Headers>; }>;}): (self: S) => encodeToWithHeaders<S, Body, Headers>Example Encoding an error with headers
import { Schema } from "effect"import { HttpApiSchema } from "effect/http-api"
class UserNotFound extends Schema.TaggedError<UserNotFound>()("UserNotFound", { userId: Schema.Int}) {}
const UserNotFoundWithHeaders = UserNotFound.pipe( HttpApiSchema.encodeToWithHeaders({ body: HttpApiSchema.Empty(404), headers: { "x-user-id": Schema.Int } }, { decode: ({ headers }) => new UserNotFound({ userId: headers["x-user-id"] }), encode: (error) => ({ headers: { "x-user-id": error.userId }, body: undefined }) }))
const encoded = Schema.encodeSync(UserNotFoundWithHeaders)(new UserNotFound({ userId: 123 }))encoded // => { body: undefined, headers: { "x-user-id": 123 } }Models
Type of the Accepted schema, a void schema annotated with HTTP status code 202.
Signature
interface Accepted extends Void { constructor(_: never);}Type of the Created schema, a void schema annotated with HTTP status code 201.
Signature
interface Created extends Void { constructor(_: never);}HTTP API body encoding metadata used by payloads and responses.
Signature
type Encoding = PayloadEncoding | ResponseEncodingType of the NoContent schema, a void schema annotated with HTTP status code 204.
Signature
interface NoContent extends Void { constructor(_: never);}HTTP API request payload encoding metadata.
Signature
type PayloadEncoding = { readonly _tag: "Multipart"; readonly contentType: string; readonly limits?: Multipart_.withLimits.Options; readonly mode: "buffered" | "stream";} | { readonly _tag: "Json" | "FormUrlEncoded" | "Uint8Array" | "Text"; readonly contentType: string;}HTTP API response body encoding metadata.
Signature
type ResponseEncoding = { readonly _tag: "Json" | "FormUrlEncoded" | "Uint8Array" | "Text"; readonly contentType: string;}Event schema produced when StreamSse is constructed from a JSON data schema.
Signature
interface SseEventFromData<Data extends Schema.Constraint> extends ConstraintCodec<{ readonly data: Data["Type"]; readonly event: string; readonly id?: string;}, { readonly data: string; readonly event?: string; readonly id?: string;}, Data["DecodingServices"], Data["EncodingServices"]> {}Common HTTP status code literals accepted by status.
Signature
type StatusLiteral = HttpStatus.LiteralSchema for a stream of newline-delimited JSON elements.
Details
Each element is encoded as one line of JSON followed by \n, including the
last. When decoding, \n, \r\n and a bare \r end a line, empty lines are
skipped, a leading BOM is stripped and invalid UTF-8 fails. The first
malformed line or element that fails to decode ends the stream, after every
element before it has been delivered. A missing final newline is accepted if
the last line parses.
maxElementBytes caps the UTF-8 bytes of one line, without its terminator.
maxBytes caps the whole body and has no default.
As a request payload, the handler receives the elements as the body is
read. Malformed lines and elements that fail to decode are an
HttpApiSchemaError, and limit breaches are an HttpApiPayloadTooLarge.
Gotchas
There is no error schema. A failure mid-stream aborts the connection, so
the reader sees a body read error, never a clean end. Typed failures
mid-stream are a StreamSse feature. A body cut cleanly at a line
boundary can't be told apart from a complete one.
Signature
interface StreamNdjson<S extends Schema.Constraint> extends BottomLazy<SchemaAST.Declaration, StreamNdjson<S>> { constructor(_: never); readonly _tag: "StreamNdjson"; readonly "~effect/http-api/HttpApiSchema/Stream": "~effect/http-api/HttpApiSchema/Stream"; readonly "~type.make": Stream<S["Type"], unknown, never>; readonly "~type.make.in": Stream<S["Type"], unknown, never>; readonly contentType: string; readonly contentTypes: readonly [string, string]; readonly DecodingServices: S["DecodingServices"]; readonly Encoded: Stream<S["Type"], unknown, never>; readonly EncodingServices: S["EncodingServices"]; readonly Iso: Stream<S["Type"], unknown, never>; readonly maxBytes: ByteSize | undefined; readonly maxElementBytes: ByteSize; readonly mode: "ndjson"; readonly Rebuild: StreamNdjson<S>; readonly schema: S; readonly Type: Stream<S["Type"], unknown, never>;}Schema for a streaming HTTP API body.
Details
StreamUint8Array and StreamNdjson describe the body in either direction, with the endpoint slot deciding which. StreamSse is for success responses only.
Signature
type StreamSchema = StreamSse<Sse.EventCodec, Schema.Top, unknown> | StreamUint8Array | StreamNdjson<Schema.Top>Schema for a Server-Sent Events success response.
Details
events describes successful application events emitted by the stream, and
error describes typed stream failures that will be encoded by later
endpoint/server/client integrations using the reserved failure event. If
error is omitted, it defaults to Schema.Never. When StreamSse is
constructed from data, handlers and clients expose raw data values while
the server and client still use an SSE event schema internally.
Gotchas
The client treats effect/http-api/stream/failure as a stream failure only
when its decoded data is a Cause. If an event schema accepts that name
dynamically but decodes data to another value, the client emits it as an
application event. Endpoint construction rejects event schemas that declare
the reserved name statically.
StreamSse is response-only. The payload slot's type excludes it, and
endpoint construction rejects it.
Signature
interface StreamSse<Events extends Sse.EventCodec, Error extends Schema.Constraint, Value = Events["Type"]> extends BottomLazy<SchemaAST.Declaration, StreamSse<Events, Error, Value>> { constructor(_: never); readonly _tag: "StreamSse"; readonly "~effect/http-api/HttpApiSchema/Stream": "~effect/http-api/HttpApiSchema/Stream"; readonly "~type.make": Stream<Value, Error["Type"], never>; readonly "~type.make.in": Stream<Value, Error["Type"], never>; readonly "~Value"?: Value; readonly contentType: string; readonly contentTypes: readonly [string, string]; readonly DecodingServices: Events["DecodingServices"] | Error["DecodingServices"]; readonly Encoded: Stream<Value, Error["Type"], never>; readonly EncodingServices: Events["EncodingServices"] | Error["EncodingServices"]; readonly error: Error; readonly events: Events; readonly Iso: Stream<Value, Error["Type"], never>; readonly mode: "sse"; readonly Rebuild: StreamSse<Events, Error, Value>; readonly sseMode: StreamSseMode; readonly Type: Stream<Value, Error["Type"], never>;}Mode describing whether an SSE stream emits full events or raw data values.
Signature
type StreamSseMode = "events" | "data"Schema for a streaming Uint8Array body.
Details
It works as a success response or as a request payload. It is intentionally
separate from the buffered asUint8Array encoding. As a payload, the handler
receives the body as it is read, and maxBytes caps the bytes read. It has
no default and does not fall back to MaxBodySize.
Signature
interface StreamUint8Array extends Bottom<Stream.Stream<Uint8Array, unknown, never>, Stream.Stream<Uint8Array, unknown, never>, never, never, SchemaAST.Declaration, StreamUint8Array> { constructor(_: never); readonly _tag: "StreamUint8Array"; readonly "~effect/http-api/HttpApiSchema/Stream": "~effect/http-api/HttpApiSchema/Stream"; readonly contentType: string; readonly contentTypes: readonly [string, string]; readonly maxBytes: ByteSize | undefined; readonly mode: "uint8array"; readonly Rebuild: StreamUint8Array;}Schema for a success response that switches the connection to another protocol with an HTTP/1.1 upgrade.
Details
Created with Upgrade. The response is 101 Switching Protocols, and
the connection then carries raw bytes in both directions as a Socket.
Headers is the schema of the custom 101 headers, or never when the
upgrade declares none.
Signature
interface Upgrade<Headers extends Schema.Top = Schema.Top> extends Bottom<Socket.Socket, Socket.Socket, never, never, SchemaAST.Declaration, Upgrade<Headers>> { constructor(_: never); readonly "~effect/http-api/HttpApiSchema/Upgrade": "~effect/http-api/HttpApiSchema/Upgrade"; readonly headers: Headers | undefined; readonly protocol: string; readonly Rebuild: Upgrade<Headers>;}The Type of a WithHeaders schema: what handlers return and what the
client resolves to, constructed via withHeaders.
Details
body is the inner success value. For stream success schemas it is the
Stream itself, so headers are decided before the body starts streaming.
Signature
interface withHeaders<A, H> { readonly "~effect/http-api/HttpApiSchema/WithHeadersValue": "~effect/http-api/HttpApiSchema/WithHeadersValue"; readonly body: A; readonly headers: H;}A response schema wrapping a body schema together with a response headers schema.
Details
WithHeaders is a branded declaration schema: it carries the inner success
schema and the headers schema as properties, and server, client, and OpenAPI
integrations detect the brand and handle body and headers separately. It is
supported for error responses, though encodeToWithHeaders is usually
more convenient there because handlers can fail with the domain error value.
schemais the inner response schema. Success responses may wrapStreamSseandStreamUint8Array; error responses remain non-streaming. NestingWithHeadersis rejected at construction.headersis any schema; endpoint construction applies a StringTree codec unless codecs are disabled. Optional headers that encode toundefinedare omitted from the response.- Status and response-encoding annotations are resolved from the wrapper first, falling through to the inner schema.
- A header-carrying response cannot share its status and content type with another response in the same success or error union. Endpoint construction rejects ambiguous declarations, including those made with encodeToWithHeaders.
Rebuildpreserves the brand and both parts, so.annotatekeeps the wrapper intact.
Signature
interface WithHeaders<S extends Schema.Top, H extends Schema.Top> extends Bottom<withHeaders<S["Type"], H["Type"]>, withHeaders<S["Encoded"], Schema.StringTree>, S["DecodingServices"] | H["DecodingServices"], S["EncodingServices"] | H["EncodingServices"], SchemaAST.Declaration, WithHeaders<S, H>> { constructor(_: never); readonly "~effect/http-api/HttpApiSchema/WithHeaders": "~effect/http-api/HttpApiSchema/WithHeaders"; readonly headers: H; readonly Rebuild: WithHeaders<S, H>; readonly schema: S;}Other
Signature
declare function status(code: number): <S extends Top>(self: S) => S["Rebuild"]declare function status(code: "NotFound" | "Forbidden" | "Unauthorized" | "InternalServerError" | "BadRequest" | "MethodNotAllowed" | "NotAcceptable" | "RequestTimeout" | "Conflict" | "Gone" | "UnprocessableEntity" | "NotImplemented" | "ServiceUnavailable" | "Continue" | "SwitchingProtocols" | "Processing" | "EarlyHints" | "OK" | "Ok" | "Created" | "Accepted" | "NonAuthoritativeInformation" | "NoContent" | "ResetContent" | "PartialContent" | "MultiStatus" | "AlreadyReported" | "ImUsed" | "MultipleChoices" | "MovedPermanently" | "Found" | "SeeOther" | "NotModified" | "TemporaryRedirect" | "PermanentRedirect" | "PaymentRequired" | "ProxyAuthenticationRequired" | "LengthRequired" | "PreconditionFailed" | "PayloadTooLarge" | "UriTooLong" | "UnsupportedMediaType" | "RangeNotSatisfiable" | "ExpectationFailed" | "ImATeapot" | "MisdirectedRequest" | "Locked" | "FailedDependency" | "TooEarly" | "UpgradeRequired" | "PreconditionRequired" | "TooManyRequests" | "RequestHeaderFieldsTooLarge" | "UnavailableForLegalReasons" | "BadGateway" | "GatewayTimeout" | "HttpVersionNotSupported" | "VariantAlsoNegotiates" | "InsufficientStorage" | "LoopDetected" | "NotExtended" | "NetworkAuthenticationRequired"): <S extends Top>(self: S) => S["Rebuild"]Signature
declare function Upgrade(options: { readonly protocol: string;}): Upgrade<never>declare function Upgrade<H extends Fields>(options: { readonly headers: H; readonly protocol: string;}): Upgrade<Struct<H>>declare function Upgrade<H extends Top>(options: { readonly headers: H; readonly protocol: string;}): Upgrade<H>Example Declaring an exec start endpoint
import { Schema } from "effect"import { HttpApiEndpoint, HttpApiSchema } from "effect/http-api"
const start = HttpApiEndpoint.post("start", "/exec/:id/start", { params: { id: Schema.String }, payload: Schema.Struct({ Detach: Schema.Boolean, Tty: Schema.Boolean }), success: [ HttpApiSchema.Upgrade({ protocol: "tcp", headers: { "content-type": Schema.Literals([ "application/vnd.docker.raw-stream", "application/vnd.docker.multiplexed-stream" ]) } }), // a detached exec answers with an empty 200 instead HttpApiSchema.Empty(200) ]})WithHeaders
unstableSignature
declare function WithHeaders<S extends Top, H extends Fields>(schema: S, headers: H): WithHeaders<S, Struct<H>>declare function WithHeaders<S extends Top, H extends Top>(schema: S, headers: H): WithHeaders<S, H>Example Adding response headers to a success schema
import { Schema } from "effect"import { HttpApiSchema } from "effect/http-api"
const schema = HttpApiSchema.WithHeaders(Schema.String, { "x-total-count": Schema.FiniteFromString})const response: typeof schema.Type = HttpApiSchema.withHeaders({ body: "created", headers: { "x-total-count": 1 }})
HttpApiSchema.isWithHeaders(schema) // => trueresponse.body // => "created"response.headers // => { "x-total-count": 1 }Predicates
isNoContent
unstableReturns true when a schema AST represents a no-content response.
Details
The check succeeds for direct void schemas and schemas whose encoded or
transformation target is void.
Signature
declare function isNoContent(ast: AST): booleanisWithHeaders
unstableReturns true when a schema is a WithHeaders response schema.
Signature
declare function isWithHeaders(u: unknown): u is WithHeaders<Top, Top>Example Detecting a response headers schema
import { Schema } from "effect"import { HttpApiSchema } from "effect/http-api"
const schema = HttpApiSchema.WithHeaders(Schema.String, { "x-request-id": Schema.String})
HttpApiSchema.isWithHeaders(schema) // => trueHttpApiSchema.isWithHeaders(Schema.String) // => falseSchemas
Schema for empty HTTP responses with status code 202.
Signature
declare const Accepted: AcceptedSchema type returned by asMultipart for buffered multipart payloads.
Signature
interface asMultipart<S extends Schema.Top> extends brand<S["Rebuild"], MultipartTypeId> { constructor(_: never);}Schema type returned by asMultipartStream for streaming multipart payloads.
Signature
interface asMultipartStream<S extends Schema.Top> extends brand<S["Rebuild"], MultipartStreamTypeId> { constructor(_: never);}Schema type returned by asNoContent, encoding as void while decoding to the original schema type.
Signature
interface asNoContent<S extends Schema.Constraint> extends decodeTo<Schema.toType<S>, Schema.Void> { constructor(_: never);}Schema type returned by asProblem, encoding as a problem details body
while decoding to the original schema type.
Signature
interface asProblem<S extends Schema.Constraint> extends decodeTo<Schema.toType<S>, Schema.Struct<{ readonly status: Schema.middlewareDecoding<Schema.withDecodingDefaultKey<Schema.tag<number>>, never>; readonly title: Schema.middlewareDecoding<Schema.withDecodingDefaultKey<Schema.tag<string>>, never>; readonly type: Schema.tag<string>;}>> { constructor(_: never);}Schema for empty HTTP responses with status code 201.
Signature
declare const Created: CreatedSchema type returned by encodeToWithHeaders, encoding as a { body, headers }
pair while decoding to the source schema type.
Signature
interface encodeToWithHeaders<S extends Schema.Top, Body extends Schema.Top, Headers extends Schema.Struct.Fields> extends decodeTo<Schema.toType<S>, Schema.Struct<{ readonly body: Body; readonly headers: Schema.Struct<Headers>;}>> { constructor(_: never);}Schema for empty HTTP responses with status code 204.
Signature
declare const NoContent: NoContentType IDs
MultipartStreamTypeId
unstableRuntime brand key used to mark schemas as streaming multipart payloads.
Signature
declare const MultipartStreamTypeId: "~effect/http-api/HttpApiSchema/MultipartStream"Type-level brand identifier used by asMultipartStream.
Signature
type MultipartStreamTypeId = typeof MultipartStreamTypeIdMultipartTypeId
unstableRuntime brand key used to mark schemas as buffered multipart payloads.
Signature
declare const MultipartTypeId: "~effect/http-api/HttpApiSchema/Multipart"Type-level brand identifier used by asMultipart.
Signature
type MultipartTypeId = typeof MultipartTypeIdWithHeadersTypeId
unstableRuntime brand key used to mark WithHeaders response schemas.
Signature
declare const WithHeadersTypeId: "~effect/http-api/HttpApiSchema/WithHeaders"Type-level brand identifier used by WithHeaders.
Signature
type WithHeadersTypeId = typeof WithHeadersTypeIdWithHeadersValueTypeId
unstableRuntime brand key used to mark WithHeaders response values.
Signature
declare const WithHeadersValueTypeId: "~effect/http-api/HttpApiSchema/WithHeadersValue"Type-level brand identifier used by WithHeaders response values.
Signature
type WithHeadersValueTypeId = typeof WithHeadersValueTypeId