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

React + Vite SSR with Hono #3162

Closed
redbaron76 opened this issue Jul 19, 2024 · 4 comments
Closed

React + Vite SSR with Hono #3162

redbaron76 opened this issue Jul 19, 2024 · 4 comments
Labels
enhancement New feature or request.

Comments

@redbaron76
Copy link

What is the feature you are proposing?

I'm trying to perform SSR using React + Vite in Hono instead of Express, as explained in all online examples. The problem is that Hono middlewares are not compatible with Connect instances, so it's not possible to achieve the same goal.

Vite server provides a vite.middlewares which is a Connect instance, good for Express but not for Hono 'cause it expects as req an IncomingMessage that Hono context doesn't provide.

Is there any plan to make Hono middlewares fully compatible with Connect or is there any walkaround to get compatibility or any example to follow?

Thanks

@redbaron76 redbaron76 added the enhancement New feature or request. label Jul 19, 2024
@yusukebe
Copy link
Member

Hi @redbaron76

Have you ever tried @hono/vite-dev-server? https://github.com/honojs/vite-plugins/tree/main/packages/dev-server?

@millsp
Copy link

millsp commented Jul 22, 2024

In my case, I can't use this vite dev server because it is the Remix server forwarding to hono, but not the opposite.

@thgh
Copy link

thgh commented Sep 5, 2024

Hono does actually provide the IncomingMessage using the node-server: https://hono.dev/docs/getting-started/nodejs#access-the-raw-node-js-apis

import { serve, type HttpBindings } from '@hono/node-server'

const app = new Hono<{ Bindings: HttpBindings }>()
app.all(
  '/*',
  createMiddleware<{ Bindings: HttpBindings }>(
    (ctx, next) =>
      new Promise((resolve) =>
        viteServer.middlewares(ctx.env.incoming, ctx.env.outgoing, () =>
          resolve(next())
        )
      )
  )
)

serve({ fetch: app.fetch, port: 3000 }, () => {
  console.log('Listening on http://localhost:3000')
})

Got something working to get Hono & Vite in both Bun & Node. Not sure if it extends to SSR, only supports streaming in Node.js

// Usage
import { serve, type HttpBindings } from '@hono/node-server'

const app = new Hono<{ Bindings: HttpBindings }>()
app.all('/*', viteMiddleware())

serve({ fetch: app.fetch, port: 3000 }, () => {
  console.log('Listening on http://localhost:3000')
})

// Library
import { createServer, type ViteDevServer } from 'vite'
import { IncomingMessage, ServerResponse } from 'http'
import react from '@vitejs/plugin-react'
import { HttpBindings } from '@hono/node-server'
import { createMiddleware } from 'hono/factory'

let server: ViteDevServer

export async function getViteServer() {
  if (!server) {
    server = await createServer({
      ...
      appType: 'spa',
      plugins: [react()],
    })
  }
  return server
}

export function viteMiddleware() {
  return createMiddleware<{ Bindings: HttpBindings }>(async (ctx, next) => {
    const vite = await getViteServer()
    return new Promise((resolve) => {
      // Node.js
      // @ts-expect-error
      if (typeof Bun === 'undefined') {
        vite.middlewares(ctx.env.incoming, ctx.env.outgoing, () =>
          resolve(next())
        )
        return
      }

      // Bun
      let sent = false
      const headers = new Headers()
      vite.middlewares(
        {
          url: new URL(ctx.req.raw.url, 'http://localhost').pathname,
          method: ctx.req.raw.method,
          headers: Object.fromEntries(
            // @ts-expect-error
            ctx.req.raw.headers
          ),
        } as IncomingMessage,
        {
          setHeader(name, value: any) {
            headers.set(name, value)
            return this
          },
          end(body) {
            sent = true
            resolve(
              ctx.body(body, {
                status: this.statusCode,
                statusText: this.statusMessage,
                headers,
              })
            )
          },
        } as ServerResponse,
        () => sent || resolve(next())
      )
    })
  })
}

@yusukebe
Copy link
Member

I'll close this issue.

As mentioned above, the Node.js adapter can handle the IncomingMessage. Then, we have the @hono/vite-dev-server https://github.com/honojs/vite-plugins/tree/main/packages/dev-serve.

You can use the @hono/vite-dev-server. Or if you want to implement another one yourself, you can refer to it.

Regarding the Connect compatible API, we can discuss it here: #3293

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request.
Projects
None yet
Development

No branches or pull requests

4 participants