Open aheissenberger opened 2 months ago
That would be awesome @aheissenberger!
Currently the only adapter in existence is for Vercel. Creating the @lazarv/react-server-adapter-sst
would have been my next target after moving @lazarv/react-server-router
around.
To implement another adapter, there are some dependencies regarding moving any code from the Vercel adapter either into the core package or into a shared package for adapters, like @lazarv/react-server-adapter-core
.
What I have in mind already:
@vercel/nft
which is the only part using a progress bar as this takes some timeThe adapters for @lazarv/react-server
are more close to the various adapters SvelteKit have at https://github.com/sveltejs/kit/tree/main/packages. These are just generator / build scripts instead of Vite / Rollup plugins used in Waku. I'm not familiar with Vike deploy adapters.
The entrypoint for the Vercel serverless function is just using the middleware mode of the framework at https://github.com/lazarv/react-server/blob/main/packages/react-server-adapter-vercel/functions/index.mjs. I think something similar will be ok for AWS too, but surely needs more than this.
I looked at the existing code and the problems are:
hattip
middleware is wrapped with the @hattip/adapter-node
. Each serverless provider (e.g. aws: @hattip/adapter-aws-lambda
) needs its own hattip adapter which should be part of the adapter package and not part of the core.handling static files differ too. A typical AWS setup will use a Cloudfront (CDN) in front of a AWS Lambda (Serverless Function). The static files are deployed to a S3 bucket (block storage). As Cloudfront has limited possibilities (max. 25) to split traffic based on root path (e.g. /asset, /public) there is a need to serve static assets which are directly in the root directory or in dynamic directories with a static handler (e.g. @hattip/static
) from the lambda. There is a possibility to use Lambda@Edge to create a Routingfunction with all known static routs but this brings the problems of a deployment which needs to handle more than one region as the Lambda@Edge function can only live in the US East (N. Virginia)
region.
Here is a code I used to wrap the vike.dev middleware for AWS Lambda:
import { existsSync } from "node:fs";
import awsLambdaAdapter from "@hattip/adapter-aws-lambda";
import { walk } from "@hattip/walk";
import type { FileInfo } from "@hattip/walk";
import { createStaticMiddleware } from "@hattip/static";
import { createFileReader } from "@hattip/static/fs";
import hattipHandler from "@batijs/hattip/hattip-entry";
import type { Handler, APIGatewayProxyResultV2, APIGatewayProxyEventV2 } from "aws-lambda";
const root = new URL("./dist/client", import.meta.url); const staticRootExists = existsSync(root); const files = staticRootExists ? walk(root) : new Map<string, FileInfo>(); const staticMiddleware = staticRootExists ? createStaticMiddleware(files, createFileReader(root), { urlRoot: "/", }) : undefined;
const awsHandler = awsLambdaAdapter((ctx) => { if (hattipHandler === undefined) throw new Error("hattipHandler is undefined"); if (staticMiddleware === undefined) return hattipHandler(ctx); return staticMiddleware(ctx) || hattipHandler(ctx); });
export const handler: Handler<APIGatewayProxyEventV2, APIGatewayProxyResultV2> = awsHandler;
The deployment tool `sst` or `aws cdk` will need the entry point to the aws handler and a link to the folder with the static files for the s3 bucket. The entry point is than again bundled by the tools (set, idk) to include all dependencies as part of the deployment to aws.
I think I will need your help to get the restructuring of (1.) done.
Regarding dependency management I think that the @vercel/nft
package is doing a great job, stripping down any unused files even from the packages used.
It might would be a good approach in case of AWS sst or cdk to have a generated output that the developer can integrate into an existing sst or cdk setup, but also provide a simple default for easy deployment.
I think I will need your help to get the restructuring of (1.) done.
Yes I can prepare everything for this work to get started, I'll prioritize it up.
2. very simple rule like with Vercel to serve all static files first and then as a fallback call the Lambda.
Did you got this working? This only works for client side routing and not for server side routing - at least I never got that working ;-)
here is what Vike.dev is using to abstract the adapters: https://github.com/magne4000/universal-middleware
I am not sure if this kind of abstraction is really needed compared to choose one middleware and stick with it. Based on numbers its h3 which is used most and second by hono(hattip benchmarks) as the speed king but I am not an expert in this field. For people using your framework it should not matter what you choose to use as long as there is an adapter for the most used deployment platforms and based on my experience it is not hard to create a new one if the platform is not directly supported.
I have never used @vercel/nft
but bundling withesbuild
including dependencies strips down the whole deployment usually to under 5 MB.
When I looked at packages/react-server/lib/start/create-server.mjs
there have been dependencies from node:
which works with AWS Lambda
but not with Lambda@Edge
and other serverless runtimes running in a browser environment.
For the start I would provide a CDK Stack as its only javascript and does not need a native binary to be installed as it is the case with sst
which can be more difficult in a CD pipeline. The benefits of providing sst
deployments is the higher performance and the support of any target provided by terraform adapters.
You can also just try to copy together a production build of a simple example (even just a Hello World!) and deploy it manually to AWS Lambda by using any tech to be aware of any pain points which now exists, using https://github.com/lazarv/react-server/blob/main/packages/react-server/lib/start/node.mjs and https://github.com/lazarv/react-server/blob/main/packages/react-server/lib/start/create-server.mjs as a starting point to create a Lamda handler. The only necessary steps in this are:
runtime_init$
./render-stream.mjs
https://github.com/lazarv/react-server/blob/main/packages/react-server/lib/start/render-stream.mjsruntime$
state handling./ssr-handler.mjs
is needed https://github.com/lazarv/react-server/blob/main/packages/react-server/lib/start/ssr-handler.mjsWhen using a build which not included any client components, none of the static files are needed to make this work. I would suggest a Hello World! with a Math.random()
.
Following this there should be an AWS Lambda handler as result which only needs some refactor to include it in the framework similar to @lazarv/react-server/node
. Any other part of a complete deployment is about collecting necessary files.
Supporting Edge runtimes like Lambda@Edge or Vercel Edge is a feature I'm not working right now at all. I consider it nice to have for now. It needs major architectural work in the framework, starting with running router middlewares (like in Next.js) on the Edge.
I have never used @vercel/nft but bundling withesbuild including dependencies strips down the whole deployment usually to under 5 MB.
The issue with the esbuild
approach could be that we need the exact files generated at a production build as client components for SSR are lazily resolved on-demand using the manifest files for server, client and browser. But I'm not against it. Although Vite will drop esbuild
when Rolldown is ready (I think there will be some announcement around this very soon at ViteConf) and then it's not a good choice to stick to it on the long run. But for now if it works, I'm ok with it.
Hi @aheissenberger! If you're still interested, then you can use @lazarv/react-server-adapter-core
to create a new adapter based on the Vercel adapter and check out https://react-server.dev/deploy/api for more information. Let me know if you have any questions or suggestions about it.
Hi @aheissenberger! If you're still interested, then you can use
@lazarv/react-server-adapter-core
to create a new adapter based on the Vercel adapter and check out https://react-server.dev/deploy/api for more information. Let me know if you have any questions or suggestions about it.
still interested but was busy with work 😄
Description
I would like to help to implement the Adapter as I have done this for WAKU and Vike.dev (BATI.dev).
Suggested solution
using SST or CDK.
Alternative
No response
Additional context
Is there any other example which targets a node environment except for the Vercel adapter?
Validations