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

feat: pass hono's context in getLoadContext #24

Merged
merged 3 commits into from
Nov 20, 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
82 changes: 82 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,88 @@ export const onRequest = handle(build, server, { getLoadContext })

This way is almost the same as [Remix](https://remix.run/docs/en/main/guides/vite#augmenting-load-context).

### Getting Hono context

You can get the Hono context in Remix routes. For example, you can pass the value with `c.set()` from your Hono instance in the `server/index.ts`:

```ts
// server/index.ts
import { Hono } from 'hono'

const app = new Hono<{
Variables: {
message: string
}
}>()

app.use(async (c, next) => {
c.set('message', 'Hi from Hono')
await next()
})

export default app
```

In the Remix route, you can get the context from `args.context.hono.context`:

```ts
// app/routes/_index.tsx
import type { LoaderFunctionArgs } from '@remix-run/cloudflare'
import { useLoaderData } from '@remix-run/react'

export const loader = ({ context }) => {
const message = args.context.hono.context.get('message')
return { message }
}

export default function Index() {
const { message } = useLoaderData<typeof loader>()
return <h1>Message is {message}</h1>
}
```

To enable type inference, config the `load-context.ts` like follows:

```ts
// load-context.ts
import type { AppLoadContext } from '@remix-run/cloudflare'
import type { Context } from 'hono'
import type { PlatformProxy } from 'wrangler'

type Env = {
Variables: {
message: string
}
}

type Cloudflare = Omit<PlatformProxy, 'dispose'>

declare module '@remix-run/cloudflare' {
interface AppLoadContext {
cloudflare: Cloudflare
hono: {
context: Context<Env>
}
extra: string
}
}

type GetLoadContext = (args: {
request: Request
context: {
cloudflare: Cloudflare
hono: { context: Context<Env> }
}
}) => AppLoadContext

export const getLoadContext: GetLoadContext = ({ context }) => {
return {
...context,
extra: 'stuff',
}
}
```

## Auth middleware for Remix routes

If you want to add Auth Middleware, e.g. Basic Auth middleware, please be careful that users can access the protected pages with SPA tradition. To prevent this, add a `loader` to the page:
Expand Down
6 changes: 4 additions & 2 deletions examples/cloudflare-pages/app/routes/_index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import { useLoaderData } from '@remix-run/react'
export const loader = (args: LoaderFunctionArgs) => {
const extra = args.context.extra
const cloudflare = args.context.cloudflare
return { cloudflare, extra }
const myVarInVariables = args.context.hono.context.get('MY_VAR_IN_VARIABLES')
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This args.context.hono.context seems verbose, but I don't have a good idea for the naming.

return { cloudflare, extra, myVarInVariables }
}

export default function Index() {
const { cloudflare, extra } = useLoaderData<typeof loader>()
const { cloudflare, extra, myVarInVariables } = useLoaderData<typeof loader>()
return (
<div>
<h1>Remix and Hono</h1>
Expand All @@ -19,6 +20,7 @@ export default function Index() {
{cloudflare.caches ? 'caches are available' : ''}
</h3>
<h4>Extra is {extra}</h4>
<h5>Var in Variables is {myVarInVariables}</h5>
</div>
)
}
3 changes: 3 additions & 0 deletions examples/cloudflare-pages/e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ test('Should return 200 response - /', async ({ page }) => {

const contentH4 = await page.textContent('h4')
expect(contentH4).toBe('Extra is stuff')

const contentH5 = await page.textContent('h5')
expect(contentH5).toBe('Var in Variables is My variable set in c.set')
})

test('Should return 200 response - /api', async ({ page }) => {
Expand Down
20 changes: 16 additions & 4 deletions examples/cloudflare-pages/load-context.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,34 @@
import type { AppLoadContext } from '@remix-run/cloudflare'
import type { Context } from 'hono'
import type { PlatformProxy } from 'wrangler'

interface Env {
MY_VAR: string
type Env = {
Bindings: {
MY_VAR: string
}
Variables: {
MY_VAR_IN_VARIABLES: string
}
}

type Cloudflare = Omit<PlatformProxy<Env>, 'dispose'>
type Cloudflare = Omit<PlatformProxy<Env['Bindings']>, 'dispose'>

declare module '@remix-run/cloudflare' {
interface AppLoadContext {
cloudflare: Cloudflare
extra: string
hono: {
context: Context<Env>
}
}
}

type GetLoadContext = (args: {
request: Request
context: { cloudflare: Cloudflare }
context: {
cloudflare: Cloudflare
hono: { context: Context<Env> }
}
}) => AppLoadContext

// Shared implementation compatible with Vite, Wrangler, and Cloudflare Pages
Expand Down
9 changes: 6 additions & 3 deletions examples/cloudflare-pages/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,22 @@ const app = new Hono<{
Bindings: {
MY_VAR: string
}
Variables: {
MY_VAR_IN_VARIABLES: string
}
}>()

app.use(async(c, next) => {
app.use(async (c, next) => {
c.set('MY_VAR_IN_VARIABLES', 'My variable set in c.set')
await next()
c.header('X-Powered-By', 'Remix and Hono')
})

app.get('/api', (c) => {
return c.json({
message: 'Hello',
var: c.env.MY_VAR
var: c.env.MY_VAR,
})
})


export default app
14 changes: 11 additions & 3 deletions src/remix.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
import type { AppLoadContext } from '@remix-run/cloudflare'
import type { Context } from 'hono'

export type GetLoadContext = (args: {
type GetLoadContextArgs = {
request: Request
context: {
// Relaxing the type definition
// eslint-disable-next-line @typescript-eslint/no-explicit-any
cloudflare: any
hono: {
context: Context
}
}
}) => AppLoadContext | Promise<AppLoadContext>
}

export type GetLoadContext = (args: GetLoadContextArgs) => AppLoadContext | Promise<AppLoadContext>

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const defaultGetLoadContext = ({ context }: any): AppLoadContext => {
Expand All @@ -17,7 +22,7 @@ export const defaultGetLoadContext = ({ context }: any): AppLoadContext => {
}
}

export const createGetLoadContextArgs = (c: Context) => {
export const createGetLoadContextArgs = (c: Context): GetLoadContextArgs => {
return {
context: {
cloudflare: {
Expand All @@ -29,6 +34,9 @@ export const createGetLoadContextArgs = (c: Context) => {
// @ts-expect-error globalThis.caches is not typed
caches: globalThis.caches ? caches : undefined,
},
hono: {
context: c,
},
},
request: c.req.raw,
}
Expand Down