Issues with Layers in HTTP API with Auth Middleware
Hello! I'm trying to make a http api with an auth middleware that provides the current user to be used in handlers. But I'm running into problems with the layers it seems.
Here's a playground: https://effect.website/play#e71fa7270ec3
Anyone mind explaining what I'm doing wrong here? And if there's a more preferred way?
Here's a playground: https://effect.website/play#e71fa7270ec3
Anyone mind explaining what I'm doing wrong here? And if there's a more preferred way?
import { ... } from "@effect/platform" // omitted to not make message too long for discord
import { NodeHttpServer, NodeRuntime } from "@effect/platform-node"
import { Context, Effect, Layer, Schema } from "effect"
import { createServer } from "node:http"
class User extends Schema.Class<User>("User")({ name: Schema.String }) {}
class Me extends Context.Tag("Me")<Me, User>() {}
class AuthenticationMiddleware extends HttpApiMiddleware.Tag<AuthenticationMiddleware>()(
"AuthenticationMiddleware",
{ provides: Me }
) {}
const AuthenticationMiddlewareLive = Layer.effect(
AuthenticationMiddleware,
Effect.gen(function*() {
// extract session from httponly cookie (omitted for simplicity)
return Effect.succeed(new User({ name: "alice" }))
})
)
const Api = HttpApi.make("api").add(
HttpApiGroup.make("greetings")
.add(HttpApiEndpoint.get("hello", "/hello").addSuccess(Schema.String))
).middleware(AuthenticationMiddleware)
const GreetingsLive = HttpApiBuilder.group(Api, "greetings", (handlers) =>
Effect.gen(function*() {
const me = yield* Me
return handlers.handle("hello", () => Effect.succeed(`hello ${me.name}`))
}))
// type of ApiLive: Layer.Layer<HttpApi.Api, never, Me> but I would expect it to be Layer.Layer<HttpApi.Api, never, never> since AuthenticationMiddlewareLive provides Me?
const ApiLive = HttpApiBuilder.api(Api).pipe(
Layer.provide(GreetingsLive),
Layer.provide(AuthenticationMiddlewareLive)
)
const Main = HttpApiBuilder.serve(HttpMiddleware.logger).pipe(
Layer.provide(ApiLive),
Layer.provide(NodeHttpServer.layer(createServer, { port: 4000 }))
)
NodeRuntime.runMain(Layer.launch(Main))import { ... } from "@effect/platform" // omitted to not make message too long for discord
import { NodeHttpServer, NodeRuntime } from "@effect/platform-node"
import { Context, Effect, Layer, Schema } from "effect"
import { createServer } from "node:http"
class User extends Schema.Class<User>("User")({ name: Schema.String }) {}
class Me extends Context.Tag("Me")<Me, User>() {}
class AuthenticationMiddleware extends HttpApiMiddleware.Tag<AuthenticationMiddleware>()(
"AuthenticationMiddleware",
{ provides: Me }
) {}
const AuthenticationMiddlewareLive = Layer.effect(
AuthenticationMiddleware,
Effect.gen(function*() {
// extract session from httponly cookie (omitted for simplicity)
return Effect.succeed(new User({ name: "alice" }))
})
)
const Api = HttpApi.make("api").add(
HttpApiGroup.make("greetings")
.add(HttpApiEndpoint.get("hello", "/hello").addSuccess(Schema.String))
).middleware(AuthenticationMiddleware)
const GreetingsLive = HttpApiBuilder.group(Api, "greetings", (handlers) =>
Effect.gen(function*() {
const me = yield* Me
return handlers.handle("hello", () => Effect.succeed(`hello ${me.name}`))
}))
// type of ApiLive: Layer.Layer<HttpApi.Api, never, Me> but I would expect it to be Layer.Layer<HttpApi.Api, never, never> since AuthenticationMiddlewareLive provides Me?
const ApiLive = HttpApiBuilder.api(Api).pipe(
Layer.provide(GreetingsLive),
Layer.provide(AuthenticationMiddlewareLive)
)
const Main = HttpApiBuilder.serve(HttpMiddleware.logger).pipe(
Layer.provide(ApiLive),
Layer.provide(NodeHttpServer.layer(createServer, { port: 4000 }))
)
NodeRuntime.runMain(Layer.launch(Main))