elysia
elysia copied to clipboard
Cloudflare Pages/Worker Example
The documentation briefly mentions Cloudflare, but Wrangler was a bit more annoying to set up than I expected. Should I add a documentation section or can I just somehow link to an example?
Regardless, I made an example.
Issues:
-
@elysiajs/html
is incompatible with Cloudflare, using the underlying@kitajs/html
seems to work, then again, no streaming sadly. - The bundle to deploy to Cloudflare seems to be quite big (170kb), vs Hono (27kb). Maybe someone can put more time in bundling Elysia for Cloudflare.
Bit of a question but, did you manage to get environment variables working with Elysia?
@tarosbubbletea
This is how I'm doing right now:
Tiny wrapper for cloudflare Elysia class:
export class Ely<
BasePath extends string = "",
Decorators extends DecoratorBase = {
request: {};
store: {};
derive: {};
resolve: {};
},
Definitions extends DefinitionBase = {
type: {};
error: {};
},
ParentSchema extends RouteSchema = {},
Macro extends Record<string, unknown> = {},
Routes extends RouteBase = {},
Scoped extends boolean = false,
> extends Elysia<
BasePath,
Decorators & { request: { env: any; ctx: any } }, // <--- Notice this line: we are telling it that 'env' and 'ctx' are decorators
Definitions,
ParentSchema,
Macro,
Routes,
Scoped
> {
constructor(config?: Partial<ElysiaConfig<BasePath, Scoped>>) {
config ??= {};
config.aot = false;
super(config);
}
}
Then your app can access it:
const app = new Ely()
.get("/", ({env, ctx}) => env.FOO) // Code completion and typescript works!
Finally declare the entry point like this:
export default {
fetch(request: Request, env: any, ctx: any) {
return new Ely().decorate({ env, ctx }).use(app).fetch(request);
},
};
If you want to you can refine the env
and ctx
types to use your own Env
interface and cloudflare's ExecutionContext
.
import { Elysia } from "elysia";
import type { Context } from "elysia";
type Env = {
SECRET: string;
API_HOST: string;
};
interface CF extends Context {
env: Env;
ctx: ExecutionContext;
}
const app = new Elysia({ aot: false });
app.get("/", (c: CF) => {
console.log(typeof c.ctx.waitUntil);
console.log(c.env.API_HOST);
return "Elysia on Workers";
});
export default {
fetch: (request: Request, env: Env, ctx: ExecutionContext) => {
return app.decorate({ env, ctx }).handle(request);
},
};
@Rados51 is there a way to use that context globally for multi file apis?
(ie. have this in index.ts
and want to use some properties in utils/auth.ts
)
@SelfhostedPro You can pass it as a function argument or store it as a state.
app.derive((c: CF) => {
const auth = utilsAuth(c.ctx);
return { auth }
});
Where does ExecutionContext
come from?
Where does
ExecutionContext
come from?
@cloudflare/workers-types
This breaks type inference with things like params:
app.get(
'/foo/:bar',
async (c: CF) => {
return c.params.bar;
},
{
params: t.Object({
bar: t.String(),
}),
},
);
Params is inferred as as never
@Ehesp You just need to update to the latest version, and it will work out of the box.
@Ehesp in version like 0.8
, you would use something like
interface CF extends Context<RouteSchema, DecoratorBase, "/id/:id"> {
env: Env;
ctx: ExecutionContext;
}
interface CF<P extends string> extends Context<RouteSchema, DecoratorBase, P> {
env: Env;
ctx: ExecutionContext;
}
@Rados51 Thanks, unless I'm doing something odd it doesn't seem to work on the latest (1.1.23):
import { Context, t } from 'elysia';
export interface App extends Context {
env: Env;
ctx: ExecutionContext;
}
sessionController.post('/:bar', (app: App) => {
const requestBody = app.body;
console.log(app.params);
// ...
}, {
body: schemaSessionCreate,
params: t.Object({
bar: t.String(),
}),
});
In this case, app.ctx
and app.env
are typed, however app.body
is unknown
, and app.params
is a generic Record<string, string>
.