Confused about bun / hono implementation

hey, its been about a month since ive messed around. seems like theres been alot of big updates. right now, my old implementation w/ hono + bun doesnt seem to be working //routes/rivet-registry.ts export const registry = setup({ use: { ...actors }, }) const driver = createMemoryDriver() const rivetRegistry = (opts: RunConfig['cors']) => { const { hono } = registry.createServer({ // cors: opts, getUpgradeWebSocket: () => upgradeWebSocket, driver, }) return hono } // main entry app.route('/registry', rivet(corsOptions)) the docs only show support for hono w/ a node server, does the new package have a way to return a hono route with registry.start ?
84 Replies
jog1t
jog1t3mo ago
does the new package have a way to return a hono route with registry.start
no but... you can use it in the other way
import { serve } from "@hono/node-server";
import { Hono } from "hono";
import { registry } from "./registry";

// Start RivetKit
const { client } = registry.start();

// Setup router
const app = new Hono();

// Example HTTP endpoint
app.post("/increment/:name", async (c) => {
const name = c.req.param("name");

const counter = client.counter.getOrCreate(name);
const newCount = await counter.increment(1);

return c.text(`New Count: ${newCount}`);
});

serve({ fetch: app.fetch, port: 8080 });
console.log("Listening on port 8080");
import { serve } from "@hono/node-server";
import { Hono } from "hono";
import { registry } from "./registry";

// Start RivetKit
const { client } = registry.start();

// Setup router
const app = new Hono();

// Example HTTP endpoint
app.post("/increment/:name", async (c) => {
const name = c.req.param("name");

const counter = client.counter.getOrCreate(name);
const newCount = await counter.increment(1);

return c.text(`New Count: ${newCount}`);
});

serve({ fetch: app.fetch, port: 8080 });
console.log("Listening on port 8080");
nicolas-angelo
nicolas-angeloOP3mo ago
yea but the node-server defeats the purpose of using bun i had already mentioned this to @Nathan back when i first started using rivetkit. im already running a server w/ hono using Bun.serve so i cant have a seperate server im guessing ill have to downgrade for now
jog1t
jog1t3mo ago
maybe this will help (it uses Deno, but I think Bun has something similar)?
import { upgradeWebSocket } from "hono/deno";
import { registry } from "./registry.ts";

const { fetch } = registry.start({
// Deno requires using Deno.serve
disableServer: true,
// Specify Deno-specific upgradeWebSocket
getUpgradeWebSocket: () => upgradeWebSocket,
});

// Start server
Deno.serve({ port: 8080 }, fetch);
import { upgradeWebSocket } from "hono/deno";
import { registry } from "./registry.ts";

const { fetch } = registry.start({
// Deno requires using Deno.serve
disableServer: true,
// Specify Deno-specific upgradeWebSocket
getUpgradeWebSocket: () => upgradeWebSocket,
});

// Start server
Deno.serve({ port: 8080 }, fetch);
.start returns fetch handler so you can pass it around
nicolas-angelo
nicolas-angeloOP3mo ago
yea i saw the fetch handler,. im going to have to play around with it. but it was super convenient back when you had built in hono app router support
jog1t
jog1t3mo ago
cc: @Nathan 👆🏻 btw, for bun it will be something like this:
const { fetch } = registry.start();

Bun.serve({
// `routes` requires Bun v1.2.3+
routes: {
/* your routes */
},
fetch(req, ...args) {
return fetch(req, ...args);
},
});
const { fetch } = registry.start();

Bun.serve({
// `routes` requires Bun v1.2.3+
routes: {
/* your routes */
},
fetch(req, ...args) {
return fetch(req, ...args);
},
});
nicolas-angelo
nicolas-angeloOP3mo ago
yep, i was thinking same thing. just figured out how to build a router for rivet & hono Bun.serve({ fetch: (req, server) => { if (req.url.includes('/registry')) { return createRivetRegistry().fetch(req) } return app.fetch(req, { ...env, server }) }, }) im assuming all request from rivet client will start with /registry?
jog1t
jog1t3mo ago
Unfortunately, that has changed also, all routes are available from "/"
nicolas-angelo
nicolas-angeloOP3mo ago
and also, this means i have to change the websocket handler since the request isnt running through hono anymore. hono has a utility for this already const createRivetRegistry = (opts: RunConfig['cors'] = {}) => { const server = registry.start({ cors: opts, getUpgradeWebSocket: () => upgradeWebSocket, driver, }) return server } so this is a super painful transition lol
jog1t
jog1t3mo ago
no, but you can specify the basepath, its up to you now
nicolas-angelo
nicolas-angeloOP3mo ago
ok, i updated the base path. think i just need to figure out the websockets now. i dont use the bun native websocket api directly since hono abstracts that but im sure ill figure it out
jog1t
jog1t3mo ago
Unfortunately, this was required as the cors for the rivetkit should be separate from the whole application is some cases, where it will be exposed to public, whereas the other endpoints shouldnot. i think it will be something similar to:
const { upgradeWebSocket, websocket } = createBunWebSocket()
const { fetch } = registry.start({
basePath: "/registry",
dislabeServer: true,
getUpgradeWebSocket: () => upgradeWebSocket
});


