Skip to main content Link Search Menu Expand Document (external link)

HttpLayerRouter.ts overview

Since v1.0.0


Exports Grouped by Category


Configuration

RouterConfig (class)

Signature

declare class RouterConfig

Source

Since v1.0.0

HttpApi

addHttpApi

import * as NodeHttpServer from "@effect/platform-node/NodeHttpServer"
import * as NodeRuntime from "@effect/platform-node/NodeRuntime"
import * as HttpApi from "@effect/platform/HttpApi"
import * as HttpApiBuilder from "@effect/platform/HttpApiBuilder"
import * as HttpApiEndpoint from "@effect/platform/HttpApiEndpoint"
import * as HttpApiGroup from "@effect/platform/HttpApiGroup"
import * as HttpApiScalar from "@effect/platform/HttpApiScalar"
import * as HttpLayerRouter from "@effect/platform/HttpLayerRouter"
import * as HttpMiddleware from "@effect/platform/HttpMiddleware"
import * as Effect from "effect/Effect"
import * as Layer from "effect/Layer"
import { createServer } from "http"

// First, we define our HttpApi
class MyApi extends HttpApi.make("api").add(
  HttpApiGroup.make("users").add(HttpApiEndpoint.get("me", "/me")).prefix("/users")
) {}

// Implement the handlers for the API
const UsersApiLayer = HttpApiBuilder.group(MyApi, "users", (handers) => handers.handle("me", () => Effect.void))

// Use `HttpLayerRouter.addHttpApi` to register the API with the router
const HttpApiRoutes = HttpLayerRouter.addHttpApi(MyApi, {
  openapiPath: "/docs/openapi.json"
}).pipe(
  // Provide the api handlers layer
  Layer.provide(UsersApiLayer)
)

// Create a /docs route for the API documentation
const DocsRoute = HttpApiScalar.layerHttpLayerRouter({
  api: MyApi,
  path: "/docs"
})

const CorsMiddleware = HttpLayerRouter.middleware(HttpMiddleware.cors())
// You can also use HttpLayerRouter.cors() to create a CORS middleware

// Finally, we merge all routes and serve them using the Node HTTP server
const AllRoutes = Layer.mergeAll(HttpApiRoutes, DocsRoute).pipe(Layer.provide(CorsMiddleware.layer))

HttpLayerRouter.serve(AllRoutes).pipe(
  Layer.provide(NodeHttpServer.layer(createServer, { port: 3000 })),
  Layer.launch,
  NodeRuntime.runMain
)

Signature

declare const addHttpApi: <Id extends string, Groups extends HttpApiGroup.HttpApiGroup.Any, E, R>(
  api: HttpApi.HttpApi<Id, Groups, E, R>,
  options?: { readonly openapiPath?: `/${string}` | undefined }
) => Layer.Layer<
  never,
  never,
  | Etag.Generator
  | HttpRouter
  | FileSystem
  | HttpPlatform
  | Path
  | R
  | HttpApiGroup.HttpApiGroup.ToService<Id, Groups>
  | HttpApiGroup.HttpApiGroup.ErrorContext<Groups>
>

Source

Since v1.0.0

HttpRouter

HttpRouter

Signature

declare const HttpRouter: Context.Tag<HttpRouter, HttpRouter>

Source

Since v1.0.0

HttpRouter (interface)

Signature

export interface HttpRouter {
  readonly [TypeId]: TypeId

  readonly prefixed: (prefix: string) => HttpRouter

  readonly add: <E, R>(
    method: "*" | "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "OPTIONS",
    path: PathInput,
    handler:
      | Effect.Effect<HttpServerResponse.HttpServerResponse, E, R>
      | ((request: HttpServerRequest.HttpServerRequest) => Effect.Effect<HttpServerResponse.HttpServerResponse, E, R>),
    options?: { readonly uninterruptible?: boolean | undefined } | undefined
  ) => Effect.Effect<void, never, Request.From<"Requires", Exclude<R, Provided>> | Request.From<"Error", E>>

