Closed aulneau closed 1 year ago
Hi @aulneau !
Wow, this is great!!
I have just used tRPC for the first time, and it is interesting. Hono's advantage is that Hono can run on both Node.js and Cloudflare Workers or Bun and Deno.
It is also good to make it like Middleware.
export const trpcServer = <TRouter extends AnyRouter>(opts: { router: TRouter, endpoint: string }): MiddlewareHandler => {
return async (c, next) => {
c.res = await honoRequestHandler({
router: opts.router,
ctx:c,
endpoint: opts.endpoint || 'trpc/'
})
await next()
}
}
In script:
import { trpcServer } from './trpc-server';
//...
app.use('/trpc/*', trpcServer({ router: helloRouter, endpoint: 'trpc/' }))
// Can we set `endpoint` automatically?
I think it would be good to have the code placed in the repository of tRPC itself.
yeah! trpc is very popular and very good! I think it'd be awesome if hono supported it (or vis versa)
I was thinking middleware would be better, I wasn't sure what style of API would work best. I'd love to defer to you on that :) my implementation was a quick and dirty one.
I think it would be good to have the code placed in the repository of tRPC itself.
agree -- but short-term, I wonder if it could also live in the hono repo/org? @honojs/trpc-adapter
?
I wonder if it could also live in the hono repo/org?
@honojs/trpc-adapter
?
Ah, yes. It is also good to make it @honojs/trpc-adapter
. Both are good.
Since tRPC is so popular, I think it would be better to have it placed in that tRPC repository because it would be used by more people :)
For example, we could create @honojs/trpc-adapter
and put examples of Hono in tRPC examples.
By the way, this is a different issue, but I am thinking of changing the way to manage Third-party Middleware. Specifically, I'm thinking of making it monorepo. In that case, it may take some time to create @honojs/trpc-adapter
.
agree -- i think in both places makes sense. @yusukebe do you have any modifications to the API (eg: making it middleware)?
What I would like to do is just add the following code and make it middleware.
https://github.com/honojs/hono/issues/582#issuecomment-1267006380
@aulneau
Do you try to make @honojs/trpc-adapter
? This means you can develop the middleware in the honojs/org repository such as github.com/honojs/trcp-adapter
, and you will release it to the npm registry. If you want to do it, I will prepare the repository for you. Or I can do it. Or it's OK the different way you want!
@yusukebe yes I'd be happy to, feel free to create it and I can create a PR (as collaborator or fork)
Alright. But please wait a minute. I'm thinking about how to manage third-party middleware again. We may go with it as is or use monorepo.
monorepo +1
Instead (or rather in addition too) of integrating with TRPC, might it not be better to just create a Proxy
client that is native for Hono? I think that will be much simpler and more reliable (not break when TRPC changes things.)
I spent some time trying to extract client interface type that can be used to build the fetch API request inside a Proxy
handler using Typescipt autocomplete, ex:
I think for someone with more experience in the Hono code it won't be hard, my basic implementation is only ~40 lines but it probably can be much better.
edit: final usage if there was a client Proxy implementation:
type MyAPI = typeof routes
----- Only import type into frontend code -----
const client = honoClient<MyApi>('http://my-url.com/api')
const biz: {bid: string} = await client.run()['/api/business/:id'].post({id: '123'})
------ or maybe better with something like this for path arguments? ----
const biz: {bid: string} = await client.run().business().id('123').post()
I've uploaded the change I have so far. https://github.com/cleaton/hono/tree/hono-client Essentially just need a way for typescript to keep all the paths, arguments(optional or not) (path, body, query and maybe headers) and return type (before converting to json/response).
Currently I do this by creating a factory class that unions previous paths every time adding a new one. The existing HandlerInterface on Hono class might be able to do this as well since it already returns this (instead of this, return a new instance with updated generic types). Or just have a completely new interface for this use-case.
@cleaton !!
Wow cool!! I've tried it, it's like a tRPC but just using only Hono!
This is the usage, right?
This is great; it is simpler to write than the code for using tRPC. And when combined with the Validator middleware, it becomes even more amazing. The tRPC adapter works well for users who are used to using @trpc/client
, but for those who are not familiar with it, this approach may be better.
We shouldn't try to do a lot of things in Hono itself, but I like it.
@aulneau cc: @OnurGvnc @usualoma What do you think about it?
@cleaton This is great!
And when combined with the Validator middleware, it becomes even more amazing. The tRPC adapter works well for users who are used to using @trpc/client , but for those who are not familiar with it, this approach may be better.
@yusukebe this approach makes a lot of sense
from my point of view; I'm currently doing a lot of fetch operations between cloudflare workers, so this could be a great option for my use case.
And how about you? : @ThatOneBro
@yusukebe yes exactly like that :+1: I think trpc and hono to a large degree is the same thing, an endpoint router definition except that trpc has automatic client creation as well. I've actually been working on an TRPC alternative myself as I found ergonomics not to my liking when working on cloudflare/other edge frameworks. It's can be surprisingly simple to create something like TRPC, just requires some typescript vodoo (I manage to do it as a complete beginner in typescript)
I came across this ticket because I was looking at how to do Google JWT auth middleware and was looking at Hono implementation. It made me think that maybe hono could just have a client instead.
Here's my current implementation: https://github.com/cleaton/type-knit example of a client proxy that recursively builds the fetch request https://github.com/cleaton/type-knit/blob/main/src/client.ts#L74
I also have a stream implementation, which probably should just use EventSource
instead to be even simpler
I think this approach is great too, but I think trpc should also be supported, as they have a large ecosystem of tooling. For folks who aren't using trpc already, this approach could get them very close to a similar experience.
As reference, this approach is also very similar to “itty-durable”, wrapping durable object fetch API in a proxy https://github.com/kwhitley/itty-durable
@aulneau
but I think trpc should also be supported, as they have a large ecosystem of tooling. For folks who aren't using trpc already, this approach could get them very close to a similar experience.
Yes! I agree. tRPC adapter shold be implemented.
@cleaton
Do yow know "Validator Middleware"?
https://honojs.dev/docs/builtin-middleware/validator/
It sounds complicated, but it would be great if we could get the validator and your idea together. Both input and output values can be type checked from the client.
// Server
app.post(
'/posts',
validator((v) => ({
post: v.object('post', (v) => ({
id: v.json('id').asNumber().isRequired(),
title: v.json('title').isRequired(),
})),
})),
(c) => {
const res = c.req.valid()
const newPost = createPost(res.post)
return c.json(newPost)
}
)
const routes = app.build()
export type AppRouter = typeof routes
// Client
class Client<T> {
baseURL: string
constructor(baseURL: string) {
this.baseURL = baseURL
}
run: () => T
}
const client = new Client<AppRouter>('https://example.com/api')
const tmp = client.run()
const newPost = tmp['/posts'].post({ post: { id: 123, title: 'Hello!' } })
console.log(`${newPost.id} is created!`)
@yusukebe yes, I did try a bit but nothing functioning yet. My current implementation has a type conflict when adding middlewares.
I think in theory, once the types are matching correctly we can just use D extends ValidatedData
generic type for the post argument. It's a little bit complicated because Hono API treats everything as generic ...handlers: Handler<>
even if it's a middleware or other.
TRPC for example uses something like builder().input(validationF).handler(args => )
and in my implementation builder().(Validator<T>, handler: (args: T) => ...)
with Validator = { validate: <T>(args: unknown): T }
Oh this looks great! I'd love to see something like this built into Hono. Looks like it could be a be a great addition to the Hono toolset!
Also @yusukebe, regarding the monorepo idea -- I've just been learning about TurboRepo and other monorepo solutions (such as publishing/ version management through changesets), it may be worth it to go over these ideas sometime if you'd like 😄 I think a monorepo would be great to keep us in sync with all related packages
I got it working but currently it's ugly. The problems:
Handler
(returning Response), it does not match against existing middelware functions.ValidatedData
type it looks like it need to come before the main handler, or the type inference will complain they are not of the same type.SchemaToProp
with a buch of nested VString, VNumber etc. so looks bit ugly in the vscode autocomplete.On other note, regarding multiple packages in a monorepo. Is it really needed with modern bundlers that supports 'tree shaking' for ES modules? That way the repo can be simpler (no mono repo tools and multiple packages) but at same time can keep all functionality in one place.
Another library you might look for inspiration is Zodios, like Hono validator it can receive type definitions for headers, query, body, path params, etc and autocomplete on the client-side. Unlike tRPC, it's just REST and can be inspected with tools like Postman, Insomnia, etc
On other note, regarding multiple packages in a monorepo. Is it really needed with modern bundlers that supports 'tree shaking' for ES modules?
I think it's not needed, so I think we can go with only yarn workspaces and changesets.
Hi everyone!!
Very interesting discussion! I created the following Issues about the feature suggested by @cleaton and the topic of monorepo. Let's talk about them there.
Hi @aulneau !
I've made the monorepo for third-party middleware. This was just created, so let's make it better together.
https://github.com/honojs/middleware
Then, there is a skeleton project for tRPC adapter middleware:
https://github.com/honojs/middleware/tree/main/packages/trpc-adapter
Do you write the code for the tRPC Adapter? If so, please clone and create a PR. We may set up CI and releases, etc., so let's do it together. If you don't write the code, I can write it instead. Let's go!
Hi there! I've created and released "tRPC Server Middleware". Check it!
https://www.npmjs.com/package/@hono/trpc-server https://github.com/honojs/middleware/tree/main/packages/trpc-server
Hi there! I've created and released "tRPC Server Middleware". Check it!
https://www.npmjs.com/package/@hono/trpc-server https://github.com/honojs/middleware/tree/main/packages/trpc-server
Just tried it; works great for me! Thanks :)
I'll close this issue.
Is this compatible with trpc v11? (https://trpc.io/docs/migrate-from-v10-to-v11)
I'm getting a "unmet peer @trpc/server@^10.10.0".
But besides that, seems to work fine. So maybe update that peer dep?
Hi @vittis
But besides that, seems to work fine. So maybe update that peer dep?
Yes maybe. But I don't have enough time to check the behavior to use the new tRPC. If you will do it and it works, please create the PR to update it!
hi again :)
so in exploring trpc, they have some adapters, I took a stab at modifying their fetch adapter to work with hono, but I'd love some feedback on if this looks okay:
Usage: