new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

Hono · all subjects

rpc client

38 notes, read out of this brain and free to use. Each one was extracted from a source and is re-checked against its exam.

RPC feature exports server API types to client

The RPC feature allows sharing of API specifications between server and client. Export the typeof your Hono app (commonly called AppType) or just the routes you want available to the client from your server code. By accepting AppType as a generic parameter, the Hono Client can infer both input types specified by the Validator and output types emitted by handlers returning c.json().

RPC types require strict TypeScript mode in monorepo

For RPC types to work properly in a monorepo, in both the Client's and Server's tsconfig.json files, set "strict": true in compilerOptions.

RPC client creation with hc function

On the client side, import hc and AppType. hc is a function to create a client. Pass AppType as generics and specify the server URL as an argument. Example: const client = hc<AppType>('http://localhost:8787/')

RPC client request calling convention

Call client.{path}.{method} and pass the data you wish to send to the server as an argument. The response res is compatible with the fetch Response API. Retrieve data from the server with res.json().

RPC client send cookies with every request

To make the client send cookies with every request, add { 'init': { 'credentials': 'include' } } to the options when creating the client. Example: const client = hc<AppType>('http://localhost:8787/', { init: { credentials: 'include' } })

RPC status code type inference

If you explicitly specify the status code, such as 200 or 404, in c.json(), it will be added as a type for passing to the client. The client can then get the data by the status code using res.status.

RPC InferResponseType for status-specific response types

Use InferResponseType to infer the response type from a client route. You can optionally pass a specific status code as the second parameter: InferResponseType<typeof client.posts.$get, 200> returns only the type for that status code.

RPC ApplyGlobalResponse for global error types

Hono RPC client does not automatically infer response types from global error handlers like app.onError() or global middleware. Use the ApplyGlobalResponse type helper to merge global error response types into all routes. Example: type AppWithErrors = ApplyGlobalResponse<typeof app, { 500: { json: { error: string } } }>

RPC ApplyGlobalResponse multiple status codes

You can define multiple global error status codes at once with ApplyGlobalResponse: type AppWithErrors = ApplyGlobalResponse<typeof app, { 401: { json: { error: string; message: string } }, 500: { json: { error: string; message: string } } }>

RPC avoid c.notFound() with client

Do not use c.notFound() for the Not Found response if you want to use a client. The data that the client gets from the server cannot be inferred correctly. Instead, use c.json() and specify the status code for the 404 response, or use module augmentation to extend the NotFoundResponse interface.

RPC module augmentation for typed notFound response

You can use module augmentation to extend the NotFoundResponse interface to make c.notFound() return a typed response. Declare module 'hono' with interface NotFoundResponse extending Response and TypedResponse<{ error: string }, 404, 'json'>.

RPC path parameters must be passed as strings

Both path parameters and query values must be passed as string when using the RPC client, even if the underlying value is of a different type. Specify the string you want to include in the path with param, and any query values with query.

RPC multiple path parameters syntax

Handle routes with multiple parameters by adding multiple [''] to specify params in path. Example: client.posts[':postId'][':authorId'].$get({ param: { postId: '123', authorId: '456' } })

RPC parameters with slashes using regex

The hc function does not URL-encode the values of param. To include slashes in parameters, use regular expressions on the server. Example server route: app.get('/posts/:id{.+}', ...). Note that basic path parameters without regular expressions do not match slashes. Encoding parameters using encodeURIComponent is the recommended approach.

RPC append headers to request

You can append headers to individual requests using the second parameter: const res = await client.search.$get({}, { headers: { 'X-Custom-Header': 'value' } }). To add a common header to all requests, specify it as an argument to the hc function: const client = hc<AppType>('/api', { headers: { Authorization: 'Bearer TOKEN' } })

RPC init option for RequestInit

You can pass the fetch RequestInit object to the request as the init option. A RequestInit object defined by init takes the highest priority and can be used to overwrite things set by other options like body, method, or headers. Example: { init: { signal: abortController.signal } }

RPC $url() method requires absolute URL

Use $url() to get a URL object for accessing the endpoint. You must pass an absolute URL for this to work; passing a relative URL like '/' will result in a TypeError. Passing a relative URL will throw: Uncaught TypeError: Failed to construct 'URL': Invalid URL

RPC $url() returns URL object for endpoint

Call $url() on a client route to get a URL object. Access the pathname property: client.api.posts.$url() returns URL with pathname '/api/posts'. For path parameters: client.api.posts[':id'].$url({ param: { id: '123' } }) returns pathname '/api/posts/123'

RPC typed URL for type-safe URL keys

Pass the base URL as the second type parameter to hc to get more precise URL types: const client = hc<typeof route, 'http://localhost:8787'>('http://localhost:8787/'). This returns a TypedURL with precise type information including protocol, host, and path, useful as a type-safe key for libraries like SWR.

RPC $path() returns path string instead of URL

$path() is similar to $url() but returns a path string instead of a URL object. Unlike $url(), it does not include the base URL origin, so it works regardless of the base URL passed to hc. Example: client.api.posts.$path() returns '/api/posts'

