Request & Response
Every HTTP handler in Effect does two things: it reads from the incoming
request and returns a response. Both are plain immutable values backed by
a small set of shared data types — Headers, UrlParams, Url, HttpBody, and
Cookies — that the same modules use on the client side too.
The request is read from context through the HttpServerRequest service. The
response is built with the HttpServerResponse constructors and combinators, all
of which are immutable: every combinator returns a new response.
import { Effect, Schema } from "effect"import { HttpServerRequest, HttpServerResponse } from "effect/unstable/http"
const CreateUser = Schema.Struct({ name: Schema.String, email: Schema.String})
// A handler: read the JSON body with a schema, return a JSON responseconst handler = Effect.gen(function* () { // Read request metadata directly from the service const request = yield* HttpServerRequest.HttpServerRequest console.log(request.method) // => "POST" console.log(request.url) // => "/users"
// Decode the body against a schema (fails with SchemaError on bad input) const body = yield* HttpServerRequest.schemaBodyJson(CreateUser)
// Build a 201 JSON response return yield* HttpServerResponse.json( { id: 1, ...body }, { status: 201 } )})Reading the request
Section titled “Reading the request”HttpServerRequest.HttpServerRequest is a Context.Service. Yielding it gives
you the request value, whose metadata is available synchronously while body
access is effectful (reading and parsing can fail).
import { Effect, Option } from "effect"import { HttpServerRequest } from "effect/unstable/http"
const inspect = Effect.gen(function* () { const req = yield* HttpServerRequest.HttpServerRequest
req.method // => "GET" | "POST" | ... (HttpMethod) req.url // => "/search?q=effect" (path without scheme/host) req.originalUrl // => the full original URL string req.headers // => Headers (lowercase-keyed record) req.cookies // => { session: "abc123" } (parsed from the Cookie header) req.remoteAddress // => Option<string>
// Body accessors are Effects (inherited from HttpIncomingMessage) const text = yield* req.text // => string const json = yield* req.json // => parsed JSON value const buffer = yield* req.arrayBuffer // => ArrayBuffer req.stream // => Stream<Uint8Array, HttpServerError>
// Derive a modified view without mutating the original const rewritten = req.modify({ url: "/v2" + req.url, remoteAddress: Option.some("127.0.0.1") }) return rewritten})Decoding the body with schemas
Section titled “Decoding the body with schemas”Rather than parse raw values by hand, decode directly into a typed value. Each
helper is an Effect that fails with Schema.SchemaError (and HttpServerError
when reading the body itself can fail).
import { Effect, Schema } from "effect"import { HttpServerRequest } from "effect/unstable/http"
const Login = Schema.Struct({ username: Schema.String, password: Schema.String})
// JSON body → typed valueconst fromJson = HttpServerRequest.schemaBodyJson(Login)
// application/x-www-form-urlencoded body → typed valueconst fromForm = HttpServerRequest.schemaBodyUrlParams(Login)
// Either multipart OR urlencoded form, chosen by content-typeconst fromAnyForm = HttpServerRequest.schemaBodyForm(Login)HttpServerRequest
Section titled “HttpServerRequest”The service tag and the request model. Yield the tag to get the current request; its fields are described above.
import { Effect } from "effect"import { HttpServerRequest } from "effect/unstable/http"
const method = Effect.map( HttpServerRequest.HttpServerRequest, (req) => req.method)// => Effect<HttpMethod, never, HttpServerRequest>schemaBodyJson
Section titled “schemaBodyJson”Reads the request body as JSON and decodes it with the supplied schema.
import { Schema } from "effect"import { HttpServerRequest } from "effect/unstable/http"
const User = Schema.Struct({ id: Schema.Number, name: Schema.String })const decoded = HttpServerRequest.schemaBodyJson(User)// => Effect<{ id: number; name: string }, HttpServerError | SchemaError, HttpServerRequest>schemaBodyForm
Section titled “schemaBodyForm”Decodes the body as form data: multipart requests are persisted and decoded as
multipart, other requests are decoded from URL-encoded parameters. Requires
Scope, FileSystem, and Path for the multipart path.
import { Schema } from "effect"import { HttpServerRequest } from "effect/unstable/http"
const Form = Schema.Struct({ title: Schema.String })const decoded = HttpServerRequest.schemaBodyForm(Form)// => Effect<{ title: string }, MultipartError | SchemaError | HttpServerError, ...>schemaBodyFormJson
Section titled “schemaBodyFormJson”Builds a decoder that reads a JSON value out of a single named form field (works for both multipart fields and URL-encoded parameters).
import { Schema } from "effect"import { HttpServerRequest } from "effect/unstable/http"
const Payload = Schema.Struct({ tags: Schema.Array(Schema.String) })const decodeField = HttpServerRequest.schemaBodyFormJson(Payload)const decoded = decodeField("metadata") // reads the "metadata" field as JSONschemaBodyUrlParams
Section titled “schemaBodyUrlParams”Reads the body as application/x-www-form-urlencoded parameters and decodes
them with the schema.
import { Schema } from "effect"import { HttpServerRequest } from "effect/unstable/http"
const Search = Schema.Struct({ q: Schema.String })const decoded = HttpServerRequest.schemaBodyUrlParams(Search)// => Effect<{ q: string }, HttpServerError | SchemaError, HttpServerRequest>schemaBodyMultipart
Section titled “schemaBodyMultipart”Persists the multipart body and decodes it with the schema. Requires Scope,
FileSystem, and Path to persist uploaded files.
import { Schema } from "effect"import { HttpServerRequest, Multipart } from "effect/unstable/http"
const Upload = Schema.Struct({ title: Schema.String, file: Multipart.SingleFileSchema})const decoded = HttpServerRequest.schemaBodyMultipart(Upload)schemaCookies
Section titled “schemaCookies”Decodes a schema from the request’s parsed cookies.
import { Schema } from "effect"import { HttpServerRequest } from "effect/unstable/http"
const Session = Schema.Struct({ session: Schema.String })const cookies = HttpServerRequest.schemaCookies(Session)// => Effect<{ session: string }, SchemaError, HttpServerRequest>schemaHeaders
Section titled “schemaHeaders”Decodes a schema from the request headers (header names are lowercase).
import { Schema } from "effect"import { HttpServerRequest } from "effect/unstable/http"
const Auth = Schema.Struct({ authorization: Schema.String })const headers = HttpServerRequest.schemaHeaders(Auth)// => Effect<{ authorization: string }, SchemaError, HttpServerRequest>schemaSearchParams
Section titled “schemaSearchParams”Decodes a schema from the parsed query string. Requires the ParsedSearchParams
service (provided by the router / adapter).
import { Schema } from "effect"import { HttpServerRequest } from "effect/unstable/http"
const Query = Schema.Struct({ q: Schema.String, page: Schema.optional(Schema.String) })const params = HttpServerRequest.schemaSearchParams(Query)// => Effect<{ q: string; page?: string }, SchemaError, ParsedSearchParams>ParsedSearchParams
Section titled “ParsedSearchParams”Context.Service holding the decoded query parameters for the current request.
Each key maps to a string, or an array when the parameter repeats.
import { Effect } from "effect"import { HttpServerRequest } from "effect/unstable/http"
const all = HttpServerRequest.ParsedSearchParams// => Effect<ReadonlyRecord<string, string | Array<string>>, never, ParsedSearchParams>searchParamsFromURL
Section titled “searchParamsFromURL”Converts a URL’s search parameters into a record; repeated keys become arrays
in insertion order.
import { HttpServerRequest } from "effect/unstable/http"
HttpServerRequest.searchParamsFromURL(new URL("https://x.dev/?a=1&a=2&b=3"))// => { a: ["1", "2"], b: "3" }upgradeChannel
Section titled “upgradeChannel”Creates a Channel backed by the current request’s upgraded socket — the
building block for WebSocket / raw socket handlers.
import { HttpServerRequest } from "effect/unstable/http"
const channel = HttpServerRequest.upgradeChannel()// => Channel reading socket frames and writing string | Uint8Array | CloseEventThe lower-level request.upgrade accessor yields the Socket directly:
import { Effect } from "effect"import { HttpServerRequest } from "effect/unstable/http"
const socket = Effect.flatMap( HttpServerRequest.HttpServerRequest, (req) => req.upgrade // => Effect<Socket, HttpServerError>)fromWeb / fromClientRequest
Section titled “fromWeb / fromClientRequest”Wrap a Web Request (or an HttpClientRequest) as an HttpServerRequest — handy
for adapters and tests.
import { HttpServerRequest } from "effect/unstable/http"
const req = HttpServerRequest.fromWeb( new Request("https://example.com/users", { method: "POST" }))req.method // => "POST"req.url // => "/users"toWeb / toWebResult
Section titled “toWeb / toWebResult”Convert an HttpServerRequest to a Web Request. toWebResult returns a
Result synchronously; toWeb returns an Effect that streams the body using
the current context.
import { Effect } from "effect"import { HttpServerRequest } from "effect/unstable/http"
const program = Effect.gen(function* () { const req = yield* HttpServerRequest.HttpServerRequest const web = yield* HttpServerRequest.toWeb(req) return web // => globalThis.Request})toClientRequest
Section titled “toClientRequest”Convert a server request into an HttpClientRequest, preserving method, headers,
and body — useful for proxying.
import { HttpServerRequest } from "effect/unstable/http"
declare const req: HttpServerRequest.HttpServerRequestconst clientReq = HttpServerRequest.toClientRequest(req)Builds an absolute URL for the request (host from the host header, defaulting
to localhost; https only when x-forwarded-proto is https). Returns
Option.none() for invalid URLs.
import { HttpServerRequest } from "effect/unstable/http"
declare const req: HttpServerRequest.HttpServerRequestHttpServerRequest.toURL(req) // => Option<URL>MaxBodySize
Section titled “MaxBodySize”Re-exported fiber reference controlling the maximum body size accepted while
reading request bodies. Set it with Effect.provideService (or the fiber-ref
combinators) around your handler.
Building responses
Section titled “Building responses”HttpServerResponse values are immutable. A response is a status, optional
statusText, headers, cookies, and an HttpBody. Constructors set practical
defaults — empty is 204, redirect is 302, and body constructors are 200
— and reflect body metadata (content type, content length) into headers.
import { HttpServerResponse } from "effect/unstable/http"
// 200 text/plainconst ok = HttpServerResponse.text("hello")
// 201 with a header, then a cookie (unsafe variant throws on invalid cookies)const created = HttpServerResponse.text("created", { status: 201 }).pipe( HttpServerResponse.setHeader("x-request-id", "abc"), HttpServerResponse.setCookieUnsafe("seen", "1", { path: "/" }))
// 302 redirectconst away = HttpServerResponse.redirect("/login")Most constructors accept an Options object:
interface Options { readonly status?: number | undefined readonly statusText?: string | undefined readonly headers?: Headers.Input | undefined readonly cookies?: Cookies.Cookies | undefined readonly contentType?: string | undefined readonly contentLength?: number | undefined}Body-aware constructors narrow this: Options.WithContent omits
contentType/contentLength (the body decides them), and Options.WithContentType
omits only contentLength.
An empty response, defaulting to status 204.
import { HttpServerResponse } from "effect/unstable/http"
HttpServerResponse.empty() // => 204, no bodyHttpServerResponse.empty({ status: 304 }) // => 304A text/plain response from a string.
import { HttpServerResponse } from "effect/unstable/http"
HttpServerResponse.text("pong")// => 200, content-type: text/plainA JSON response built in Effect; serialization failures become HttpBodyError.
import { HttpServerResponse } from "effect/unstable/http"
HttpServerResponse.json({ ok: true })// => Effect<HttpServerResponse, HttpBodyError>jsonUnsafe
Section titled “jsonUnsafe”A JSON response built synchronously; throws if JSON.stringify fails.
import { HttpServerResponse } from "effect/unstable/http"
HttpServerResponse.jsonUnsafe({ ok: true })// => HttpServerResponse (200, application/json)schemaJson
Section titled “schemaJson”Returns a JSON-response constructor that first encodes the value with a schema.
Can fail with HttpBodyError on encoding or serialization errors.
import { Schema } from "effect"import { HttpServerResponse } from "effect/unstable/http"
const User = Schema.Struct({ id: Schema.Number, name: Schema.String })const userResponse = HttpServerResponse.schemaJson(User)
userResponse({ id: 1, name: "Ada" }, { status: 201 })// => Effect<HttpServerResponse, HttpBodyError>A text/html response. Pass a string for a direct response, or use it as a
template tag to render interpolated values (returning an Effect).
import { HttpServerResponse } from "effect/unstable/http"
HttpServerResponse.html("<h1>Hi</h1>") // => HttpServerResponseHttpServerResponse.html`<h1>${"Ada"}</h1>` // => Effect<HttpServerResponse, ...>htmlStream
Section titled “htmlStream”A streaming text/html response from a template, encoding the template as a byte
stream and supporting streaming interpolated values.
import { Stream } from "effect"import { HttpServerResponse } from "effect/unstable/http"
HttpServerResponse.htmlStream`<ul>${Stream.make("<li>a</li>", "<li>b</li>")}</ul>`// => Effect<HttpServerResponse, never, ...>uint8Array
Section titled “uint8Array”A response whose body is raw bytes (defaults to application/octet-stream).
import { HttpServerResponse } from "effect/unstable/http"
HttpServerResponse.uint8Array(new Uint8Array([1, 2, 3]))// => 200, content-type: application/octet-streamA response with a platform-native body value (a Web Response, Blob, or
ReadableStream) passed through to the adapter.
import { HttpServerResponse } from "effect/unstable/http"
HttpServerResponse.raw(new Blob(["data"]), { contentType: "text/plain" })stream
Section titled “stream”A streaming response from a Stream<Uint8Array, E>.
import { Stream } from "effect"import { HttpServerResponse } from "effect/unstable/http"
const bytes = Stream.make(new Uint8Array([72, 105])) // "Hi"HttpServerResponse.stream(bytes, { contentType: "text/plain" })formData
Section titled “formData”A response whose body is a Web FormData value (the runtime supplies the
multipart boundary).
import { HttpServerResponse } from "effect/unstable/http"
const fd = new FormData()fd.append("name", "Ada")HttpServerResponse.formData(fd)urlParams
Section titled “urlParams”A response encoded as application/x-www-form-urlencoded.
import { HttpServerResponse, UrlParams } from "effect/unstable/http"
HttpServerResponse.urlParams(UrlParams.fromInput({ q: "effect", page: 1 }))// => body: "q=effect&page=1"A streamed file response for a filesystem path. Requires HttpPlatform; can fail
with PlatformError. Supports offset, bytesToRead, and chunkSize.
import { HttpServerResponse } from "effect/unstable/http"
HttpServerResponse.file("/var/www/report.pdf", { contentType: "application/pdf" })// => Effect<HttpServerResponse, PlatformError, HttpPlatform>fileWeb
Section titled “fileWeb”A streamed file response for a Web File-like value. Requires HttpPlatform.
import { HttpServerResponse } from "effect/unstable/http"
declare const file: FileHttpServerResponse.fileWeb(file)// => Effect<HttpServerResponse, never, HttpPlatform>setStatus
Section titled “setStatus”Returns a response with a new status code (and optional status text).
import { HttpServerResponse } from "effect/unstable/http"
HttpServerResponse.text("nope").pipe(HttpServerResponse.setStatus(403, "Forbidden"))// => status 403, statusText "Forbidden"setHeader / setHeaders
Section titled “setHeader / setHeaders”Set one header, or set many at once.
import { HttpServerResponse } from "effect/unstable/http"
HttpServerResponse.empty().pipe( HttpServerResponse.setHeader("cache-control", "no-store"), HttpServerResponse.setHeaders({ "x-a": "1", "x-b": "2" }))setBody
Section titled “setBody”Replace the response body. Content-type / content-length headers follow the new body’s metadata.
import { HttpBody, HttpServerResponse } from "effect/unstable/http"
HttpServerResponse.empty().pipe( HttpServerResponse.setBody(HttpBody.text("replaced")))Cookie combinators
Section titled “Cookie combinators”Cookies are managed on the response’s cookie collection. The safe variants return
an Effect failing with CookiesError; the *Unsafe variants throw.
import { Effect } from "effect"import { Cookies, HttpServerResponse } from "effect/unstable/http"
const withCookies = Effect.gen(function* () { let res = HttpServerResponse.empty()
// setCookie / setCookieUnsafe: add one cookie res = yield* HttpServerResponse.setCookie(res, "session", "abc123", { httpOnly: true, sameSite: "lax", path: "/" }) res = HttpServerResponse.setCookieUnsafe(res, "theme", "dark")
// setCookies / setCookiesUnsafe: add many at once res = yield* HttpServerResponse.setCookies(res, [ ["a", "1"], ["b", "2", { path: "/" }] ]) res = HttpServerResponse.setCookiesUnsafe(res, [["c", "3"]])
// expireCookie / expireCookieUnsafe: emit an already-expired cookie res = yield* HttpServerResponse.expireCookie(res, "session", { path: "/" }) res = HttpServerResponse.expireCookieUnsafe(res, "theme")
// collection-level operations res = HttpServerResponse.removeCookie(res, "a") // drop one cookie res = HttpServerResponse.mergeCookies(res, Cookies.empty) // merge in a collection res = HttpServerResponse.replaceCookies(res, Cookies.empty) // replace all res = HttpServerResponse.updateCookies(res, (c) => Cookies.remove(c, "b")) // edit via fn
return res})| Combinator | Effectful? | Description |
|---|---|---|
setCookie | yes | Add one cookie, validating it |
setCookieUnsafe | no | Add one cookie, throwing on invalid input |
setCookies | yes | Add many cookies from [name, value, options?] tuples |
setCookiesUnsafe | no | Add many cookies, throwing on invalid input |
expireCookie | yes | Add an expired cookie (empty value, Max-Age=0) |
expireCookieUnsafe | no | Expire a cookie, throwing on invalid input |
removeCookie | no | Remove one cookie from the collection |
mergeCookies | no | Merge another Cookies collection in |
replaceCookies | no | Replace the whole cookie collection |
updateCookies | no | Map the cookie collection with a function |
Converting responses
Section titled “Converting responses”toWeb produces a Web Response (cookies become Set-Cookie headers). fromWeb
wraps a Web Response. toClientResponse / fromClientResponse bridge to the
client model.
import { HttpServerResponse } from "effect/unstable/http"
const res = HttpServerResponse.text("hi")const web = HttpServerResponse.toWeb(res) // => globalThis.Responseconst back = HttpServerResponse.fromWeb(web) // => HttpServerResponseisHttpServerResponse is the runtime guard.
Respondable
Section titled “Respondable”HttpServerRespondable.Respondable lets a value describe its own response. Domain
errors and API errors commonly implement it so error handling can ask the value
how to render itself instead of building a response at every call site.
import { Effect } from "effect"import { HttpServerRespondable, HttpServerResponse } from "effect/unstable/http"
class NotFoundError { // The protocol method returns Effect<HttpServerResponse, unknown> [HttpServerRespondable.symbol]() { return Effect.succeed(HttpServerResponse.text("not found", { status: 404 })) }}
HttpServerRespondable.isRespondable(new NotFoundError()) // => truetoResponse
Section titled “toResponse”Converts a Respondable (or an existing HttpServerResponse) into a response.
Conversion failures become defects.
import { HttpServerRespondable } from "effect/unstable/http"
declare const value: HttpServerRespondable.RespondableHttpServerRespondable.toResponse(value)// => Effect<HttpServerResponse>The module also exposes toResponseOrElse and toResponseOrElseDefect, which
fall back to a supplied response for unknown failures — mapping SchemaError to
400 and NoSuchElementError to 404.
HttpBody reference
Section titled “HttpBody reference”HttpBody is the transport-facing body representation shared by requests and
responses. You rarely build one directly (the response constructors do it for
you), but the constructors are useful with setBody and when proxying. Variants:
Empty, Raw, Uint8Array, FormData, Stream.
The singleton empty body.
import { HttpBody } from "effect/unstable/http"
HttpBody.empty._tag // => "Empty"UTF-8 text body, defaulting to text/plain.
import { HttpBody } from "effect/unstable/http"
HttpBody.text("hello").contentType // => "text/plain"JSON body in Effect; JSON.stringify failures become HttpBodyError.
import { HttpBody } from "effect/unstable/http"
HttpBody.json({ a: 1 }) // => Effect<HttpBody.Uint8Array, HttpBodyError>jsonUnsafe
Section titled “jsonUnsafe”JSON body built synchronously; throws on serialization failure.
import { HttpBody } from "effect/unstable/http"
HttpBody.jsonUnsafe({ a: 1 }).contentType // => "application/json"uint8Array
Section titled “uint8Array”Byte-array body, defaulting to application/octet-stream.
import { HttpBody } from "effect/unstable/http"
HttpBody.uint8Array(new Uint8Array([1, 2])).contentLength // => 2Wraps an arbitrary runtime body value with optional content metadata.
import { HttpBody } from "effect/unstable/http"
HttpBody.raw(new Blob(["x"]), { contentType: "text/plain", contentLength: 1 })stream
Section titled “stream”Streaming body from a Stream<Uint8Array>, defaulting to
application/octet-stream; content length is optional.
import { Stream } from "effect"import { HttpBody } from "effect/unstable/http"
HttpBody.stream(Stream.make(new Uint8Array([1])), "text/plain")formData
Section titled “formData”Wraps a Web FormData value as a body (content type left unset for the boundary).
import { HttpBody } from "effect/unstable/http"
const fd = new FormData()fd.append("name", "Ada")HttpBody.formData(fd)formDataRecord
Section titled “formDataRecord”Builds a FormData body from a record. Arrays append repeated keys, primitives
are stringified, File/Blob values are appended directly, nullish values are
skipped.
import { HttpBody } from "effect/unstable/http"
HttpBody.formDataRecord({ name: "Ada", tags: ["a", "b"], skip: null })// => FormData with name=Ada, tags=a, tags=burlParams
Section titled “urlParams”application/x-www-form-urlencoded body from UrlParams.
import { HttpBody, UrlParams } from "effect/unstable/http"
HttpBody.urlParams(UrlParams.fromInput({ q: "effect" }))// => contentType: application/x-www-form-urlencodedfile / fileFromInfo
Section titled “file / fileFromInfo”Stream a file from a path. file stats the file to set content length;
fileFromInfo reuses already-known File.Info. Both require FileSystem and may
fail with PlatformError.
import { HttpBody } from "effect/unstable/http"
HttpBody.file("/tmp/data.bin")// => Effect<HttpBody.Stream, PlatformError, FileSystem>Body classes & HttpBodyError
Section titled “Body classes & HttpBodyError”Empty, Raw, Uint8Array, FormData, and Stream are the body variant
classes (each carries a _tag). isHttpBody is the runtime guard. HttpBodyError
is a tagged error with a reason of JsonError or SchemaError.
import { HttpBody } from "effect/unstable/http"
const body = HttpBody.text("hi")HttpBody.isHttpBody(body) // => truebody._tag // => "Uint8Array"Headers reference
Section titled “Headers reference”Headers is an immutable, lowercase-keyed string map. Names are normalized
because HTTP header names are case-insensitive, and combinators return new
collections.
import { Option } from "effect"import { Headers } from "effect/unstable/http"
const h = Headers.fromInput({ "Content-Type": "application/json", accept: ["application/json", "text/plain"]})Headers.get(h, "content-type") // => Option.some("application/json")h["accept"] // => "application/json, text/plain" (array values joined with ", ")The empty header collection.
import { Headers } from "effect/unstable/http"
Headers.empty // => {} (Headers)fromInput
Section titled “fromInput”Builds Headers from a record or iterable of entries; names are lowercased,
array values joined with ", ", undefined omitted.
import { Headers } from "effect/unstable/http"
Headers.fromInput({ "X-Trace": "1", drop: undefined })["x-trace"] // => "1"fromRecordUnsafe
Section titled “fromRecordUnsafe”Treats an existing record as Headers without normalizing names. Caller must
supply lowercase keys.
import { Headers } from "effect/unstable/http"
Headers.fromRecordUnsafe({ "content-type": "text/plain" })Reads a header value safely (lowercases the lookup).
import { Headers } from "effect/unstable/http"
Headers.get(Headers.fromInput({ a: "1" }), "A") // => Option.some("1")Returns whether a header is present.
import { Headers } from "effect/unstable/http"
Headers.has(Headers.fromInput({ a: "1" }), "A") // => trueReturns a new collection with one header set.
import { Headers } from "effect/unstable/http"
Headers.set(Headers.empty, "Authorization", "Bearer x")["authorization"]// => "Bearer x"setAll
Section titled “setAll”Sets many headers at once (input normalized via fromInput).
import { Headers } from "effect/unstable/http"
Headers.setAll(Headers.empty, { "X-A": "1", "X-B": "2" })Merges two Headers; the second wins on conflicts.
import { Headers } from "effect/unstable/http"
Headers.merge(Headers.fromInput({ a: "1" }), Headers.fromInput({ a: "2", b: "3" }))// => { a: "2", b: "3" }remove
Section titled “remove”Removes one header by name.
import { Headers } from "effect/unstable/http"
Headers.remove(Headers.fromInput({ a: "1", b: "2" }), "A") // => { b: "2" }removeMany
Section titled “removeMany”Removes several headers by name.
import { Headers } from "effect/unstable/http"
Headers.removeMany(Headers.fromInput({ a: "1", b: "2", c: "3" }), ["a", "c"])// => { b: "2" }redact & CurrentRedactedNames
Section titled “redact & CurrentRedactedNames”redact returns a record with selected header values wrapped in Redacted.
CurrentRedactedNames is the context reference listing which names are masked on
inspection (defaults: authorization, cookie, set-cookie, x-api-key).
import { Headers } from "effect/unstable/http"
Headers.redact(Headers.fromInput({ authorization: "secret", a: "1" }), "authorization")// => { authorization: Redacted(<redacted>), a: "1" }Equivalence, HeadersSchema, isHeaders
Section titled “Equivalence, HeadersSchema, isHeaders”Equivalence compares two Headers by names and values; HeadersSchema is the
schema encoding Headers as a string record; isHeaders is the runtime guard.
import { Headers } from "effect/unstable/http"
Headers.isHeaders(Headers.empty) // => trueUrlParams reference
Section titled “UrlParams reference”UrlParams is an ordered list of [key, value] string pairs that preserves
duplicate keys and order. Used for query strings and URL-encoded bodies.
An empty UrlParams.
import { UrlParams } from "effect/unstable/http"
UrlParams.empty.params // => []Builds from ordered string pairs as-is (no coercion).
import { UrlParams } from "effect/unstable/http"
UrlParams.make([["a", "1"], ["a", "2"]]) // keeps both "a" pairsfromInput
Section titled “fromInput”Builds from a record, iterable, or URLSearchParams. Primitives become strings,
arrays become repeated params, nested records use bracket notation, undefined
omitted.
import { UrlParams } from "effect/unstable/http"
UrlParams.toString(UrlParams.fromInput({ a: 1, tags: ["x", "y"] }))// => "a=1&tags=x&tags=y"Replaces all values for a key with a single value (appended at the end).
import { UrlParams } from "effect/unstable/http"
UrlParams.toString(UrlParams.set(UrlParams.fromInput({ a: "1" }), "a", "2"))// => "a=2"setAll
Section titled “setAll”Replaces values for the keys present in the input; other params are preserved.
import { UrlParams } from "effect/unstable/http"
UrlParams.toString(UrlParams.setAll(UrlParams.fromInput({ a: "1", b: "2" }), { a: "9" }))// => "a=9&b=2"append
Section titled “append”Appends a value without removing existing values for the key.
import { UrlParams } from "effect/unstable/http"
UrlParams.toString(UrlParams.append(UrlParams.fromInput({ a: "1" }), "a", "2"))// => "a=1&a=2"appendAll
Section titled “appendAll”Appends all params from the input, preserving existing ones.
import { UrlParams } from "effect/unstable/http"
UrlParams.toString(UrlParams.appendAll(UrlParams.fromInput({ a: "1" }), { b: "2" }))// => "a=1&b=2"remove
Section titled “remove”Removes all values for a key.
import { UrlParams } from "effect/unstable/http"
UrlParams.toString(UrlParams.remove(UrlParams.fromInput({ a: "1", b: "2" }), "a"))// => "b=2"getAll / getFirst / getLast
Section titled “getAll / getFirst / getLast”Read all values for a key, or the first / last value as an Option.
import { UrlParams } from "effect/unstable/http"
const p = UrlParams.fromInput({ a: ["1", "2"] })UrlParams.getAll(p, "a") // => ["1", "2"]UrlParams.getFirst(p, "a") // => Option.some("1")UrlParams.getLast(p, "a") // => Option.some("2")toRecord / toReadonlyRecord
Section titled “toRecord / toReadonlyRecord”Build a record; single-value keys map to a string, repeated keys to a non-empty array.
import { UrlParams } from "effect/unstable/http"
UrlParams.toRecord(UrlParams.fromInput({ a: 1, e: [1, 2, 3] }))// => { a: "1", e: ["1", "2", "3"] }toString
Section titled “toString”Serializes to a query string (no leading ?).
import { UrlParams } from "effect/unstable/http"
UrlParams.toString(UrlParams.fromInput({ q: "a b" })) // => "q=a+b"makeUrl
Section titled “makeUrl”Builds a URL from a base string plus params and an optional hash; returns a
Result failing with UrlParamsError.
import { UrlParams } from "effect/unstable/http"
UrlParams.makeUrl("https://x.dev/search", UrlParams.fromInput({ q: "effect" }), undefined)// => Result.success(URL "https://x.dev/search?q=effect")transform
Section titled “transform”Maps the underlying ordered pairs.
import { UrlParams } from "effect/unstable/http"
UrlParams.transform( UrlParams.fromInput({ a: "1" }), (pairs) => [...pairs, ["b", "2"]])schemaRecord / schemaJsonField
Section titled “schemaRecord / schemaJsonField”schemaRecord decodes UrlParams into a record schema; schemaJsonField reads
one field’s value as JSON. Both compose with Schema.decodeTo.
import { Schema } from "effect"import { UrlParams } from "effect/unstable/http"
const toStruct = UrlParams.schemaRecord.pipe( Schema.decodeTo(Schema.Struct({ some: Schema.String, number: Schema.FiniteFromString })))Schema.decodeSync(toStruct)(UrlParams.fromInput({ some: "value", number: 42 }))// => { some: "value", number: 42 }Equivalence, UrlParamsSchema, UrlParamsError, isUrlParams
Section titled “Equivalence, UrlParamsSchema, UrlParamsError, isUrlParams”Equivalence is order-sensitive; UrlParamsSchema encodes UrlParams as an
array of string tuples; UrlParamsError is the tagged error from makeUrl;
isUrlParams is the guard.
import { UrlParams } from "effect/unstable/http"
UrlParams.isUrlParams(UrlParams.empty) // => trueUrl reference
Section titled “Url reference”The Url module keeps the WHATWG URL as the representation and adds safe
parsing plus immutable, pipeable setters. Setters never mutate the input; each
clones the URL and assigns on the clone.
fromString
Section titled “fromString”Parses a URL string safely, returning a Result (fails with
IllegalArgumentError). Accepts an optional base for relative URLs.
import { Url } from "effect/unstable/http"
Url.fromString("/path", "https://example.com")// => Result.success(URL "https://example.com/path")mutate
Section titled “mutate”Applies a callback to a clone of the URL, allowing several assignments at once.
import { Url } from "effect/unstable/http"
Url.mutate(new URL("https://example.com"), (url) => { url.username = "user" url.password = "pass"}).toString()// => "https://user:pass@example.com/"urlParams
Section titled “urlParams”Reads the query parameters as UrlParams.
import { Url } from "effect/unstable/http"
Url.urlParams(new URL("https://example.com?foo=bar")).params// => [["foo", "bar"]]setUrlParams
Section titled “setUrlParams”Replaces the query parameters with the given UrlParams.
import { Url, UrlParams } from "effect/unstable/http"
Url.setUrlParams(new URL("https://example.com?foo=bar"), UrlParams.fromInput({ key: "value" })).toString()// => "https://example.com/?key=value"modifyUrlParams
Section titled “modifyUrlParams”Reads the params, applies a function, and writes them back.
import { Url, UrlParams } from "effect/unstable/http"
Url.modifyUrlParams(new URL("https://example.com?foo=bar"), UrlParams.append("key", "value")).toString()// => "https://example.com/?foo=bar&key=value"Component setters
Section titled “Component setters”Pipeable setters for each URL component. setPassword also accepts Redacted
input (the resulting URL still contains the real credential).
import { Url } from "effect/unstable/http"
const base = new URL("https://example.com")Url.setHash(base, "#section") // url.hashUrl.setHost(base, "api.example.com:8080") // host (domain + port)Url.setHostname(base, "api.example.com") // domain onlyUrl.setHref(base, "https://other.dev/") // replace whole URLUrl.setPassword(base, "pass") // password (string | Redacted)Url.setPathname(base, "/v1/users") // pathUrl.setPort(base, 8080) // port (string | number)Url.setProtocol(base, "http:") // protocolUrl.setSearch(base, "?q=1") // query stringUrl.setUsername(base, "user") // usernameCookies reference
Section titled “Cookies reference”Cookies is an immutable collection keyed by cookie name (at most one cookie per
name). A Cookie stores both the decoded value and the encoded valueEncoded.
import { Cookies } from "effect/unstable/http"
const cookies = Cookies.setUnsafe(Cookies.empty, "session", "abc123", { httpOnly: true, path: "/", sameSite: "lax", secure: true})Cookies.toSetCookieHeaders(cookies)// => ["session=abc123; Path=/; HttpOnly; Secure; SameSite=Lax"]The empty cookie collection.
import { Cookies } from "effect/unstable/http"
Cookies.isEmpty(Cookies.empty) // => truefromIterable / fromReadonlyRecord / fromSetCookie
Section titled “fromIterable / fromReadonlyRecord / fromSetCookie”Build a collection from an iterable of Cookie, a record keyed by name, or a set
of Set-Cookie header strings.
import { Cookies } from "effect/unstable/http"
Cookies.fromSetCookie(["a=1; Path=/", "b=2"])// => Cookies containing a and bmakeCookie / makeCookieUnsafe
Section titled “makeCookie / makeCookieUnsafe”Create a single Cookie, validating name, value, domain, path, and finite
max-age. makeCookie returns a Result; the unsafe variant throws.
import { Cookies } from "effect/unstable/http"
Cookies.makeCookie("a", "1", { path: "/" })// => Result.success(Cookie)Cookies.makeCookieUnsafe("a", "1") // => Cookie (throws on invalid input)set / setUnsafe
Section titled “set / setUnsafe”Create and add a cookie by name and value. set returns a Result; setUnsafe
throws.
import { Cookies } from "effect/unstable/http"
Cookies.set(Cookies.empty, "a", "1") // => Result.success(Cookies)Cookies.setUnsafe(Cookies.empty, "a", "1") // => CookiessetAll / setAllUnsafe
Section titled “setAll / setAllUnsafe”Create and add many cookies from [name, value, options?] tuples. setAll
returns the first CookiesError on failure; setAllUnsafe throws.
import { Cookies } from "effect/unstable/http"
Cookies.setAll(Cookies.empty, [["a", "1"], ["b", "2", { path: "/" }]])// => Result.success(Cookies)setCookie / setAllCookie
Section titled “setCookie / setAllCookie”Add already-built Cookie values (one, or an iterable).
import { Cookies } from "effect/unstable/http"
const a = Cookies.makeCookieUnsafe("a", "1")Cookies.setCookie(Cookies.empty, a) // add one CookieCookies.setAllCookie(Cookies.empty, [a]) // add many Cookiesget / getValue
Section titled “get / getValue”Look up a Cookie (or its decoded value) by name as an Option.
import { Cookies } from "effect/unstable/http"
const c = Cookies.setUnsafe(Cookies.empty, "a", "1")Cookies.get(c, "a") // => Option.some(Cookie)Cookies.getValue(c, "a") // => Option.some("1")Combine two collections; the second collection wins on name conflicts.
import { Cookies } from "effect/unstable/http"
Cookies.merge( Cookies.setUnsafe(Cookies.empty, "a", "1"), Cookies.setUnsafe(Cookies.empty, "b", "2"))remove
Section titled “remove”Remove a cookie by name.
import { Cookies } from "effect/unstable/http"
Cookies.remove(Cookies.setUnsafe(Cookies.empty, "a", "1"), "a")// => empty collectionexpireCookie / expireCookieUnsafe
Section titled “expireCookie / expireCookieUnsafe”Add an expired cookie (empty value, Max-Age=0, epoch Expires). expireCookie
returns a Result; the unsafe variant throws.
import { Cookies } from "effect/unstable/http"
Cookies.expireCookieUnsafe(Cookies.empty, "session", { path: "/" })// => Set-Cookie: session=; Max-Age=0; Path=/; Expires=...toCookieHeader / toSetCookieHeaders / toRecord
Section titled “toCookieHeader / toSetCookieHeaders / toRecord”toCookieHeader builds an outbound request Cookie header. toSetCookieHeaders
builds response Set-Cookie header strings. toRecord returns decoded values
keyed by name.
import { Cookies } from "effect/unstable/http"
const c = Cookies.setAllUnsafe(Cookies.empty, [["a", "1"], ["b", "2"]])Cookies.toCookieHeader(c) // => "a=1; b=2"Cookies.toSetCookieHeaders(c) // => ["a=1", "b=2"]Cookies.toRecord(c) // => { a: "1", b: "2" }parseHeader
Section titled “parseHeader”Parses a request Cookie header into a record of name/value pairs.
import { Cookies } from "effect/unstable/http"
Cookies.parseHeader("a=1; b=2") // => { a: "1", b: "2" }serializeCookie
Section titled “serializeCookie”Serializes a single Cookie into its Set-Cookie string.
import { Cookies } from "effect/unstable/http"
Cookies.serializeCookie(Cookies.makeCookieUnsafe("a", "1", { path: "/", httpOnly: true }))// => "a=1; Path=/; HttpOnly"schemaRecord
Section titled “schemaRecord”Schema transforming Cookies into a record of decoded string values keyed by
name.
import { Cookies } from "effect/unstable/http"
Cookies.schemaRecord // => Schema decoding Cookies → Record<string, string>isCookie / isCookies / isEmpty & CookiesError
Section titled “isCookie / isCookies / isEmpty & CookiesError”isCookie / isCookies are runtime guards; isEmpty checks for no cookies;
CookiesError is the tagged error (reason._tag is InvalidCookieName,
InvalidCookieValue, InvalidCookieDomain, InvalidCookiePath, or
CookieInfinityMaxAge).
import { Cookies } from "effect/unstable/http"
Cookies.isCookies(Cookies.empty) // => trueCookies.isEmpty(Cookies.empty) // => true