H
Hono4d ago
Mattèo

Can't get the RPC typing

Hey ! Hope you're doing well, I have an issue with the RPC types, how can I manage to get the client types ?
// hc.ts
import router from "./root";
import { hc } from "hono/client";

const client = hc<typeof router>("");

export type Client = typeof client;

export const hcWithType = (...args: Parameters<typeof hc>): Client =>
hc<typeof router>(...args);

const test = hcWithType("http://localhost:3000/api");
// ^? unknown
// hc.ts
import router from "./root";
import { hc } from "hono/client";

const client = hc<typeof router>("");

export type Client = typeof client;

export const hcWithType = (...args: Parameters<typeof hc>): Client =>
hc<typeof router>(...args);

const test = hcWithType("http://localhost:3000/api");
// ^? unknown
// root.ts
import { Hono } from "hono";
import userRouter from "./services/user";
import todoRouter from "./services/todo";
import { cors } from "hono/cors";

const app = new Hono().basePath("/api");

app.use(
"*",
cors({
origin:
process.env.NODE_ENV === "development" ? "*" : "https://app.example.com",
}),
);

const router = app.route("/users", userRouter).route("/todos", todoRouter);

export default router;
// root.ts
import { Hono } from "hono";
import userRouter from "./services/user";
import todoRouter from "./services/todo";
import { cors } from "hono/cors";

const app = new Hono().basePath("/api");

app.use(
"*",
cors({
origin:
process.env.NODE_ENV === "development" ? "*" : "https://app.example.com",
}),
);

const router = app.route("/users", userRouter).route("/todos", todoRouter);

export default router;
6 Replies
ambergristle
ambergristle4d ago
couple things if you're using rpc, it's recommended that you chain your handlers + middleware otherwise the inference breaks also, your hcWithType implementation isn't generic, which will probably cause you some issues i was wrong. it could be simplified though to improve typing performance
// index.ts
const app = new Hono()
.basePath("/api")
.use('*', cors({ /** */ })
.route('/users', usersRouter)
.route('/todos', todosRouter)

export type AppType = typeof app;
export default app
// hc.ts
import { hc, type ClientRequestOptions } from 'hono/client';

export const hcWithType(baseUrl: string, options?: ClientRequestOptions) => {
return hc<AppType>(baseUrl, options);
}
// index.ts
const app = new Hono()
.basePath("/api")
.use('*', cors({ /** */ })
.route('/users', usersRouter)
.route('/todos', todosRouter)

export type AppType = typeof app;
export default app
// hc.ts
import { hc, type ClientRequestOptions } from 'hono/client';

export const hcWithType(baseUrl: string, options?: ClientRequestOptions) => {
return hc<AppType>(baseUrl, options);
}
each time you do typeof, the TS server in vs code has to evaluate the type it's best to do it once + share
Mattèo
MattèoOP4d ago
Thanks for your feedback ! I've updated my code to looks like this now :
import { Hono } from "hono"
import userRoute from "./services/user"
import { cors } from "hono/cors"

const app = new Hono()

app.use("*", cors())

const routes = app.route("/user", userRoute)

///////

export default app

export type AppType = typeof routes
import { Hono } from "hono"
import userRoute from "./services/user"
import { cors } from "hono/cors"

const app = new Hono()

app.use("*", cors())

const routes = app.route("/user", userRoute)

///////

export default app

export type AppType = typeof routes
And its working !!🚀
import { AppType } from "./app"
import { hc } from "hono/client"

// assign the client to a variable to calculate the type when compiling
const client = hc<AppType>("")
export type Client = typeof client

export const hcWithType = (...args: Parameters<typeof hc>): Client => hc<AppType>(...args)

const test = hcWithType("http://localhost:3000")

test.user.$get()
import { AppType } from "./app"
import { hc } from "hono/client"

// assign the client to a variable to calculate the type when compiling
const client = hc<AppType>("")
export type Client = typeof client

export const hcWithType = (...args: Parameters<typeof hc>): Client => hc<AppType>(...args)

const test = hcWithType("http://localhost:3000")

test.user.$get()
I understand my issue, but that's a bit strange behavior to work with... Thanks a lot for your time !
ambergristle
ambergristle4d ago
let's gooooooo happy to help! using Parameters<typeof hc> is kind of an expensive alternative to just importing the option type from hono/client. what motivates that design, if you don't mind me asking? as far as "strange behavior", do you mean the chaining? it makes sense if you take a look at the typing. tl;dr - unlike something like trpc, which creates types from the handlers you instantiate, hono rpc takes the merged type output of your app and your app instance is only aware of what you've chained to it that's probably the biggest dealbreaker w hono rpc: it doesn't work if you really want a direct code link between the client + the handler this is also why the cmd+click isn't working
Mattèo
MattèoOP3d ago
No problem, I just pasted what's in the docs https://hono.dev/docs/guides/rpc#compile-your-code-before-using-it-recommended Do you have a better option ?
RPC - Hono
Web framework built on Web Standards for Cloudflare Workers, Fastly Compute, Deno, Bun, Vercel, Node.js, and others. Fast, but not only fast.
Mattèo
MattèoOP3d ago
I see Thanks for your explanation, chaining was my issue
ambergristle
ambergristle3d ago
interesting. seems kind of extra. i would do what i shared above:
import { hc, type ClientRequestOptions } from 'hono/client';

export const hcWithType(baseUrl: string, options?: ClientRequestOptions) => {
return hc<AppType>(baseUrl, options);
}
import { hc, type ClientRequestOptions } from 'hono/client';

export const hcWithType(baseUrl: string, options?: ClientRequestOptions) => {
return hc<AppType>(baseUrl, options);
}

Did you find this page helpful?