From 2eda6f23ebd7abefd666bd3d2c8fdd94cbc4bd0c Mon Sep 17 00:00:00 2001 From: Patrick Roza Date: Wed, 13 Aug 2025 23:21:39 +0200 Subject: [PATCH 1/5] prepare --- api/src/lib/routing.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/lib/routing.ts b/api/src/lib/routing.ts index 29e18dc5..b574bba3 100644 --- a/api/src/lib/routing.ts +++ b/api/src/lib/routing.ts @@ -81,7 +81,7 @@ const RequireRolesLive = Layer.effect( }) ) -const middleware = Object.assign(RpcMiddleware, { +export const middleware = Object.assign(RpcMiddleware, { Default: RpcMiddleware.layer.pipe(Layer.provide([ AllowAnonymousLive, RequireRolesLive, From f9e5bbd0f39c5a75d8b6138484579730d5f93e21 Mon Sep 17 00:00:00 2001 From: Patrick Roza Date: Wed, 13 Aug 2025 23:22:04 +0200 Subject: [PATCH 2/5] add alt resource and controller --- api/src/HelloWorld.alt.controllers.ts | 48 +++++++++++++++++++++++++++ api/src/controllers.ts | 3 +- api/src/resources/HelloWorld.alt.ts | 31 +++++++++++++++++ 3 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 api/src/HelloWorld.alt.controllers.ts create mode 100644 api/src/resources/HelloWorld.alt.ts diff --git a/api/src/HelloWorld.alt.controllers.ts b/api/src/HelloWorld.alt.controllers.ts new file mode 100644 index 00000000..df943557 --- /dev/null +++ b/api/src/HelloWorld.alt.controllers.ts @@ -0,0 +1,48 @@ +import { middleware } from "#lib/routing" +import { User } from "#models/User" +import { GetHelloWorld, HelloWorldRpc, meta } from "#resources/HelloWorld.alt" +import { UserRepo } from "#services" +import { getRequestContext } from "@effect-app/infra/api/setupRequest" +import { generate } from "@effect-app/infra/test" +import { RpcServer } from "@effect/rpc" +import { Effect, Layer, S } from "effect-app" + +// TODO: make this simpler in one go, similar to effect-app Router? +const server = RpcServer + .layerHttpRouter({ + spanPrefix: "RpcServer." + meta.moduleName, + group: HelloWorldRpc, + path: ("/rpc/" + meta.moduleName) as `/${typeof meta.moduleName}`, + protocol: "http" + }) +export default Layer + .provide( + server, + HelloWorldRpc + .toLayerDynamic(Effect.gen(function*() { + const userRepo = yield* UserRepo + return { + // TODO: generator support? *Get({ echo }) { - but then need to handle the span stacktrace, like in effect-app Router + Get: Effect.fn(function*({ echo }) { + const context = yield* getRequestContext + const user = yield* userRepo + .tryGetCurrentUser + .pipe( + Effect.catchTags({ + "NotLoggedInError": () => Effect.succeed(null), + "NotFoundError": () => Effect.succeed(null) + }) + ) + + return new GetHelloWorld.success({ + context, + echo, + currentUser: user, + randomUser: generate(S.A.make(User)).value + }) + }) + } + })) + ) + // would be kind of nice if we could just pass { effect, dependencies } to toLayerDynamic call, just like Effect.Service? + .pipe(Layer.provide([UserRepo.Default, middleware.Default])) diff --git a/api/src/controllers.ts b/api/src/controllers.ts index 21be5610..bf38e565 100644 --- a/api/src/controllers.ts +++ b/api/src/controllers.ts @@ -1,7 +1,8 @@ // codegen:start {preset: barrel, include: ./*.controllers.ts, import: default} import accountsControllers from "./Accounts.controllers.js" +import helloWorldAltControllers from "./HelloWorld.alt.controllers.js" import helloWorldControllers from "./HelloWorld.controllers.js" import operationsControllers from "./Operations.controllers.js" -export { accountsControllers, helloWorldControllers, operationsControllers } +export { accountsControllers, helloWorldAltControllers, helloWorldControllers, operationsControllers } // codegen:end diff --git a/api/src/resources/HelloWorld.alt.ts b/api/src/resources/HelloWorld.alt.ts new file mode 100644 index 00000000..37d301d7 --- /dev/null +++ b/api/src/resources/HelloWorld.alt.ts @@ -0,0 +1,31 @@ +import { RequestContext } from "@effect-app/infra/RequestContext" +import { RpcGroup } from "@effect/rpc" +import { middlewareGroup } from "effect-app/rpc" +import { RpcMiddleware, S } from "./lib.js" +import { UserView } from "./views.js" + +class Response extends S.Class("Response")({ + now: S.Date.withDefault, + echo: S.String, + context: RequestContext, + currentUser: S.NullOr(UserView), + randomUser: UserView +}) {} + +export class GetHelloWorld extends S.Req()("GetHelloWorld", { + echo: S.String +}, { allowAnonymous: true, allowRoles: ["user"], success: Response }) {} + +export const HelloWorldRpc = middlewareGroup(RpcMiddleware)(RpcGroup + .make( + RpcMiddleware.rpc("Get", { + payload: GetHelloWorld.fields, + // TODO: add fromTaggedRequeset with config support instead + success: GetHelloWorld.success, + config: GetHelloWorld.config + }) + )) + +// codegen:start {preset: meta, sourcePrefix: src/resources/} +export const meta = { moduleName: "HelloWorld.alt" } as const +// codegen:end From e99fc614603ca4a874b6a5512235d6e3a1e9e851 Mon Sep 17 00:00:00 2001 From: Patrick Roza Date: Wed, 13 Aug 2025 23:22:36 +0200 Subject: [PATCH 3/5] play with atom and rpc client and native rpc controller. --- frontend/pages/index.vue | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/frontend/pages/index.vue b/frontend/pages/index.vue index f3721575..1a0e9dad 100644 --- a/frontend/pages/index.vue +++ b/frontend/pages/index.vue @@ -1,6 +1,8 @@