RPC $path() with query parameters

Pass query parameters to $path(): const path = client.api.posts.$path({ query: { page: '1', limit: '10' } }) returns '/api/posts?page=1&limit=10'

RPC file upload via form body

Upload files using a form body on the client: const res = await client.user.picture.$put({ form: { file: new File([fileToUpload], filename, { type: fileToUpload.type }) } }). On the server use zValidator with z.instanceof(File).

RPC custom fetch method

You can set a custom fetch method when creating the client. Example for Cloudflare Worker Service Bindings: const client = hc<CreateProfileType>('http://localhost', { fetch: c.env.AUTH.fetch.bind(c.env.AUTH) })

RPC custom query serializer with buildSearchParams

Customize how query parameters are serialized using the buildSearchParams option. This is useful for bracket notation for arrays or custom formats. Pass a function that takes query and returns URLSearchParams.

RPC buildSearchParams example for array bracket notation

Example custom buildSearchParams: buildSearchParams: (query) => { const searchParams = new URLSearchParams(); for (const [k, v] of Object.entries(query)) { if (v === undefined) continue; if (Array.isArray(v)) { v.forEach((item) => searchParams.append(`${k}[]`, item)) } else { searchParams.set(k, v) } } return searchParams }

RPC InferRequestType and InferResponseType helpers

Use InferRequestType and InferResponseType to know the type of object to be requested and the type of object to be returned. Example: type ReqType = InferRequestType<typeof $post>['form']; type ResType = InferResponseType<typeof $post>

RPC parseResponse helper for type-safe parsing

Use parseResponse() helper to easily parse a Response from hc with type-safety. It automatically parses the response body based on Content-Type and throws an error if response is not ok. Example: const result = await parseResponse(client.hello.$get()).catch((e: DetailedError) => { console.error(e) })

RPC with React SWR library example

Example using SWR with Hono RPC client: import useSWR from 'swr'; const App = () => { const client = hc<AppType>('/api'); const $get = client.hello.$get; const fetcher = (arg: InferRequestType<typeof $get>) => async () => { const res = await $get(arg); return await res.json() }; const { data, error, isLoading } = useSWR('api-hello', fetcher({ query: { name: 'SWR' } })); return <h1>{data?.message}</h1> }

RPC with larger applications - split into multiple files

For larger applications, split your app into multiple files with separate handlers. Chain the handlers so that types are always inferred. At the top level, chain .route() calls and export the type of the result. Example: const routes = app.route('/authors', authors).route('/books', books); export type AppType = typeof routes

RPC IDE performance issue with many routes

When using RPC, the more routes you have, the slower your IDE becomes. This is because massive amounts of type instantiations are executed to infer the type of your app. Each route results in multiple type arguments being instantiated, which takes significant time for tsserver.

RPC Hono version mismatch causes type issues

If your backend is separated from the frontend in a different directory, ensure that Hono versions match. Using one Hono version on the backend and another on the frontend causes issues such as 'Type instantiation is excessively deep and possibly infinite'.

RPC TypeScript project references for monorepo

If your backend and frontend are separate, use TypeScript project references. TypeScript project references allow one TypeScript codebase to access and use code from another TypeScript codebase. This is necessary to access code like AppType from the backend on the frontend.

RPC compile code before using for IDE performance

tsc can do heavy type instantiation tasks at compile time instead of during IDE usage. Compile your client including the server app and export a pre-calculated Client type. This avoids tsserver needing to instantiate all type arguments every time, making IDE performance significantly faster.

RPC hcWithType trick for compiled type performance

Create a type-safe client wrapper that pre-calculates types at compile time: export type Client = ReturnType<typeof hc<typeof app>>; export const hcWithType = (...args: Parameters<typeof hc>): Client => hc<typeof app>(...args). Then use hcWithType instead of hc.

RPC specify type arguments manually for performance

You can specify type arguments manually to avoid type instantiation, though this is cumbersome. Example: const app = new Hono().get<'foo/:id'>('foo/:id', (c) => c.json({ ok: true }, 200)). This helps with IDE performance but requires effort for many routes.

RPC split app into multiple files for IDE performance

As described in using RPC with larger applications, split your app into multiple apps and create a client for each. This way tsserver does not need to instantiate types for all routes at once, improving IDE performance.

RPC client access request parameters and query strings

On the server side, use c.req.valid() to access validated request parameters. The RPC client automatically passes parameters through the validators you define with zValidator. For path parameters use param: { id: '123' }, for query strings use query: { page: '1' }, for JSON body use json: { field: 'value' }, and for form data use form: { field: 'value' }.

RPC server validator setup with Zod

On the server side, write a validator and create a variable route. Example uses Zod Validator: const route = app.post('/posts', zValidator('form', z.object({ title: z.string(), body: z.string() })), (c) => { return c.json({ ok: true, message: 'Created!' }, 201) }). Then export type AppType = typeof route

Give your agent this brain