  readonly addAll: <const Routes extends ReadonlyArray<Route<any, any>>>(
    routes: Routes
  ) => Effect.Effect<
    void,
    never,
    | Request.From<"Requires", Exclude<Route.Context<Routes[number]>, Provided>>
    | Request.From<"Error", Route.Error<Routes[number]>>
  >

  readonly addGlobalMiddleware: <E, R>(
    middleware: ((
      effect: Effect.Effect<HttpServerResponse.HttpServerResponse, unhandled>
    ) => Effect.Effect<HttpServerResponse.HttpServerResponse, E, R>) &
      (unhandled extends E ? unknown : "You cannot handle any errors")
  ) => Effect.Effect<
    void,
    never,
    Request.From<"GlobalRequires", Exclude<R, GlobalProvided>> | Request.From<"GlobalError", Exclude<E, unhandled>>
  >

  readonly asHttpEffect: () => Effect.Effect<
    HttpServerResponse.HttpServerResponse,
    unknown,
    HttpServerRequest.HttpServerRequest | Scope.Scope
  >
}

Source

Since v1.0.0

TypeId

Signature

declare const TypeId: unique symbol

Source

Since v1.0.0

TypeId (type alias)

Signature

type TypeId = typeof TypeId

Source

Since v1.0.0

add

Create a layer that adds a single route to the HTTP router.

import * as HttpLayerRouter from "@effect/platform/HttpLayerRouter"
import * as HttpServerResponse from "@effect/platform/HttpServerResponse"

const Route = HttpLayerRouter.add("GET", "/hello", HttpServerResponse.text("Hello, World!"))

Signature

declare const add: <E, R>(
  method: "*" | "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "OPTIONS",
  path: PathInput,
  handler:
    | Effect.Effect<HttpServerResponse.HttpServerResponse, E, R>
    | ((request: HttpServerRequest.HttpServerRequest) => Effect.Effect<HttpServerResponse.HttpServerResponse, E, R>),
  options?: { readonly uninterruptible?: boolean | undefined }
) => Layer.Layer<never, never, HttpRouter | Request.From<"Requires", Exclude<R, Provided>> | Request.From<"Error", E>>

Source

Since v1.0.0

addAll

Create a layer that adds multiple routes to the HTTP router.

import * as HttpLayerRouter from "@effect/platform/HttpLayerRouter"
import * as HttpServerResponse from "@effect/platform/HttpServerResponse"

const Routes = HttpLayerRouter.addAll([
  HttpLayerRouter.route("GET", "/hello", HttpServerResponse.text("Hello, World!"))
])

Signature

declare const addAll: <Routes extends ReadonlyArray<Route<any, any>>, EX = never, RX = never>(
  routes: Routes | Effect.Effect<Routes, EX, RX>,
  options?: { readonly prefix?: string | undefined }
) => Layer.Layer<
  never,
  EX,
  | HttpRouter
  | Exclude<RX, Scope.Scope>
  | Request.From<"Requires", Exclude<Route.Context<Routes[number]>, Provided>>
  | Request.From<"Error", Route.Error<Routes[number]>>
>

Source

Since v1.0.0

layer

Signature

declare const layer: Layer.Layer<HttpRouter, never, never>

Source

Since v1.0.0

make

Signature

declare const make: Effect.Effect<HttpRouter, never, never>

Source

Since v1.0.0

toHttpEffect

Signature

declare const toHttpEffect: <A, E, R>(
  appLayer: Layer.Layer<A, E, R>
) => Effect.Effect<
  Effect.Effect<
    HttpServerResponse.HttpServerResponse,
    Request.Only<"Error", R> | Request.Only<"GlobalRequires", R> | HttpServerError.RouteNotFound,
    Scope.Scope | HttpServerRequest.HttpServerRequest | Request.Only<"Requires", R> | Request.Only<"GlobalRequires", R>
  >,
  Request.Without<E>,
  Exclude<Request.Without<R>, HttpRouter> | Scope.Scope
>

Source

Since v1.0.0

use

A helper function that is the equivalent of:

import * as HttpLayerRouter from "@effect/platform/HttpLayerRouter"
import * as Effect from "effect/Effect"
import * as Layer from "effect/Layer"

const MyRoute = Layer.scopedDiscard(
  Effect.gen(function* () {
    const router = yield* HttpLayerRouter.HttpRouter

    // then use `yield* router.add(...)` to add a route
  })
)

