effect-http-server

HttpApi ├── HttpApiGroup │ ├── HttpApiEndpoint │ └── HttpApiEndpoint

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "effect-http-server" with this command: npx skills add tstelzer/skills/tstelzer-skills-effect-http-server

Structure

HttpApi ├── HttpApiGroup │ ├── HttpApiEndpoint │ └── HttpApiEndpoint

Defining Endpoints

import { HttpApiEndpoint, HttpApiSchema, HttpApiError } from "@effect/platform" import { Schema } from "effect"

const User = Schema.Struct({ id: Schema.Number, name: Schema.String, createdAt: Schema.DateTimeUtc })

const idParam = HttpApiSchema.param("id", Schema.NumberFromString)

// GET with path param (template string syntax) const getUser = HttpApiEndpoint.get("getUser")/users/${idParam} .addSuccess(User) .addError(HttpApiError.NotFound) // 404

// GET with URL params const listUsers = HttpApiEndpoint.get("listUsers", "/users") .setUrlParams(Schema.Struct({ page: Schema.NumberFromString, sort: Schema.String })) .addSuccess(Schema.Array(User))

// POST with payload const createUser = HttpApiEndpoint.post("createUser", "/users") .setPayload(Schema.Struct({ name: Schema.String })) .addSuccess(User, { status: 201 })

// DELETE const deleteUser = HttpApiEndpoint.del("deleteUser")/users/${idParam}

// PATCH const updateUser = HttpApiEndpoint.patch("updateUser")/users/${idParam} .setPayload(Schema.Struct({ name: Schema.String })) .addSuccess(User)

// Headers (keys must be lowercase) const withHeaders = HttpApiEndpoint.get("withHeaders", "/") .setHeaders(Schema.Struct({ "x-api-key": Schema.String, "x-request-id": Schema.String }))

Grouping & API Assembly

import { HttpApi, HttpApiGroup } from "@effect/platform"

class UserNotFound extends Schema.TaggedError<UserNotFound>()("UserNotFound", {}) {} class Unauthorized extends Schema.TaggedError<Unauthorized>()("Unauthorized", {}) {}

const usersGroup = HttpApiGroup.make("users") .add(getUser) .add(listUsers) .add(createUser) .add(deleteUser) .add(updateUser) .addError(Unauthorized, { status: 401 }) // group-level error

const api = HttpApi.make("myApi") .add(usersGroup) .prefix("/api/v1") // optional prefix

Implementation

import { HttpApiBuilder } from "@effect/platform" import { Context, Effect, Layer, DateTime } from "effect"

// Service for handlers class UsersRepo extends Context.Tag("UsersRepo")<UsersRepo, { findById: (id: number) => Effect.Effect<typeof User.Type> }>() {}

// Implement group handlers const usersGroupLive = HttpApiBuilder.group(api, "users", (handlers) => Effect.gen(function* () { const repo = yield* UsersRepo return handlers .handle("getUser", ({ path: { id } }) => repo.findById(id)) .handle("listUsers", ({ urlParams: { page, sort } }) => Effect.succeed([{ id: 1, name: "John", createdAt: DateTime.unsafeNow() }]) ) .handle("createUser", ({ payload: { name } }) => Effect.succeed({ id: 2, name, createdAt: DateTime.unsafeNow() }) ) .handle("deleteUser", ({ path: { id } }) => Effect.void) .handle("updateUser", ({ path: { id }, payload: { name } }) => Effect.succeed({ id, name, createdAt: DateTime.unsafeNow() }) ) // Access raw request if needed (do not echo secrets) .handle("withHeaders", ({ request }) => Effect.succeed(method: ${request.method}) ) }) )

// Combine into API layer const MyApiLive = HttpApiBuilder.api(api).pipe(Layer.provide(usersGroupLive))

Serving

import { HttpApiSwagger, HttpMiddleware, HttpServer } from "@effect/platform" import { NodeHttpServer, NodeRuntime } from "@effect/platform-node" import { createServer } from "node:http"

const ServerLive = HttpApiBuilder.serve(HttpMiddleware.logger).pipe( Layer.provide(HttpApiSwagger.layer()), // /docs Layer.provide(HttpApiBuilder.middlewareCors()), // CORS Layer.provide(MyApiLive), HttpServer.withLogAddress, Layer.provide(NodeHttpServer.layer(createServer, { port: 3000 })) )

Layer.launch(ServerLive).pipe(NodeRuntime.runMain)

Client

import { HttpApiClient, FetchHttpClient } from "@effect/platform"

const program = Effect.gen(function* () { const client = yield* HttpApiClient.make(api, { baseUrl: "http://localhost:3000" }) const user = yield* client.users.getUser({ path: { id: 1 } }) })

program.pipe(Effect.provide(FetchHttpClient.layer), Effect.runPromise)

Custom Encoding

// URL-encoded request const urlEncoded = HttpApiEndpoint.post("urlEncoded", "/form") .setPayload( Schema.Struct({ a: Schema.String }) .pipe(HttpApiSchema.withEncoding({ kind: "UrlParams" })) )

// CSV response const csv = HttpApiEndpoint.get("csv", "/export") .addSuccess( Schema.String.pipe(HttpApiSchema.withEncoding({ kind: "Text", contentType: "text/csv" })) )

Predefined Errors

Error Status

HttpApiError.BadRequest

400

HttpApiError.Unauthorized

401

HttpApiError.Forbidden

403

HttpApiError.NotFound

404

HttpApiError.Conflict

409

HttpApiError.InternalServerError

500

Additional Resources

For deriving http clients For creating middlewares For deriving swagger UIs For multipart uploads For streaming

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

skill-creator

No summary provided by upstream source.

Repository SourceNeeds Review
General

effect-vitest

No summary provided by upstream source.

Repository SourceNeeds Review
General

effect

No summary provided by upstream source.

Repository SourceNeeds Review
General

discovery

No summary provided by upstream source.

Repository SourceNeeds Review