HttpLayerRouter.ts overview
Since v1.0.0
Exports Grouped by Category
- Configuration
- HttpApi
- HttpRouter
- Middleware
- PathInput
- Re-exports
- Request types
- Route
- Route context
- Server
Configuration
RouterConfig (class)
Signature
declare class RouterConfig
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>
>
Since v1.0.0
HttpRouter
HttpRouter
Signature
declare const HttpRouter: Context.Tag<HttpRouter, HttpRouter>
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
>
}
Since v1.0.0
TypeId
Signature
declare const TypeId: unique symbol
Since v1.0.0
TypeId (type alias)
Signature
type TypeId = typeof TypeId
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>>
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]>>
>
Since v1.0.0
layer
Signature
declare const layer: Layer.Layer<HttpRouter, never, never>
Since v1.0.0
make
Signature
declare const make: Effect.Effect<HttpRouter, never, never>
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
>
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>>
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"]
}>
}
Since v1.0.0
MiddlewareTypeId
Signature
declare const MiddlewareTypeId: unique symbol
Since v1.0.0
MiddlewareTypeId (type alias)
Signature
type MiddlewareTypeId = typeof MiddlewareTypeId
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>
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>
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
>)
Since v1.0.0
middleware (namespace)
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
}>
}
Since v1.0.0
Fn (type alias)
Signature
type Fn = (
effect: Effect.Effect<HttpServerResponse.HttpServerResponse>
) => Effect.Effect<HttpServerResponse.HttpServerResponse>
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
}
Since v1.0.0
PathInput
PathInput (type alias)
Signature
type PathInput = `/${string}` | "*"
Since v1.0.0
prefixPath
Signature
declare const prefixPath: { (prefix: string): (self: string) => string; (self: string, prefix: string): string }
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"
Since v1.0.0
Request types
GlobalProvided (type alias)
Services provided to global middleware.
Signature
type GlobalProvided = HttpServerRequest.HttpServerRequest | Scope.Scope
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
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
}
Since v1.0.0
Request (namespace)
Since v1.0.0
From (type alias)
Signature
type From<Kind, R> = R extends infer T ? Request<Kind, T> : never
Since v1.0.0
Only (type alias)
Signature
type Only<Kind, A> = A extends Request<Kind, infer T> ? T : never
Since v1.0.0
Without (type alias)
Signature
type Without<A> = A extends Request<infer _Kind, infer _> ? never : A
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>
}
Since v1.0.0
Route (namespace)
Since v1.0.0
Error (type alias)
Signature
type Error<R> = R extends Route<infer E, infer _R> ? E : never
Since v1.0.0
Context (type alias)
Signature
type Context<T> = T extends Route<infer _E, infer R> ? R : never
Since v1.0.0
RouteTypeId
Signature
declare const RouteTypeId: unique symbol
Since v1.0.0
RouteTypeId (type alias)
Signature
type RouteTypeId = typeof RouteTypeId
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>
}
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>>
Since v1.0.0
Route context
RouteContext
Signature
declare const RouteContext: Context.Tag<RouteContext, RouteContext>
Since v1.0.0
params
Signature
declare const params: Effect.Effect<Readonly<Record<string, string | undefined>>, never, RouteContext>
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
>
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
>
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>
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>
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>
>
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>
}
Since v1.0.0