Documentation Index
Fetch the complete documentation index at: https://mintlify.com/effect-TS/effect-smol/llms.txt
Use this file to discover all available pages before exploring further.
The HttpServer module provides abstractions for building HTTP servers with Effect. It offers a composable, type-safe way to define server applications with middleware support and automatic resource management.
Overview
HttpServer features:
- Composable middleware architecture
- Automatic scope management
- Type-safe request/response handling
- Platform-agnostic abstractions
- Built-in static file serving
- Testing utilities
Basic Usage
import { Effect, HttpServer, HttpServerResponse } from "effect"
const app = Effect.gen(function*() {
const request = yield* HttpServerRequest.HttpServerRequest
return HttpServerResponse.json({
message: "Hello, World!",
path: request.url
})
})
const server = HttpServer.serve(app)
Server Setup
serve
Creates a Layer that serves an HTTP application.
serve: {
(): <E, R>(
effect: Effect.Effect<HttpServerResponse, E, R>
) => Layer.Layer<never, never, HttpServer | Exclude<R, HttpServerRequest | Scope>>
<E, R, App>(
middleware: HttpMiddleware.Applied<App, E, R>
): (effect: Effect.Effect<HttpServerResponse, E, R>) => Layer.Layer<...>
<E, R>(
effect: Effect.Effect<HttpServerResponse, E, R>
): Layer.Layer<never, never, HttpServer | Exclude<R, HttpServerRequest | Scope>>
<E, R, App>(
effect: Effect.Effect<HttpServerResponse, E, R>,
middleware: HttpMiddleware.Applied<App, E, R>
): Layer.Layer<...>
}
Example:
import { Effect, HttpServer, HttpServerResponse, Layer } from "effect"
const app = HttpServerResponse.text("Hello!")
const ServerLive = HttpServer.serve(app)
const program = Effect.gen(function*() {
yield* Effect.log("Server is running...")
yield* Effect.never // Keep server alive
})
const runnable = Effect.provide(program, ServerLive)
serveEffect
Serves an HTTP application as an Effect.
serveEffect: {
<E, R>(
effect: Effect.Effect<HttpServerResponse, E, R>
): Effect.Effect<void, never, Scope | HttpServer | Exclude<R, HttpServerRequest>>
}
Example:
import { Effect, HttpServer, HttpServerResponse } from "effect"
const app = HttpServerResponse.text("Hello!")
const program = Effect.scoped(
Effect.gen(function*() {
yield* HttpServer.serveEffect(app)
yield* Effect.log("Server started")
yield* Effect.never
})
)
Server Address Types
Servers can bind to TCP or Unix socket addresses:
type Address = TcpAddress | UnixAddress
interface TcpAddress {
readonly _tag: "TcpAddress"
readonly hostname: string
readonly port: number
}
interface UnixAddress {
readonly _tag: "UnixAddress"
readonly path: string
}
Formats a server address as a string.
formatAddress: (address: Address) => string
Example:
import { HttpServer } from "effect"
const tcpAddr: HttpServer.TcpAddress = {
_tag: "TcpAddress",
hostname: "localhost",
port: 3000
}
const formatted = HttpServer.formatAddress(tcpAddr)
// "http://localhost:3000"
const unixAddr: HttpServer.UnixAddress = {
_tag: "UnixAddress",
path: "/tmp/server.sock"
}
const unixFormatted = HttpServer.formatAddress(unixAddr)
// "unix:///tmp/server.sock"
logAddress
Logs the server address when it starts.
logAddress: Effect.Effect<void, never, HttpServer>
Example:
import { Effect, HttpServer, Layer } from "effect"
const ServerLive = HttpServer.serve(app).pipe(
Layer.tap(() => HttpServer.logAddress)
)
withLogAddress
Wraps a layer to log the server address on startup.
withLogAddress: <A, E, R>(
layer: Layer.Layer<A, E, R>
) => Layer.Layer<A, E, R | Exclude<HttpServer, A>>
Example:
import { HttpServer, Layer } from "effect"
const ServerLive = Layer.mergeAll(
HttpServer.serve(app),
PlatformNode.layer
).pipe(
HttpServer.withLogAddress
)
// Logs: "Listening on http://localhost:3000"
Creating Custom Servers
make
Creates a custom HttpServer implementation.
make: (
options: {
readonly serve: (
httpEffect: Effect.Effect<HttpServerResponse, unknown, HttpServerRequest | Scope>,
middleware?: HttpMiddleware
) => Effect.Effect<void, never, Scope>
readonly address: Address
}
) => HttpServer["Service"]
Example:
import { Effect, HttpServer, Scope } from "effect"
const customServer = HttpServer.make({
address: {
_tag: "TcpAddress",
hostname: "0.0.0.0",
port: 8080
},
serve: (httpEffect, middleware) =>
Effect.scoped(
Effect.gen(function*() {
// Custom server implementation
// Set up HTTP listener
// Process requests with httpEffect
})
)
})
Request Handling
Accessing the Request
import { Effect, HttpServerRequest, HttpServerResponse } from "effect"
const app = Effect.gen(function*() {
const request = yield* HttpServerRequest.HttpServerRequest
return HttpServerResponse.json({
method: request.method,
url: request.url,
headers: request.headers
})
})
Request Properties
interface HttpServerRequest {
readonly url: string
readonly originalUrl: string
readonly method: HttpMethod
readonly headers: Headers
readonly cookies: ReadonlyRecord<string, string>
readonly remoteAddress: Option<string>
// Body parsing
readonly json: Effect.Effect<unknown, HttpServerError>
readonly text: Effect.Effect<string, HttpServerError>
readonly arrayBuffer: Effect.Effect<ArrayBuffer, HttpServerError>
readonly formData: Effect.Effect<FormData, HttpServerError>
// Multipart
readonly multipart: Effect.Effect<Multipart.Persisted, MultipartError, Scope | FileSystem | Path>
readonly multipartStream: Stream.Stream<Multipart.Part, MultipartError>
// WebSocket upgrade
readonly upgrade: Effect.Effect<Socket, HttpServerError>
}
Response Creation
See the HttpServerResponse module for comprehensive response APIs:
import { HttpServerResponse } from "effect"
// Text response
const text = HttpServerResponse.text("Hello!")
// JSON response
const json = HttpServerResponse.json({ message: "Success" })
// HTML response
const html = HttpServerResponse.html("<h1>Hello</h1>")
// File response
const file = HttpServerResponse.file("./public/index.html")
// Stream response
const stream = HttpServerResponse.stream(dataStream)
// Custom response
const custom = HttpServerResponse.make({
status: 201,
headers: Headers.fromInput({ "content-type": "application/json" }),
body: HttpBody.json({ id: 123 })
})
Middleware
Middleware allows composing cross-cutting concerns:
import { Effect, HttpServer, HttpServerRequest, HttpServerResponse } from "effect"
// Logging middleware
const loggingMiddleware = HttpMiddleware.make((app) =>
Effect.gen(function*() {
const request = yield* HttpServerRequest.HttpServerRequest
yield* Effect.log(`${request.method} ${request.url}`)
const response = yield* app
yield* Effect.log(`Response: ${response.status}`)
return response
})
)
// Apply middleware
const ServerLive = HttpServer.serve(app, loggingMiddleware)
Testing
makeTestClient
Creates an HttpClient configured to make requests to the test server.
makeTestClient: Effect.Effect<
HttpClient.HttpClient,
never,
HttpServer | HttpClient.HttpClient
>
Example:
import { Effect, HttpClient, HttpServer, HttpServerResponse, Layer } from "effect"
const app = HttpServerResponse.json({ message: "Hello!" })
const test = Effect.gen(function*() {
const client = yield* HttpServer.makeTestClient
const response = yield* client.get("/api/users")
const data = yield* response.json
// Assert response...
}).pipe(
Effect.provide(HttpServer.serve(app))
)
layerTestClient
A Layer version of makeTestClient.
layerTestClient: Layer.Layer<
HttpClient.HttpClient,
never,
HttpServer | HttpClient.HttpClient
>
Example:
import { Effect, HttpClient, HttpServer, Layer } from "effect"
const app = HttpServerResponse.text("Hello!")
const TestLive = Layer.mergeAll(
HttpServer.serve(app),
HttpServer.layerTestClient,
HttpClient.layer
)
const test = Effect.gen(function*() {
const client = yield* HttpClient.HttpClient
const response = yield* client.get("/")
const text = yield* response.text
// Assert text === "Hello!"
}).pipe(
Effect.provide(TestLive)
)
layerServices
Provides default platform services for testing.
layerServices: Layer.Layer<
Path.Path | HttpPlatform.HttpPlatform | FileSystem.FileSystem | Etag.Generator
>
Example:
import { Effect, HttpServer, Layer } from "effect"
const TestLive = Layer.mergeAll(
HttpServer.serve(app),
HttpServer.layerServices
)
Router Integration
HttpServer works seamlessly with HttpRouter for routing:
import { Effect, HttpRouter, HttpServer, HttpServerResponse } from "effect"
const router = HttpRouter.empty.pipe(
HttpRouter.get("/", HttpServerResponse.text("Home")),
HttpRouter.get("/users",
Effect.map(
getUsersFromDb,
(users) => HttpServerResponse.json(users)
)
),
HttpRouter.post("/users",
Effect.gen(function*() {
const request = yield* HttpServerRequest.HttpServerRequest
const body = yield* request.json
const user = yield* createUser(body)
return HttpServerResponse.json(user, { status: 201 })
})
)
)
const ServerLive = HttpServer.serve(
HttpRouter.toHttpApp(router)
)
Static Files
Serve static files with HttpStaticServer:
import { HttpRouter, HttpServer, HttpStaticServer } from "effect"
const router = HttpRouter.empty.pipe(
HttpRouter.mount("/static", HttpStaticServer.fromDirectory("./public"))
)
const ServerLive = HttpServer.serve(
HttpRouter.toHttpApp(router)
)
WebSocket Support
Upgrade connections to WebSocket:
import { Effect, HttpServerRequest, Socket, Stream } from "effect"
const wsHandler = Effect.gen(function*() {
const request = yield* HttpServerRequest.HttpServerRequest
const socket = yield* request.upgrade
// Echo server
yield* Stream.run(
socket.stream,
Socket.toChannel(socket)
)
})
HttpServer is platform-agnostic. Use platform-specific layers:
Node.js
import { Effect, HttpServer } from "effect"
import { NodeHttpServer, NodeRuntime } from "@effect/platform-node"
const ServerLive = HttpServer.serve(app).pipe(
Layer.provide(NodeHttpServer.layer({ port: 3000 }))
)
const program = Effect.provide(myProgram, ServerLive)
NodeRuntime.runMain(program)
Bun
import { HttpServer } from "effect"
import { BunHttpServer, BunRuntime } from "@effect/platform-bun"
const ServerLive = HttpServer.serve(app).pipe(
Layer.provide(BunHttpServer.layer({ port: 3000 }))
)
BunRuntime.runMain(Effect.provide(myProgram, ServerLive))
Complete Example
import { Console, Effect, HttpRouter, HttpServer, HttpServerResponse, Layer } from "effect"
import { NodeHttpServer, NodeRuntime } from "@effect/platform-node"
// Define routes
const router = HttpRouter.empty.pipe(
HttpRouter.get("/", HttpServerResponse.text("Welcome!")),
HttpRouter.get("/users",
Effect.gen(function*() {
const users = yield* getUsersFromDb
return HttpServerResponse.json(users)
})
),
HttpRouter.post("/users",
Effect.gen(function*() {
const request = yield* HttpServerRequest.HttpServerRequest
const data = yield* request.json
const user = yield* createUser(data)
return HttpServerResponse.json(user, { status: 201 })
})
)
)
// Create server layer
const ServerLive = HttpServer.serve(HttpRouter.toHttpApp(router)).pipe(
Layer.provide(NodeHttpServer.layer({ port: 3000 })),
HttpServer.withLogAddress
)
// Run server
const program = Effect.gen(function*() {
yield* Console.log("Server starting...")
yield* Effect.never
})
NodeRuntime.runMain(
Effect.provide(program, ServerLive)
)
Type Reference
HttpServer
class HttpServer extends Service<HttpServer, {
readonly serve: {
<E, R>(effect: Effect.Effect<HttpServerResponse, E, R>): Effect.Effect<
void,
never,
Exclude<R, HttpServerRequest> | Scope
>
<E, R, App>(
effect: Effect.Effect<HttpServerResponse, E, R>,
middleware: HttpMiddleware.Applied<App, E, R>
): Effect.Effect<
void,
never,
Exclude<R, HttpServerRequest> | Scope
>
}
readonly address: Address
}> {}
See Also