rphlmr / react-router-hono-server

Remix with Hono in less than 10 seconds
https://www.npmjs.com/package/react-router-hono-server
MIT License
126 stars 3 forks source link

WebSocket #23

Open penspinner opened 3 days ago

penspinner commented 3 days ago

Hi. Is there a guide on how to add a WebSocket or socket.io to the server?

rphlmr commented 3 days ago

👋 You can follow the official doc: https://hono.dev/docs/helpers/websocket And for Node: https://github.com/honojs/middleware/tree/main/packages/node-ws

I expose the underlying Hono app through the configure option. You can add any middleware you want.

I think this work for node, based on the plugin example:

import { createHonoServer } from "react-router-hono-server/node";
import { createNodeWebSocket } from "@hono/node-ws";

let $injectWebSocket = () => {}

const server = await createHonoServer({
  configure: (app) => {
    const { injectWebSocket, upgradeWebSocket } = createNodeWebSocket({ app });
    // we need to use that later
    $injectWebSocket = injectWebSocket

    app.get(
      "/ws",
      upgradeWebSocket((c) => ({
        // https://hono.dev/helpers/websocket
      })),
    );

  },
});

$injectWebSocket(server);

export default server;
penspinner commented 3 days ago

I'm getting these two type errors when trying what you suggested.

app/server.ts:11:71 - error TS2322: Type 'Hono<E, BlankSchema, "/">' is not assignable to type 'Hono<BlankEnv, BlankSchema, "/">'.
  The types of 'get(...).get(...).on' are incompatible between these types.
    Type 'OnHandlerInterface<IfAnyThenEmptyObject<EnvOrEmpty<IfAnyThenEmptyObject<EnvOrEmpty<E>> & {}>> & {}, { [x: string]: { ...; }; }, "/">' is not assignable to type 'OnHandlerInterface<{}, { [x: string]: { $get: { input: any; output: {}; outputFormat: string; status: StatusCode; }; }; }, "/">'.
      Types of parameters 'handlers' and 'handlers' are incompatible.
        Type 'H<{}, any, any, any>' is not assignable to type 'H<IfAnyThenEmptyObject<EnvOrEmpty<IfAnyThenEmptyObject<EnvOrEmpty<E>> & {}>> & {}, any, any, any>'.
          Type 'Handler<{}, any, any, any>' is not assignable to type 'H<IfAnyThenEmptyObject<EnvOrEmpty<IfAnyThenEmptyObject<EnvOrEmpty<E>> & {}>> & {}, any, any, any>'.
            Type 'Handler<{}, any, any, any>' is not assignable to type 'Handler<IfAnyThenEmptyObject<EnvOrEmpty<IfAnyThenEmptyObject<EnvOrEmpty<E>> & {}>> & {}, any, any, any>'.
              Types of parameters 'c' and 'c' are incompatible.
                Type 'Context<IfAnyThenEmptyObject<EnvOrEmpty<IfAnyThenEmptyObject<EnvOrEmpty<E>> & {}>> & {}, any, any>' is not assignable to type 'Context<{}, any, any>'.
                  Types of property 'set' are incompatible.
                    Type 'Set<IsAny<IfAnyThenEmptyObject<EnvOrEmpty<IfAnyThenEmptyObject<EnvOrEmpty<E>> & {}>> & {}> extends true ? { ...; } : IfAnyThenEmptyObject<...> & {}>' is not assignable to type 'Set<{}>'.
                      Type '{}' is not assignable to type 'IsAny<IfAnyThenEmptyObject<EnvOrEmpty<IfAnyThenEmptyObject<EnvOrEmpty<E>> & {}>> & {}> extends true ? { ...; } : IfAnyThenEmptyObject<...> & {}'.

11   const { injectWebSocket, upgradeWebSocket } = createNodeWebSocket({ app })
                                                                         ~~~

  node_modules/@hono/node-ws/dist/index.d.mts:11:5
    11     app: Hono;
           ~~~
    The expected type comes from property 'app' which is declared here on type 'NodeWebSocketInit'

app/server.ts:61:18 - error TS2345: Argument of type 'Hono<BlankEnv, BlankSchema, "/">' is not assignable to parameter of type 'Server<typeof IncomingMessage, typeof ServerResponse> | Http2Server<typeof IncomingMessage, typeof ServerResponse, typeof Http2ServerRequest, typeof Http2ServerResponse> | Http2SecureServer<...>'.
  Type 'Hono<BlankEnv, BlankSchema, "/">' is missing the following properties from type 'Http2SecureServer<typeof IncomingMessage, typeof ServerResponse, typeof Http2ServerRequest, typeof Http2ServerResponse>': addListener, emit, once, prependListener, and 26 more.

61 $injectWebSocket(server)
                    ~~~~~~

Found 2 errors.
penspinner commented 3 days ago

Also, doesn't seem to work with socket.io based on https://socket.io/docs/v4/server-initialization/#with-hono-nodejs:

import { serve } from '@hono/node-server'
import { compress } from 'hono/compress'
import { createHonoServer } from 'react-router-hono-server/node'
import { Server } from 'socket.io'

const honoServer = await createHonoServer({
    configure: (app) => {
        app.use(compress())
    },
    getLoadContext: (ctx, { build }) => {
        return { nonce: ctx.get('nonce'), serverBuild: build }
    },
})

const httpServer = serve({
    fetch: honoServer.fetch,
    port: 5173,
})
const io = new Server(httpServer)

io.on('connection', (socket) => {
    console.log(socket)
})

export default httpServer

Error:

[vite] Internal server error: app.fetch is not a function
      at getRequestListener.overrideGlobalObjects
rphlmr commented 3 days ago

Thanks for reporting. I'll try to share an example if I manage to make something that works. I'll ping you back!

rphlmr commented 1 day ago

Good news. I should be able to release a WS support tomorrow!

expected API:

import { createNodeWebSocket } from "@hono/node-ws";
import { Hono } from "hono";
import { createHonoServer } from "react-router-hono-server/node";

const app = new Hono();
const { injectWebSocket, upgradeWebSocket } = createNodeWebSocket({ app });

export default await createHonoServer({
  app,
  onServe(server) {
    injectWebSocket(server);
  },
  configure(app) {
    app.get(
      "/ws",
      upgradeWebSocket(() => ({
        // https://hono.dev/helpers/websocket
        onOpen() {
          console.log("New connection 🔥");
        },
        onMessage(event, ws) {
          console.log(`Message from client: ${event.data}`);
          ws.send(`${event.data}`);
        },
        onClose: () => {
          console.log("Connection closed");
        },
      }))
    );
  },
});
penspinner commented 1 day ago

Nice! Thanks. Do you have an example with socket.io as well?