Bun.serve({
fetch(req, server) {
if(req.url.startsWith("/registry")) {
return fetch(req);
}
return app.fetch(req, { ...env, server })
},
websocket,
});
const { upgradeWebSocket, websocket } = createBunWebSocket()
const { fetch } = registry.start({
basePath: "/registry",
dislabeServer: true,
getUpgradeWebSocket: () => upgradeWebSocket
});


Bun.serve({
fetch(req, server) {
if(req.url.startsWith("/registry")) {
return fetch(req);
}
return app.fetch(req, { ...env, server })
},
websocket,
});
nicolas-angelo
nicolas-angeloOP3mo ago
yea but this only works in the context of a hono app instance const { upgradeWebSocket, websocket } = createBunWebSocket()
jog1t
jog1t3mo ago
yep
nicolas-angelo
nicolas-angeloOP3mo ago
so it would be skipped by the rivet fetch handler im looking at honos code for the bun websocket utilities and it would be an absolute pain to recreate this
nicolas-angelo
nicolas-angeloOP3mo ago
theres no way you can bring this back? https://github.com/rivet-dev/rivetkit/blob/main/packages/rivetkit/src/manager/router.ts#L79 'createManagerRouter`
GitHub
rivetkit/packages/rivetkit/src/manager/router.ts at main · rivet-d...
A library for building stateful workloads anywhere - rivet-dev/rivetkit
nicolas-angelo
nicolas-angeloOP3mo ago
im trying to rebuild it myself but theres alot of rivetkit internals that arent exposed in order to achieve this
jog1t
jog1t3mo ago
not sure what you mean by that? 🤔
nicolas-angelo
nicolas-angeloOP3mo ago
well im trying to avoid copying the whole file. theres internals like the logger(), handleRouteNotFound, handleRouteError, addManagerRoutes, addServerlessRoutes I understand most of them and i can probably just re-create but i really dont want all the junk in my project im sorry, ill just have to think on this for a while and see if downgrading is easier for now. I pitched a whole project involving the use of rivetkit and i didnt know all these changes were made so its a little frustrating that some of the features that made hono & bun work smoothly are now left out. but its my fault for not keeping up. Appreciate the help
nicolas-angelo
nicolas-angeloOP3mo ago
i mean if you actually tested it and it worked i could be wrong but from my understanding - it shouldnt work becasue these utilities: const { upgradeWebSocket, websocket } = createBunWebSocket() Are not bun native. they are hono absractions and work only in the context of hono's router (app.fetch). So using the following routing strategy means that 'upgradeWebSocket' wont work since 'app.fetch' is called after the rivet routes get picked up
Bun.serve({
fetch(req, server) {
if(req.url.startsWith("/registry")) {
return fetch(req);
}
return app.fetch(req, { ...env, server })
},
websocket,
});
Bun.serve({
fetch(req, server) {
if(req.url.startsWith("/registry")) {
return fetch(req);
}
return app.fetch(req, { ...env, server })
},
websocket,
});
nicolas-angelo
nicolas-angeloOP3mo ago
GitHub
hono/src/adapter/bun/websocket.ts at main · honojs/hono
Web framework built on Web Standards. Contribute to honojs/hono development by creating an account on GitHub.
nicolas-angelo
nicolas-angeloOP3mo ago
export const upgradeWebSocket: UpgradeWebSocket<any> = defineWebSocketHelper((c, events) => {...} takes in c which is hono context. that isnt available here: if(req.url.startsWith("/registry")) { return fetch(req); } wait is the fetch handler returned from registry.start() a hono app fetch handler? or a web standard Request / Response handler?
jog1t
jog1t3mo ago
its a hono app fetch
nicolas-angelo
nicolas-angeloOP3mo ago
oh because when i looked at the .d.ts files for registry.start, the fetch was just typed as a plain handler: fetch: (request: Request, ...args: any) => Response | Promise<Response>; Normally honos fetch handler is typed as: fetch: (request: Request, Env?: E["Bindings"] | {}, executionCtx?: ExecutionContext) => Response | Promise<Response>; so this led me to believe that there was no longer a hono app being used behind the scenes my bad
Nathan
Nathan3mo ago
this works for me:
app.use("/rivet/*", async (c) => {
return await fetch(c.req.raw, c.env);
});
app.use("/rivet/*", async (c) => {
return await fetch(c.req.raw, c.env);
});
though not pretty
nicolas-angelo
nicolas-angeloOP3mo ago
thats not too bad though, i would prefer that, then the above implemention in the bun server so i would just change the base path the /rivet
Nathan
Nathan3mo ago
we'll update the docs in a bit
nicolas-angelo
nicolas-angeloOP3mo ago
overrideServerAddress doesnt seem to be an available option on the config is that updated to a different option property?
jog1t
jog1t3mo ago
it should be available in the latest release
nicolas-angelo
nicolas-angeloOP3mo ago
yea im on 2.0.9 its not showing up in typescript i just nuked node modules and tried again. no luck (at least with intellisense) the zod schema in the .d.ts file doesnt show it available either disableDefaultServer doesnt seem to be available either
Nathan
Nathan3mo ago
that's new i'll cut a release in a sec, it's used to auto-configure the inspector & logs. nothing critical
nicolas-angelo
nicolas-angeloOP3mo ago
does it have anything to do with getting this error?: level=ERROR msg="failed to import @hono/node-server. please run 'npm install @hono/node-server @hono/node-ws'" just tried running my dev server after getting an actor set up
Nathan
Nathan3mo ago
we renamed disableServer to disableDefaultServer, i'm not sure if/when that was released. this part of the api is flagged as experimental, apologies for the hassle
nicolas-angelo
nicolas-angeloOP3mo ago
ahh gotcha. yes ' disableServer: true' seems to work (currently on 2.0.9)
Nathan
Nathan3mo ago
zod should've validated that hmm the prop name will change in the next release
nicolas-angelo
nicolas-angeloOP3mo ago
ok everything seems to be working smoothly again! are there new docs for migrating onAuth ? i see thats been moved to onBeforeConnect im assuming you handle auth inside there and just throw if needed?
Nathan
Nathan3mo ago
our docs are in the process of getting a pass this week yep either: - handle auth on the actor itself - set up your own endpoint to handle auth then forward request to the actor
nicolas-angelo
nicolas-angeloOP3mo ago
oh nice thats much cleaner
Nathan
Nathan3mo ago
going for simplicity here
nicolas-angelo
nicolas-angeloOP3mo ago
@Nathan - got this error in my terminal appx 30-50 seconds after i called and endpoint that used an actor client within an api route; cannot sleep actor with memory driver, must use file system driver For context, im using bun --watch on my server
Nathan
Nathan3mo ago
oop looks like the sleep enabled flag is not working
nicolas-angelo
nicolas-angeloOP3mo ago
do i need to dispose of the client before returning a response?
Nathan
Nathan3mo ago
do you have memory mode enabled btw
nicolas-angelo
nicolas-angeloOP3mo ago
yep using the memory driver
Nathan
Nathan3mo ago
ah ok, can you switch to fs for a sec? i'll get that fixed
nicolas-angelo
nicolas-angeloOP3mo ago
np. thanks for the speedy response! (you too @jog1t )
jog1t
jog1t3mo ago
no worries!
nicolas-angelo
nicolas-angeloOP3mo ago
Two questions (I'm asking because I'm unable to test at moment but still curious) 1. Does the onstatechange event now have a way of comparing old state to new state? Like if I access ctx.state and params.newState - did ctx already get updated or does it hold prev state?
Nathan
Nathan3mo ago
not currently, because under the hood this requires us to clone the entire state object which adds a bunch of overhead. you can do this yourself using c.vars atm. we should prob get rid of newstate, doesn't rly make sense
nicolas-angelo
nicolas-angeloOP3mo ago
Ah yes. Good point. Yea seeing it as newState in the types makes me think it's comparable to old state
Nathan
Nathan3mo ago
we can technically expose the property that changed and the old value of that property, but i kind of want to keep the api abstract so we can optmize this more and more
nicolas-angelo
nicolas-angeloOP3mo ago
can i set connection state in onBeforeConnect? Or is that not possible based on the order of hook execution
jog1t
jog1t3mo ago
At this moment you can’t unfortunately. @Nathan just for confirmation I think you could set state for the connection tho
nicolas-angelo
nicolas-angeloOP3mo ago
Yea that's what's I meant Not regular state, but can I do conn.state.somevalue = "x" ? Or does conn.state get created after onBeforeConnect is executed?
jog1t
jog1t3mo ago
Give me a few, let me check
Nathan
Nathan2mo ago
ah, looks like not. you should be able to. i'll create an issue for that. i'd recommend using createConnState for now
nicolas-angelo
nicolas-angeloOP2mo ago
no worries. also running into an error w/ useActor on react side const client = createClient<Registry>('http://localhost:8080') should this be base server url or <server_url>/registry (the endpoint im exposing the actor registry on) i tried both tbh. they both error
Nathan
Nathan2mo ago
it should be http://localhost:8080/registry can you check your network inspector and see what's up? there's a chance it's cors too
nicolas-angelo
nicolas-angeloOP2mo ago
PUT http://localhost:8080/registry/actors?namespace=default that the error in the browser console 404 ignore me. fml. i had the wrong endpoint its been a long day lol
Nathan
Nathan2mo ago
haha sg sg
nicolas-angelo
nicolas-angeloOP2mo ago
hold up got another error and this one looks weird level=WARN msg="internal error" error="Unreachable path: /rivet/connect/websocket" stack="Error: Unreachable path: /rivet/connect/websocket\n at proxyWebSocket (/home/nicka/projects/chat-pdf/node_modules/.bun/rivetkit@2.0.9+70dd36e0dd92d7aa/node_modules/rivetkit/dist/tsup/chunk-FZP2IBIX.js:2646:17)\n at proxyWebSocket (/home/nicka/projects/chat-pdf/node_modules/.bun/rivetkit@2.0.9+70dd36e0dd92d7aa/node_modules/rivetkit/dist/tsup/chunk-FZP2IBIX.js:2619:24)\n at handleWebSocketGateway (/home/nicka/projects/chat-pdf/node_modules/.bun/rivetkit@2.0.9+70dd36e0dd92d7aa/node_modules/rivetkit/dist/..." issues=https://github.com/rivet-dev/rivetkit/issues support=https://rivet.dev/discord method=GET path=/rivet/connect/websocket this is happening server side in my terminal even though before connecting client side, the registry was working client: WebSocket connection to 'ws://localhost:8080/rivet/connect/websocket' failed: I know for sure im using hono's websocket & upgradeWebsocket though
Nathan
Nathan2mo ago
it looks like the path is not being stripped correctly, it should be logging /connect/websocket not /rivet/connect/websocket. i'm not sure why the http requests work but the websockets don't though can you send a snippet of how you're mounting rivet wait nvm i might see the issue nvm my examlpe works fine
nicolas-angelo
nicolas-angeloOP2mo ago
// server.ts bun entry

import app from './app'
import createServer from '@/utils/create-server'
import { websocket } from 'hono/bun'
import { isDev, env } from './env'

const server = createServer({
development: isDev,
port: env.PORT,
websocket,
fetch: (req, server) => {
return app.fetch(req, { ...env, server })
},
})
// server.ts bun entry

import app from './app'
import createServer from '@/utils/create-server'
import { websocket } from 'hono/bun'
import { isDev, env } from './env'

const server = createServer({
development: isDev,
port: env.PORT,
websocket,
fetch: (req, server) => {
return app.fetch(req, { ...env, server })
},
})
// app.ts - hono entry

import { env } from './env'
import { contextStorage } from 'hono/context-storage'
import { notFound, onError } from 'stoker/middlewares'
import { cors, corsOptions } from './middleware'
import { clerk } from './middleware/clerk'
import { createApp } from './utils/create-app'
import { createRivetRegistry } from '@/lib/rivetkit'
// workers
import '@/workers'
import '@/workers/events'

const { fetch } = createRivetRegistry({
basePath: '/rivet',
cors: corsOptions,
disableServer: true,
})

const app = createApp()

app.use(cors)
app.use(contextStorage())
app.use(clerk({ machineSecretKey: undefined }))

app.onError(onError)
app.notFound(notFound)

app.use('/rivet/*', async c => {
return await fetch(c.req.raw, c.env)
})

export default app
// app.ts - hono entry

import { env } from './env'
import { contextStorage } from 'hono/context-storage'
import { notFound, onError } from 'stoker/middlewares'
import { cors, corsOptions } from './middleware'
import { clerk } from './middleware/clerk'
import { createApp } from './utils/create-app'
import { createRivetRegistry } from '@/lib/rivetkit'
// workers
import '@/workers'
import '@/workers/events'

const { fetch } = createRivetRegistry({
basePath: '/rivet',
cors: corsOptions,
disableServer: true,
})

const app = createApp()

app.use(cors)
app.use(contextStorage())
app.use(clerk({ machineSecretKey: undefined }))

app.onError(onError)
app.notFound(notFound)

app.use('/rivet/*', async c => {
return await fetch(c.req.raw, c.env)
})

export default app
import {
createMemoryDriver,
createFileSystemDriver,
type RunConfigInput,
} from 'rivetkit'
import { upgradeWebSocket } from 'hono/bun'
import { registry } from '@/actors'

// const driver = createMemoryDriver()
const createDriver = (type: 'memory' | 'fs') => {
if (type === 'fs') {
return createFileSystemDriver()
}
return createMemoryDriver()
}

export const createRivetRegistry = (
config: RunConfigInput & { driverType?: 'memory' | 'fs' }
) => {
const server = registry.start({
driver: createDriver(config.driverType || 'fs'),
...config,
getUpgradeWebSocket: () => upgradeWebSocket,
})
return server
}
import {
createMemoryDriver,
createFileSystemDriver,
type RunConfigInput,
} from 'rivetkit'
import { upgradeWebSocket } from 'hono/bun'
import { registry } from '@/actors'

// const driver = createMemoryDriver()
const createDriver = (type: 'memory' | 'fs') => {
if (type === 'fs') {
return createFileSystemDriver()
}
return createMemoryDriver()
}

export const createRivetRegistry = (
config: RunConfigInput & { driverType?: 'memory' | 'fs' }
) => {
const server = registry.start({
driver: createDriver(config.driverType || 'fs'),
...config,
getUpgradeWebSocket: () => upgradeWebSocket,
})
return server
}
im wondering its bc hono's recent splitting of upgradeWebsocket & websocket they now provide seperate exports for each instead of getting them from a factory fn
Nathan
Nathan2mo ago
what's your bun v
nicolas-angelo
nicolas-angeloOP2mo ago
latest 1.2.23
Nathan
Nathan2mo ago
please hold, apple moment
No description
nicolas-angelo
nicolas-angeloOP2mo ago
cant relate. struggling with my own WSL moments lol
Nathan
Nathan2mo ago
lol
nicolas-angelo
nicolas-angeloOP2mo ago
wait - since you guys are creating a hono app, and its longer attached to a route, but we use fetch handler instead is it possible that the upgradeWebsocket passed into the rivet server config is a different context from the websocket attached directly on bun's server?
Nathan
Nathan2mo ago
sorry it's taking me to get back to you on this, need to wrap up a couple other tasks first
nicolas-angelo
nicolas-angeloOP2mo ago
all good I should not have prematurely gutted my nextjs server actions before testing this 🫠 lol
Nathan
Nathan2mo ago
getting to this in a second, been wrapped up getting our cloud launched i have no issues on v1.2.23 with the hono-bun example
Nathan
Nathan2mo ago
scratch that, looks like it logs the full path including the base path
No description
Nathan
Nathan2mo ago
can you try v2.0.10 in case it's something with the new version that we already fixed? this would be my best guess don't forget to update disableSErver -> disableDefaultServer
nicolas-angelo
nicolas-angeloOP2mo ago
just upgraded server terminal: ^Clevel=WARN msg="websocket closed" code=1006 reason="" wasClean=false client console: http://localhost:8080/rivet/actors?namespace=default net::ERR_CONNECTION_REFUSED level=WARN msg="failed to reconnect" attempt=6 error="Error: HTTP request error: Request failed: TypeError: Failed to fetch" errno: 0, code: "UNABLE_TO_GET_ISSUER_CERT_LOCALLY" ok getting closer to debugging it seems to work fine when using the client in a node env - like your example but whenever i test in a browser, in my nextjs app, using the react package, i get the errors
Nathan
Nathan2mo ago
oh i might have ran across this before, ill test the browser in a bit
Nathan
Nathan2mo ago
GitHub
Deno upgradeWebSocket missing Sec-WebSocket-Protocol header in ...
What version of Hono are you using? 4.9.8 What runtime/platform is your app running on? (with version if possible) Deno 2.5.2 What steps can reproduce the bug? Create a WebSocket server using Deno&...
Nathan
Nathan2mo ago
we have patch for this in rivetkit that fixed deno, i’ll see about bun still on my radar, we've been wrapping up tmrw's launch i want to get bun and the bun docs solid next ...again
nicolas-angelo
nicolas-angeloOP2mo ago
No worries. Congrats on launching 2.0
Nathan
Nathan2mo ago
@nicolas-angelo how are you planning on running rivet in production? you might not need to mount the /rivet path in the first place i am going to get this fixed, though
nicolas-angelo
nicolas-angeloOP2mo ago
@Nathan thanks for getting back, i was actually going to follow up this. Would it be cool to discuss this privately?
Nathan
Nathan2mo ago
for sure

Did you find this page helpful?