Signature

declare const use: <A, E, R>(
  f: (router: HttpRouter) => Effect.Effect<A, E, R>
) => Layer.Layer<never, E, HttpRouter | Exclude<R, Scope.Scope>>

Source

Since v1.0.0

Middleware

Middleware (interface)

Signature

export interface Middleware<
  Config extends {
    provides: any
    handles: any
    error: any
    requires: any
    layerError: any
    layerRequires: any
  }
> {
  readonly [MiddlewareTypeId]: Config

  readonly layer: [Config["requires"]] extends [never]
    ? Layer.Layer<
        Request.From<"Requires", Config["provides"]>,
        Config["layerError"],
        Config["layerRequires"] | Request.From<"Requires", Config["requires"]> | Request.From<"Error", Config["error"]>
      >
    : "Need to .combine(middleware) that satisfy the missing request dependencies"

  readonly combine: <
    Config2 extends {
      provides: any
      handles: any
      error: any
      requires: any
      layerError: any
      layerRequires: any
    }
  >(
    other: Middleware<Config2>
  ) => Middleware<{
    provides: Config2["provides"] | Config["provides"]
    handles: Config2["handles"] | Config["handles"]
    error: Config2["error"] | Exclude<Config["error"], Config2["handles"]>
    requires: Exclude<Config["requires"], Config2["provides"]> | Config2["requires"]
    layerError: Config["layerError"] | Config2["layerError"]
    layerRequires: Config["layerRequires"] | Config2["layerRequires"]
  }>
}

Source

Since v1.0.0

MiddlewareTypeId

Signature

declare const MiddlewareTypeId: unique symbol

Source

Since v1.0.0

MiddlewareTypeId (type alias)

Signature

type MiddlewareTypeId = typeof MiddlewareTypeId

Source

Since v1.0.0

cors

A middleware that applies CORS headers to the HTTP response.

Signature

declare const cors: (
  options?:
    | {
        readonly allowedOrigins?: ReadonlyArray<string> | undefined
        readonly allowedMethods?: ReadonlyArray<string> | undefined
        readonly allowedHeaders?: ReadonlyArray<string> | undefined
        readonly exposedHeaders?: ReadonlyArray<string> | undefined
        readonly maxAge?: number | undefined
        readonly credentials?: boolean | undefined
      }
    | undefined
) => Layer.Layer<never, never, HttpRouter>

Source

Since v1.0.0

disableLogger

A middleware that disables the logger for some routes.

import * as HttpLayerRouter from "@effect/platform/HttpLayerRouter"
import * as HttpServerResponse from "@effect/platform/HttpServerResponse"
import * as Layer from "effect/Layer"

const Route = HttpLayerRouter.add("GET", "/hello", HttpServerResponse.text("Hello, World!")).pipe(
  // disable the logger for this route
  Layer.provide(HttpLayerRouter.disableLogger)
)

Signature

declare const disableLogger: Layer.Layer<never, never, never>

Source

Since v1.0.0

middleware

Create a middleware layer that can be used to modify requests and responses.

By default, the middleware only affects the routes that it is provided to.

If you want to create a middleware that applies globally to all routes, pass the global option as true.

import * as HttpLayerRouter from "@effect/platform/HttpLayerRouter"
import * as HttpMiddleware from "@effect/platform/HttpMiddleware"
import * as HttpServerResponse from "@effect/platform/HttpServerResponse"
import * as Context from "effect/Context"
import * as Effect from "effect/Effect"
import * as Layer from "effect/Layer"

// Here we are defining a CORS middleware
const CorsMiddleware = HttpLayerRouter.middleware(HttpMiddleware.cors()).layer
// You can also use HttpLayerRouter.cors() to create a CORS middleware

class CurrentSession extends Context.Tag("CurrentSession")<
  CurrentSession,
  {
    readonly token: string
  }
>() {}

// You can create middleware that provides a service to the HTTP requests.
const SessionMiddleware = HttpLayerRouter.middleware<{
  provides: CurrentSession
}>()(
  Effect.gen(function* () {
    yield* Effect.log("SessionMiddleware initialized")

    return (httpEffect) =>
      Effect.provideService(httpEffect, CurrentSession, {
        token: "dummy-token"
      })
  })
).layer

