Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: add cloudflare adapter docs and guide #254

Merged
merged 6 commits into from
Mar 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/heavy-nails-love.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"frames.js": minor
"docs": minor
---

feat: cloudflare workers adapter
4 changes: 2 additions & 2 deletions docs/pages/guides/deployment.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ description: "A guide on shipping your frames.js frame to production"

Frames.js works in most javascript frameworks or libraries, and should work out of the box with whereever you would normally deploy those.

We suggest deploying your frames to Vercel, but Docker, AWS or other platforms are compatible.
We suggest deploying your frames to Vercel, but [Cloudflare Workers](/reference/core/cloudflare-workers), Docker, AWS or other platforms are compatible.

## Ensure you set up the correct `env` variables

For the starter, `NEXT_PUBLIC_HOST` should be the full url (including https://) of the homepage.

## Vercel Deployments

Run the `vercel` command-line tool from your frame's folder to deploy your frame to vercel.
Run the `vercel` command-line tool from your frame's folder to deploy your frame to vercel.
6 changes: 0 additions & 6 deletions docs/pages/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,6 @@ import { HomePage } from "vocs/components";
<HomePage.Button href="https://github.com/framesjs/frames.js">
GitHub
</HomePage.Button>
<HomePage.Button href="#quickstart" variant="accent">
Quickstart
</HomePage.Button>
<HomePage.Button href="https://github.com/framesjs/frames.js">
GitHub
</HomePage.Button>
</HomePage.Buttons>
</HomePage.Root>

Expand Down
186 changes: 186 additions & 0 deletions docs/pages/reference/core/cloudflare-workers/index.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
# Cloudflare Workers integration

Frames.js can be easily deployed to [Cloudflare Workers](https://workers.cloudflare.com).

## Create a new project using `wrangler`

:::code-group

```bash [npm]
npm create cloudflare -- my-cf-frames --type hello-world --ts && cd ./my-cf-frames
```

```bash [yarn]
yarn create cloudflare my-cf-frames --type hello-world --ts && cd ./my-cf-frames
```

```bash [pnpm]
pnpm create cloudflare my-cf-frames --type hello-world --ts && cd ./my-cf-frames
```

:::

## Install `frames.js`

In order for `frames.js` to work properly in [Cloudflare Workers](https://workers.cloudflare.com) you must replace the `@vercel/og` dependency with `workers-og`. Follow following steps to override the dependency.

::::steps

### Override `@vercel/og` package with `workers-og`

Add following to your `package.json`.

:::code-group

```json [npm]
{
"overrides": {
"frames.js": {
"@vercel/og": "npm:workers-og@^0.0.23"
}
}
}
```

```json [yarn]
{
"resolutions": {
"@vercel/og": "npm:workers-og"
}
}
```

```json [pnpm]
{
"pnpm": {
"overrides": {
"@vercel/og": "npm:workers-og@^0.0.23"
}
}
}
```

:::

### Install the dependencies

After you have overridden the `@vercel/og` package with `workers-og`, you can install the dependencies.

:::code-group

```bash [npm]
npm install frames.js
```

```bash [yarn]
yarn add frames.js
```

```bash [pnpm]
pnpm add frames.js
```

:::
::::

## Write your Frames handler

Open the `src/index.ts` file and replace its content with the following code.

```tsx
import { createFrames, Button } from "frames.js/cloudflare-workers";

const frames = createFrames();
const handleRequest = frames(async (ctx) => {
const hasClicked = !!(ctx.message && ctx.searchParams.clicked);

return {
image: <span>Clicked: {hasClicked ? "Yes" : "No"}</span>,
buttons: hasClicked
? [
<Button action="post" target={{ query: { clicked: true } }}>
Click me
</Button>,
]
: [
<Button action="post" target="/">
Reset
</Button>,
],
};
});

export default {
fetch: handleRequest,
};
```

## Develop and test locally

You can test your Cloudflare Worker locally using `wrangler dev` and our [debugger](/guides/debugger#local-debugger-cli). Follow these steps to start developing locally:

::::steps

#### Start the local server

:::code-group

```bash [npm]
npm run dev
```

```bash [yarn]
yarn dev
```

```bash [pnpm]
pnpm dev
```

:::

#### Start the debugger

After you started the local server, you can start the debugger by running the following command where you replace `<local-url>` with a URL on which local server is running (see the output of above command, e.g. `http://localhost:8787`).

:::code-group

```bash [npm]
npx @frames.js/debugger --url <local-url>
```

```bash [yarn]
# yarn v1 doesn't have an alternative to npx, so you have to install the debugger globally (or use npx)
yarn global add @frames.js/debugger && frames --url <local-url>

# yarn v2
yarn dlx @frames.js/debugger --url <local-url>
```

```bash [pnpm]
pnpm dlx @frames.js/debugger --url <local-url>
```

:::

::::

## Deploy to Cloudflare Workers

When you tested your Frames app locally and you are ready to deploy it to Cloudflare Workers, run the following command.

:::code-group

```bash [npm]
npm run deploy
```

```bash [yarn]
yarn deploy
```

```bash [pnpm]
pnpm deploy
```

:::
4 changes: 4 additions & 0 deletions docs/vocs.config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ const sidebar = [
text: "Remix",
link: "/reference/core/remix",
},
{
text: 'Cloudflare Workers',
link: '/reference/core/cloudflare-workers'
}
],
},
{
Expand Down
10 changes: 10 additions & 0 deletions packages/frames.js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,16 @@
"types": "./dist/express/index.d.cts",
"default": "./dist/express/index.cjs"
}
},
"./cloudflare-workers": {
"import": {
"types": "./dist/cloudflare-workers/index.d.ts",
"default": "./dist/cloudflare-workers/index.js"
},
"require": {
"types": "./dist/cloudflare-workers/index.d.cts",
"default": "./dist/cloudflare-workers/index.cjs"
}
}
},
"files": [
Expand Down
27 changes: 27 additions & 0 deletions packages/frames.js/src/cloudflare-workers/index.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import * as lib from ".";

describe("cloudflare workers adapter", () => {
it.each(["createFrames", "Button"])("exports %s", (exportName) => {
expect(lib).toHaveProperty(exportName);
});

it("correctly integrates with Cloudflare Workers", async () => {
const frames = lib.createFrames();
const handler = frames(async (ctx) => {
expect(ctx.request.url).toBe("http://localhost:3000/");

return {
image: <span>Test</span>,
buttons: [<lib.Button action="post">Click me</lib.Button>],
};
});

const request = new Request("http://localhost:3000");

// @ts-expect-error - expects fetcher property on request but it is not used by our lib
const response = await handler(request, {}, {});

expect(response.status).toBe(200);
expect(response.headers.get("content-type")).toBe("text/html");
});
});
56 changes: 56 additions & 0 deletions packages/frames.js/src/cloudflare-workers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
export { Button, type types } from "../core";
import { createFrames as coreCreateFrames, types } from "../core";
import { CoreMiddleware } from "../middleware";
import { Buffer } from "node:buffer";

// make Buffer available on globalThis so it is compatible with cloudflare workers
// eslint-disable-next-line no-undef
globalThis.Buffer = Buffer;

type CreateFramesForCloudflareWorkers = types.CreateFramesFunctionDefinition<
CoreMiddleware,
(req: Request) => Promise<Response>

Check warning on line 12 in packages/frames.js/src/cloudflare-workers/index.ts

View workflow job for this annotation

GitHub Actions / lint

'req' is defined but never used
>;

/**
* Creates Frames instance to use with you Hono server
*
* @example
* import { createFrames, Button } from 'frames.js/cloudflare-workers';
*
* const frames = createFrames();
* const fetch = frames(async (ctx) => {
* return {
* image: <span>Test</span>,
* buttons: [
* <Button action="post">
* Click me
* </Button>,
* ],
* };
* });
*
* export default {
* fetch,
* } satisfies ExportedHandler;
*/
// @ts-expect-error
export const createFrames: CreateFramesForCloudflareWorkers =
function createFramesForCloudflareWorkers(
options?: types.FramesOptions<any, any>
) {
const frames = coreCreateFrames(options);

return function cloudflareWorkersFramesHandler<
TPerRouteMiddleware extends types.FramesMiddleware<any, any>[],
>(
handler: types.FrameHandlerFunction<any, any>,
handlerOptions?: types.FramesRequestHandlerFunctionOptions<TPerRouteMiddleware>
) {
const framesHandler = frames(handler, handlerOptions);

return async function handleCloudflareWorkersRequest(req) {
return framesHandler(req);
};
};
};
3 changes: 3 additions & 0 deletions packages/frames.js/tsup.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import { defineConfig } from "tsup";
import glob from "fast-glob";

export default defineConfig({
// this is necessary for cloudfare workers adapter to be able to import
// buffer from cloudflare compatiblity layer
external: ["node:buffer"],
dts: true,
format: ["cjs", "esm"],
clean: true,
Expand Down
Loading