Skip to content

Commit beaccae

Browse files
authored
fix HttpApiBuilder.middleware when used multiple times (#4005)
1 parent af409cf commit beaccae

File tree

2 files changed

+52
-28
lines changed

2 files changed

+52
-28
lines changed

.changeset/neat-tables-destroy.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@effect/platform": patch
3+
---
4+
5+
fix HttpApiBuilder.middleware when used multiple times

packages/platform/src/HttpApiBuilder.ts

Lines changed: 47 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ export const serve = <R = never>(
8888
httpApp.pipe(
8989
Effect.map((app) => HttpServer.serve(app as any, middleware!)),
9090
Layer.unwrapEffect,
91-
Layer.provide(Router.Live)
91+
Layer.provide([Router.Live, Middleware.layer])
9292
)
9393

9494
/**
@@ -100,16 +100,17 @@ export const serve = <R = never>(
100100
export const httpApp: Effect.Effect<
101101
HttpApp.Default<never, HttpRouter.HttpRouter.DefaultServices>,
102102
never,
103-
Router | HttpApi.Api
103+
Router | HttpApi.Api | Middleware
104104
> = Effect.gen(function*() {
105105
const { api, context } = yield* HttpApi.Api
106106
const middleware = makeMiddlewareMap(api.middlewares, context)
107107
const router = applyMiddleware(middleware, yield* Router.router)
108-
const apiMiddleware = yield* Effect.serviceOption(Middleware)
108+
const apiMiddlewareService = yield* Middleware
109+
const apiMiddleware = yield* apiMiddlewareService.retrieve
109110
const errorSchema = makeErrorSchema(api as any)
110111
const encodeError = Schema.encodeUnknown(errorSchema)
111112
return router.pipe(
112-
apiMiddleware._tag === "Some" ? apiMiddleware.value : identity,
113+
apiMiddleware,
113114
Effect.catchAll((error) =>
114115
Effect.matchEffect(Effect.provide(encodeError(error), context), {
115116
onFailure: () => Effect.die(error),
@@ -157,7 +158,7 @@ export const toWebHandler = <LA, LE>(
157158
readonly dispose: () => Promise<void>
158159
} => {
159160
const runtime = ManagedRuntime.make(
160-
Layer.merge(layer, Router.Live),
161+
Layer.mergeAll(layer, Router.Live, Middleware.layer),
161162
options?.memoMap
162163
)
163164
let handlerCached: ((request: Request) => Promise<Response>) | undefined
@@ -745,8 +746,26 @@ const toResponseError = toResponseSchema(HttpApiSchema.getStatusErrorAST)
745746
*/
746747
export class Middleware extends Context.Tag("@effect/platform/HttpApiBuilder/Middleware")<
747748
Middleware,
748-
HttpMiddleware.HttpMiddleware
749-
>() {}
749+
{
750+
readonly add: (middleware: HttpMiddleware.HttpMiddleware) => Effect.Effect<void>
751+
readonly retrieve: Effect.Effect<HttpMiddleware.HttpMiddleware>
752+
}
753+
>() {
754+
/**
755+
* @since 1.0.0
756+
*/
757+
static readonly layer = Layer.sync(Middleware, () => {
758+
let middleware: HttpMiddleware.HttpMiddleware = identity
759+
return Middleware.of({
760+
add: (f) =>
761+
Effect.sync(() => {
762+
const prev = middleware
763+
middleware = (app) => f(prev(app))
764+
}),
765+
retrieve: Effect.sync(() => middleware)
766+
})
767+
})
768+
}
750769

751770
/**
752771
* @since 1.0.0
@@ -756,26 +775,24 @@ export type MiddlewareFn<Error, R = HttpRouter.HttpRouter.Provided> = (
756775
httpApp: HttpApp.Default
757776
) => HttpApp.Default<Error, R>
758777

759-
const middlewareAdd = (middleware: HttpMiddleware.HttpMiddleware): Effect.Effect<HttpMiddleware.HttpMiddleware> =>
760-
Effect.map(
761-
Effect.context<never>(),
762-
(context) => {
763-
const current = Context.getOption(context, Middleware)
764-
const withContext: HttpMiddleware.HttpMiddleware = (httpApp) =>
765-
Effect.mapInputContext(middleware(httpApp), (input) => Context.merge(context, input))
766-
return current._tag === "None" ? withContext : (httpApp) => withContext(current.value(httpApp))
767-
}
768-
)
778+
const middlewareAdd = (
779+
middleware: HttpMiddleware.HttpMiddleware
780+
): Effect.Effect<void, never, Middleware> =>
781+
Effect.gen(function*() {
782+
const context = yield* Effect.context<never>()
783+
const service = yield* Middleware
784+
yield* service.add((httpApp) =>
785+
Effect.mapInputContext(middleware(httpApp), (input) => Context.merge(context, input))
786+
)
787+
})
769788

770789
const middlewareAddNoContext = (
771790
middleware: HttpMiddleware.HttpMiddleware
772-
): Effect.Effect<HttpMiddleware.HttpMiddleware> =>
773-
Effect.map(
774-
Effect.serviceOption(Middleware),
775-
(current): HttpMiddleware.HttpMiddleware => {
776-
return current._tag === "None" ? middleware : (httpApp) => middleware(current.value(httpApp))
777-
}
778-
)
791+
): Effect.Effect<void, never, Middleware> =>
792+
Effect.gen(function*() {
793+
const service = yield* Middleware
794+
yield* service.add(middleware)
795+
})
779796

780797
/**
781798
* Create an `HttpApi` level middleware `Layer`.
@@ -828,9 +845,9 @@ export const middleware: {
828845
const withContext = apiFirst ? args[2]?.withContext === true : (args as any)[1]?.withContext === true
829846
const add = withContext ? middlewareAdd : middlewareAddNoContext
830847
const middleware = apiFirst ? args[1] : args[0]
831-
return Effect.isEffect(middleware)
832-
? Layer.effect(Middleware, Effect.flatMap(middleware as any, add))
833-
: Layer.effect(Middleware, add(middleware as any))
848+
return (Effect.isEffect(middleware)
849+
? Layer.effectDiscard(Effect.flatMap(middleware as any, add))
850+
: Layer.effectDiscard(add(middleware as any))).pipe(Layer.provide(Middleware.layer))
834851
}
835852

836853
/**
@@ -885,7 +902,9 @@ export const middlewareScoped: {
885902
const withContext = apiFirst ? args[2]?.withContext === true : (args as any)[1]?.withContext === true
886903
const add = withContext ? middlewareAdd : middlewareAddNoContext
887904
const middleware = apiFirst ? args[1] : args[0]
888-
return Layer.scoped(Middleware, Effect.flatMap(middleware as any, add))
905+
return Layer.scopedDiscard(Effect.flatMap(middleware as any, add)).pipe(
906+
Layer.provide(Middleware.layer)
907+
)
889908
}
890909

891910
/**

0 commit comments

Comments
 (0)