Effect.gen(function* () {
  const router = yield* HttpLayerRouter.HttpRouter
  yield* router.add(
    "GET",
    "/hello",
    Effect.gen(function* () {
      // Requests can now access the current session
      const session = yield* CurrentSession
      return HttpServerResponse.text(`Hello, World! Your token is ${session.token}`)
    })
  )
}).pipe(
  Layer.effectDiscard,
  // Provide the SessionMiddleware & CorsMiddleware to some routes
  Layer.provide([SessionMiddleware, CorsMiddleware])
)

Signature

declare const middleware: middleware.Make<never, never> &
  (<Config extends { provides?: any; handles?: any } = {}>() => middleware.Make<
    Config extends { provides: infer R } ? R : never,
    Config extends { handles: infer E } ? E : never
  >)

Source

Since v1.0.0

middleware (namespace)

Source

Since v1.0.0

Make (type alias)

Signature

type Make<Provides, Handles> = {
  <E, R, EX, RX, const Global extends boolean = false>(
    middleware: Effect.Effect<
      (
        effect: Effect.Effect<
          HttpServerResponse.HttpServerResponse,
          Types.NoInfer<Handles | unhandled>,
          Types.NoInfer<Provides>
        >
      ) => Effect.Effect<HttpServerResponse.HttpServerResponse, E, R> &
        (unhandled extends E ? unknown : "You must only handle the configured errors"),
      EX,
      RX
    >,
    options?: {
      readonly global?: Global | undefined
    }
  ): Global extends true
    ? Layer.Layer<
        | Request.From<"Requires", Provides>
        | Request.From<"Error", Handles>
        | Request.From<"GlobalRequires", Provides>
        | Request.From<"GlobalError", Handles>,
        EX,
        | HttpRouter
        | Exclude<RX, Scope.Scope>
        | Request.From<"GlobalRequires", Exclude<R, GlobalProvided>>
        | Request.From<"GlobalError", Exclude<E, unhandled>>
      >
    : Middleware<{
        provides: Provides
        handles: Handles
        error: Exclude<E, unhandled>
        requires: Exclude<R, Provided>
        layerError: EX
        layerRequires: Exclude<RX, Scope.Scope>
      }>
  <E, R, const Global extends boolean = false>(
    middleware: ((
      effect: Effect.Effect<
        HttpServerResponse.HttpServerResponse,
        Types.NoInfer<Handles | unhandled>,
        Types.NoInfer<Provides>
      >
    ) => Effect.Effect<HttpServerResponse.HttpServerResponse, E, R>) &
      (unhandled extends E ? unknown : "You must only handle the configured errors"),
    options?: {
      readonly global?: Global | undefined
    }
  ): Global extends true
    ? Layer.Layer<
        | Request.From<"Requires", Provides>
        | Request.From<"Error", Handles>
        | Request.From<"GlobalRequires", Provides>
        | Request.From<"GlobalError", Handles>,
        never,
        | HttpRouter
        | Request.From<"GlobalRequires", Exclude<R, GlobalProvided>>
        | Request.From<"GlobalError", Exclude<E, unhandled>>
      >
    : Middleware<{
        provides: Provides
        handles: Handles
        error: Exclude<E, unhandled>
        requires: Exclude<R, Provided>
        layerError: never
        layerRequires: never
      }>
}

Source

Since v1.0.0

Fn (type alias)

Signature

type Fn = (
  effect: Effect.Effect<HttpServerResponse.HttpServerResponse>
) => Effect.Effect<HttpServerResponse.HttpServerResponse>

Source

Since v1.0.0

unhandled (interface)

A pseudo-error type that represents an error that should be not handled by the middleware.

Signature

export interface unhandled {
  readonly _: unique symbol
}

Source

Since v1.0.0

PathInput

PathInput (type alias)

Signature

type PathInput = `/${string}` | "*"

Source

Since v1.0.0

prefixPath

Signature

declare const prefixPath: { (prefix: string): (self: string) => string; (self: string, prefix: string): string }

