This is an example Todo App. Backend is written in Hono🔥 and Frontend is written in Remix.
Both are deployed on Cloudflare Workers / Pages, and connected via Service Bindings and Hono RPC.
Architectural diagram can be seen here.
This architecture allows for independent and loosely coupled front-end and back-end implementations. On the other hand, the actual API calls are type-assisted by Hono RPC and communicated within Cloudflare via Service Bindings, making them secure and fast.
The resulting backend API call via Service Bindings looks like this. You can try this page at here.
import type { LoaderFunctionArgs } from "@remix-run/cloudflare";
import { useLoaderData } from "@remix-run/react";
export async function loader({ context }: LoaderFunctionArgs) {
const api = getApi({ context }); // this is factory for hono/client
return await api.hello
.$get()
.then((res) => res.json())
.catch((err) => {
console.error(err);
return { error: "Failed to fetch data", message: null };
});
}
export default function Route() {
const data = useLoaderData<typeof loader>();
return (
<div>
<h1>Response from backend</h1>
<pre>
<code>{JSON.stringify(data, null, 2)}</code>
</pre>
</div>
);
}
Demo app is available at here.
TODO: add gif
pnpm install
cd apps/frontend
cp example.dev.vars .dev.vars
# put cookie secret for deploy
pnpm exec wrangler secret put COOKIE_SECRET
- Create D1 databse.
cd apps/backend
pnpm exec wrangler d1 create <DATABASE_NAME>
- Paste output to
apps/backend/wrangler.toml
.
[[d1_databases]]
binding = "DB" # i.e. available in your Worker on env.DB
database_name = "<DATABASE_NAME>"
database_id = "<DATABASE_ID>"
- Modify
dbCredentials
inapps/backend/drizzle.config.ts
.
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dbCredentials: {
dbName: "<DATABASE_NAME>", // edit here!!!
wranglerConfigPath: "./wrangler.toml",
},
driver: "d1",
out: "./migrations",
schema: "../../packages/module/src/schema.ts",
strict: true,
verbose: true,
});
- Put Password Salt.
pnpm exec wrangler secret put PASSWORD_SALT
-
Follow setup section.
-
Run
pnpm run dev
at project root.
.
├── apps
│ ├── backend
│ │ ├── drizzle.config.ts -> Drizzle ORM configuration
│ │ ├── package.json
│ │ ├── src
│ │ │ └── index.ts -> exports backend entrypoint from packages/api/src/server.ts
│ │ └── wrangler.toml -> backend worker configuration
│ └── frontend
│ ├── app -> Remix app
│ ├── lib
│ │ └── api.ts -> exports api client from packages/api/src/client.ts
│ ├── load-context.ts
│ ├── package.json
│ ├── worker-configuration.d.ts -> types generated from wrangler.toml
│ └── wrangler.toml -> frontend pages configuration
├── packages
│ ├── api
│ │ ├── package.json
│ │ └── src -> backend api built with Hono
│ └── module
│ └── src -> core module interact with DB
├── .gitignore
├── .prettierignore
├── eslint.config.mjs
├── package.json
├── README.md
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
├── prettier.config.mjs
├── README.md
├── tsconfig.json
└── turbo.json