effect-http icon indicating copy to clipboard operation
effect-http copied to clipboard

Embeddable examples similar to Hono?

Open danielo515 opened this issue 1 year ago • 1 comments

I have an already existing Astro server, but I'm looking for a way to have type safety cross boundaries (RPC style). This library seems like a good fit to build such foundation, but as I mentioned, I need to embed it inside my existing ASTRO backend. Hono has a similar feature that allows to embed it within other servers. As far as I know, all I need is a function capable of dealing with a request (perform some routing too) and returns a response. Is this possible? Do you have any examples ?

danielo515 avatar May 08 '24 21:05 danielo515

Hey, I don't have any experience with astro. But the integration of an effect-http app should be the same as integrating @effect/platform app / router (the RouterBuilder.build create an instance of /platform app) so you can try to ask on the Effect discord, maybe some people already dealt with it. 🙂

sukovanej avatar May 09 '24 13:05 sukovanej

I believe the solution here are the HttpApp.toWebHandler / HttpApp.toWebHandlerLayer functions. Please see this discord thread where the solution is discussed.

In the following example, the handler should be the Request -> Response function you're looking for.

import { HttpApp } from "@effect/platform"
import { NodeContext } from "@effect/platform-node"
import { Schema } from "@effect/schema"
import { Effect, pipe } from "effect"
import { Api, RouterBuilder } from "effect-http"
import { NodeSwaggerFiles } from "effect-http-node"

const Response = Schema.String

const api = pipe(
  Api.make({ title: "Example API" }),
  Api.addEndpoint(pipe(Api.get("helloWord", "/"), Api.setResponseBody(Response)))
)

const app = pipe(
  RouterBuilder.make(api),
  RouterBuilder.handle("helloWord", () => Effect.succeed("Hello, world!")),
  RouterBuilder.build
)

export const { close, handler } = HttpApp.toWebHandlerLayer(
  app,
  Layer.provide(NodeSwaggerFiles.SwaggerFilesLive, NodeContext.layer)
)

sukovanej avatar Jul 14 '24 13:07 sukovanej

Hey, thank you very much for the time put into answering me. I will have a look as soon as I can. Thanks again

danielo515 avatar Jul 16 '24 06:07 danielo515

I just wanted to thank you again for your help. Your snippet was exactly what I was looking for. In case anyone is interested, all I had to do for this to work with astro was:

// [...api].ts
export const GET: APIRoute = (_) => {
  return handler(_.request);
};

And that's it. All the requests to any API will be routed to the handler. The same needs to be done for POST in case you want that. By the way, what do I need to do with the close function?

danielo515 avatar Aug 10 '24 14:08 danielo515

By the way, what do I need to do with the close function?

It will release resources allocated within a scope. It should be passed to an app teardown function, in case of node it means calling it upon a SIGINT. If you don't allocate anything in the application scope that needs to be closed when the main process exits, you don't really need to call it at all.

sukovanej avatar Aug 10 '24 15:08 sukovanej

Ok, I think the best thing to do is try to add a hook to SIGINT, just to be sure. Thank you again

danielo515 avatar Aug 10 '24 15:08 danielo515

Sorry for adding questions to this closed issue, but they are all related. Is the /docs endpoint supposed to be serving the swagger docs? Or that only happens when you use the Node server?

danielo515 avatar Aug 11 '24 06:08 danielo515

RouterBuilder is responsible for that. It automatically adds the router serving the docs. NodeServer is just a super light wrapper around the /platform-node server and its responsibility is to start the http app and log out where it's listening.

sukovanej avatar Aug 11 '24 10:08 sukovanej

https://sukovanej.github.io/effect-http/effect-http/RouterBuilder.ts.html#options-interface you can provide options to the make constructor to modify the path where to serve the docs or potentially turn it off completely.

sukovanej avatar Aug 11 '24 10:08 sukovanej

That's weird, because I tried reaching http://localhost:4321/api/docs and I don't get any docs, just not found.

danielo515 avatar Aug 11 '24 11:08 danielo515

Ok, I had to define the route manually in order to make it work:

const app = RouterBuilder.make(api, {
  enableDocs: true,
  docsPath: "/api/docs",
}).pipe(

It seems the expectation is this to be at the root of the server, so if you have it under a nested path (like in my case, embedded in an Astro application) you have to specify it.

danielo515 avatar Aug 11 '24 11:08 danielo515