Source

Since v1.0.0

Re-exports

FindMyWay (namespace export)

Re-exports all named exports from the “find-my-way-ts” module as FindMyWay.

Signature

export * as FindMyWay from "find-my-way-ts"

Source

Since v1.0.0

Request types

GlobalProvided (type alias)

Services provided to global middleware.

Signature

type GlobalProvided = HttpServerRequest.HttpServerRequest | Scope.Scope

Source

Since v1.0.0

Provided (type alias)

Services provided by the HTTP router, which are available in the request context.

Signature

type Provided = HttpServerRequest.HttpServerRequest | Scope.Scope | HttpServerRequest.ParsedSearchParams | RouteContext

Source

Since v1.0.0

Request (interface)

Represents a request-level dependency, that needs to be provided by middleware.

Signature

export interface Request<Kind extends string, T> {
  readonly _: unique symbol
  readonly kind: Kind
  readonly type: T
}

Source

Since v1.0.0

Request (namespace)

Source

Since v1.0.0

From (type alias)

Signature

type From<Kind, R> = R extends infer T ? Request<Kind, T> : never

Source

Since v1.0.0

Only (type alias)

Signature

type Only<Kind, A> = A extends Request<Kind, infer T> ? T : never

Source

Since v1.0.0

Without (type alias)

Signature

type Without<A> = A extends Request<infer _Kind, infer _> ? never : A

Source

Since v1.0.0

Route

Route (interface)

Signature

export interface Route<E = never, R = never> {
  readonly [RouteTypeId]: RouteTypeId
  readonly method: HttpMethod.HttpMethod | "*"
  readonly path: PathInput
  readonly handler: Effect.Effect<HttpServerResponse.HttpServerResponse, E, R>
  readonly uninterruptible: boolean
  readonly prefix: Option.Option<string>
}

Source

Since v1.0.0

Route (namespace)

Source

Since v1.0.0

Error (type alias)

Signature

type Error<R> = R extends Route<infer E, infer _R> ? E : never

Source

Since v1.0.0

Context (type alias)

Signature

type Context<T> = T extends Route<infer _E, infer R> ? R : never

Source

Since v1.0.0

RouteTypeId

Signature

declare const RouteTypeId: unique symbol

Source

Since v1.0.0

RouteTypeId (type alias)

Signature

type RouteTypeId = typeof RouteTypeId

Source

Since v1.0.0

prefixRoute

Signature

declare const prefixRoute: {
  (prefix: string): <E, R>(self: Route<E, R>) => Route<E, R>
  <E, R>(self: Route<E, R>, prefix: string): Route<E, R>
}

Source

Since v1.0.0

route

Signature

declare const route: <E, R>(
  method: "*" | "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "OPTIONS",
  path: PathInput,
  handler:
    | Effect.Effect<HttpServerResponse.HttpServerResponse, E, R>
    | ((request: HttpServerRequest.HttpServerRequest) => Effect.Effect<HttpServerResponse.HttpServerResponse, E, R>),
  options?: { readonly uninterruptible?: boolean | undefined }
) => Route<E, Exclude<R, Provided>>

Source

Since v1.0.0

Route context

RouteContext

Signature

declare const RouteContext: Context.Tag<RouteContext, RouteContext>

Source

Since v1.0.0

params

Signature

declare const params: Effect.Effect<Readonly<Record<string, string | undefined>>, never, RouteContext>

Source

Since v1.0.0

schemaJson

Signature

declare const schemaJson: <
  R,
  I extends Partial<{
    readonly method: HttpMethod.HttpMethod
    readonly url: string
    readonly cookies: Readonly<Record<string, string | undefined>>
    readonly headers: Readonly<Record<string, string | undefined>>
    readonly pathParams: Readonly<Record<string, string | undefined>>
    readonly searchParams: Readonly<Record<string, string | ReadonlyArray<string> | undefined>>
    readonly body: any
  }>,
  A
>(
  schema: Schema<A, I, R>,
  options?: ParseOptions | undefined
) => Effect.Effect<
  A,
  HttpServerError.RequestError | ParseError,
  RouteContext | R | HttpServerRequest.HttpServerRequest | HttpServerRequest.ParsedSearchParams
