Typed fetch with Sveltekit and Hono using RPC

subhendu singhsubhendu singh
4 min read

What is Hono?

Hono is a fast and simple web framework that works in edge environments like Cloudflare Workers, Vercel Edge, etc. Its API is similar to that of express js, so if you have worked with express in the past, you can be productive with Hono from day one. If you are writing APIs with Hono for the edge, many of the node APIs like “fs” will not be available in Hono since the edge runtimes don’t support them (not completely, at least).

What is RPC feature and how is it beneficial?

Typically, to make an API call from the frontend, you need to specify:

1. The base URL + the path of the handler function
2. The request parameters
3. The authorization header

These 3 things are specified in the API call according to the specification shared by the API provider. RPC feature handles exactly this. It allows sharing of the API specification between the server and the client, such that, while making an API call to the Hono server, the client is already aware of the remote function path, required and optional request params, and the headers required for a successful call.

How to use the RPC feature?

A simple API route in Hono looks like this:

import { Hono } from "hono";
const app = new Hono();
app.get("/", © => c.json({ message: "Hello World" }));
app.get("/hello/:name", © =>
 c.json({ message: `Hello ${c.req.param("name")}` })
);
export default app;

This will return a JSON response to the client but the client will have no specification about the path parameter that the router /hello/:name expects and what is the shape of the response it returns. For the client, to have this specification, we will have to make a few changes to this API route:

import { Hono } from "hono";
const app = new Hono();
const router = app
 .get("/", © => c.jsonT({ message: "Hello World" }))
 .get("/hello/:name", © =>
 c.jsonT({ message: `Hello ${c.req.param("name")}` })
 );
export default app;
export type AppType = typeof router;

Here instead of the .json response, we return the typed JSON response .jsonT and we also return the type of router.

On the client side, we can access these routes like this:

import { AppType } from '.'
import { hc } from 'hono/client'
const client = hc<AppType>('http://localhost:8787/')

Here hc is the function to create a typed client of the Hono server. We pass the AppType, the type of router returned from the Hono server as generics.

Note how AppType is imported here from the server. This is because both the server and the client are a part of one monorepo*. Without them being a part of the same mono repo, this is not possible.*

Now, we can call our API routes from the client like this

const res = await client.hello[':name'].$get({
 param: {
 name: 'hono',
 },
 query: {},
})

The client here is fully typed and will give intellisense about the parameters and response.

Separating routes in different files

In large backend apps, it is quite common to have multiple routes/controllers in separate files. For example, a file named book.ts may contain all the endpoints related to books, and a separate file named author.ts may contain all the endpoints related to the authors. In this case, to share the API specifications using RPC with the client, we can do this:

// book.ts
const book = new Hono()
export const bookRoute = book.get('/', © => {
 return c.jsonT({
 books: ['foo'],
 })
})

// author.ts
const author = new Hono()
export const authorRoute = author.get('/', © => {
 return c.jsonT({
 authors: ['bar'],
 })
})

// main.ts
const app = new Hono()
const appRoute = app.route('/book', bookRoute).route('/author', authorRoute)

This way, we can share the complete API specification with our frontend app and make sure the API endpoints are typed and the request and response are properly handled.

This was one of the many lessons I learned in the process of migrating my SaaS StoreBud from AWS to Cloudflare Workers. I will keep sharing as and when I learn something new. Also, I post tech-related stuff on Twitter.

0
Subscribe to my newsletter

Read articles from subhendu singh directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

subhendu singh
subhendu singh