>

Source

Since v1.0.0

schemaNoBody

Signature

declare const schemaNoBody: <
  R,
  I extends Partial<{
    readonly method: HttpMethod.HttpMethod
    readonly url: string
    readonly cookies: Readonly<Record<string, string | undefined>>
    readonly headers: Readonly<Record<string, string | undefined>>
    readonly pathParams: Readonly<Record<string, string | undefined>>
    readonly searchParams: Readonly<Record<string, string | ReadonlyArray<string> | undefined>>
  }>,
  A
>(
  schema: Schema<A, I, R>,
  options?: ParseOptions | undefined
) => Effect.Effect<
  A,
  ParseError,
  R | RouteContext | HttpServerRequest.HttpServerRequest | HttpServerRequest.ParsedSearchParams
>

Source

Since v1.0.0

schemaParams

Signature

declare const schemaParams: <A, I extends Readonly<Record<string, string | ReadonlyArray<string> | undefined>>, R>(
  schema: Schema<A, I, R>,
  options?: ParseOptions | undefined
) => Effect.Effect<A, ParseError, R | RouteContext | HttpServerRequest.ParsedSearchParams>

Source

Since v1.0.0

schemaPathParams

Signature

declare const schemaPathParams: <A, I extends Readonly<Record<string, string | undefined>>, R>(
  schema: Schema<A, I, R>,
  options?: ParseOptions | undefined
) => Effect.Effect<A, ParseError, R | RouteContext>

Source

Since v1.0.0

Server

serve

Serves the provided application layer as an HTTP server.

Signature

declare const serve: <A, E, R, HE, HR = Request.Only<"Requires", R> | Request.Only<"GlobalRequires", R>>(
  appLayer: Layer.Layer<A, E, R>,
  options?: {
    readonly routerConfig?: Partial<FindMyWay.RouterConfig> | undefined
    readonly disableLogger?: boolean | undefined
    readonly disableListenLog?: boolean
    readonly middleware?: (
      effect: Effect.Effect<
        HttpServerResponse.HttpServerResponse,
        Request.Only<"Error", R> | Request.Only<"GlobalError", R> | HttpServerError.RouteNotFound,
        | Scope.Scope
        | HttpServerRequest.HttpServerRequest
        | Request.Only<"Requires", R>
        | Request.Only<"GlobalRequires", R>
      >
    ) => Effect.Effect<HttpServerResponse.HttpServerResponse, HE, HR>
  }
) => Layer.Layer<
  never,
  Request.Without<E>,
  HttpServer.HttpServer | Exclude<Request.Without<R> | Exclude<HR, GlobalProvided>, HttpRouter>
>

Source

Since v1.0.0

toWebHandler

Signature

declare const toWebHandler: <
  A,
  E,
  R extends
    | HttpRouter
    | Request<"Requires", any>
    | Request<"GlobalRequires", any>
    | Request<"Error", any>
    | Request<"GlobalError", any>,
  HE,
  HR = Request.Only<"Requires", R> | Request.Only<"GlobalRequires", R>
>(
  appLayer: Layer.Layer<A, E, R>,
  options?: {
    readonly memoMap?: Layer.MemoMap | undefined
    readonly routerConfig?: Partial<FindMyWay.RouterConfig> | undefined
    readonly disableLogger?: boolean | undefined
    readonly middleware?: (
      effect: Effect.Effect<
        HttpServerResponse.HttpServerResponse,
        Request.Only<"Error", R> | Request.Only<"GlobalError", R> | HttpServerError.RouteNotFound,
        | Scope.Scope
        | HttpServerRequest.HttpServerRequest
        | Request.Only<"Requires", R>
        | Request.Only<"GlobalRequires", R>
      >
    ) => Effect.Effect<HttpServerResponse.HttpServerResponse, HE, HR>
  }
) => {
  readonly handler: [HR] extends [never]
    ? (request: globalThis.Request, context?: Context.Context<never> | undefined) => Promise<Response>
    : (request: globalThis.Request, context: Context.Context<HR>) => Promise<Response>
  readonly dispose: () => Promise<void>
}

Source

Since v1.0.0