Vessbon
Vessbon3w ago

Hono tRPC Server body issue

I have been trying to get Cloudflare Workers, Hono and tRPC working for tens of hours now. I am using the @hono/trpc-server package which gives me middleware for trpc to run with hono. They even have an example with cloudflare workers and a d1 database, so I know it is possible to do what I am trying to do. The problem I am having is that tRPC is not reading the body properly, or at all. Take this function as an example:
create: publicProcedure
.input(z.object({ title: z.string() }))
.mutation(async ({ ctx, input }) => {
console.error(input);
const quest = await ctx.db
.insertInto("quest")
.values(input)
.returningAll()
.executeTakeFirst();
return quest;
}),
create: publicProcedure
.input(z.object({ title: z.string() }))
.mutation(async ({ ctx, input }) => {
console.error(input);
const quest = await ctx.db
.insertInto("quest")
.values(input)
.returningAll()
.executeTakeFirst();
return quest;
}),
It never even gets to the mutation part at all because validation fails. Using errorFormatting with tRPC shows me that input is undefined when getting passed by postman, which is weird because I am definitely sending a title. It all worked before when I hadn't moved to Cloudflare Workers and before I was using the hono trpc-server package. This is how I serve the app:
const app = new Hono<{ Bindings: Bindings }>();

app.use(
"/trpc/*",
trpcServer({
router: appRouter,
createContext: (_, c) => createContext(c),
}),
);

app.use(
"/*",
cors({
origin: "http://localhost:5173",
credentials: true,
}),
);

export default app;
const app = new Hono<{ Bindings: Bindings }>();

app.use(
"/trpc/*",
trpcServer({
router: appRouter,
createContext: (_, c) => createContext(c),
}),
);

app.use(
"/*",
cors({
origin: "http://localhost:5173",
credentials: true,
}),
);

export default app;
Here is some more context so you can understand my code a little better. context.ts
import { createDb } from "./db";
import type { Bindings } from "./types";
import type { Context as HonoContext } from "hono";

export const createContext = (c: HonoContext<{ Bindings: Bindings }>) => ({
db: createDb(c.env.questboard),
});

export type Context = Awaited<ReturnType<typeof createContext>>;
import { createDb } from "./db";
import type { Bindings } from "./types";
import type { Context as HonoContext } from "hono";

export const createContext = (c: HonoContext<{ Bindings: Bindings }>) => ({
db: createDb(c.env.questboard),
});

export type Context = Awaited<ReturnType<typeof createContext>>;
How can I get tRPC to start recognizing the input? The context and database queries work as intended, it's just the input that is not working.
4 Replies
Nick
Nick3w ago
It looks reasonable, I would put some console logs around to confirm if hono is even receiving the body and such I’ve seen hono and tRPC integrated in the wild for CF so it definitely can work
Vessbon
VessbonOP3w ago
I tried making this little debugger to see what was getting through
app.use("/trpc/*", async (c, next) => {
console.log("Method:", c.req.method);
console.log("URL:", c.req.url);
console.log("Headers:", Object.fromEntries(c.req.raw.headers));

// Try to read the body
const clonedReq = c.req.raw.clone();
try {
const body = await clonedReq.text();
console.log("Raw body:", body);
} catch (e) {
console.log("Could not read body:", e);
}

return next();
});
app.use("/trpc/*", async (c, next) => {
console.log("Method:", c.req.method);
console.log("URL:", c.req.url);
console.log("Headers:", Object.fromEntries(c.req.raw.headers));

// Try to read the body
const clonedReq = c.req.raw.clone();
try {
const body = await clonedReq.text();
console.log("Raw body:", body);
} catch (e) {
console.log("Could not read body:", e);
}

return next();
});
It returned:
Method: POST
URL: http://localhost:8787/trpc/quest.create
Headers: {
accept: '*/*',
'accept-encoding': 'br, gzip',
'content-length': '36',
'content-type': 'application/json',
host: 'localhost:8787',
'postman-token': '5e10aea3-3ed3-4abd-8e2c-6158db447a21',
'user-agent': 'PostmanRuntime/7.49.1'
}
Raw body: {
"title": "Slay the Dragon"
}
Method: POST
URL: http://localhost:8787/trpc/quest.create
Headers: {
accept: '*/*',
'accept-encoding': 'br, gzip',
'content-length': '36',
'content-type': 'application/json',
host: 'localhost:8787',
'postman-token': '5e10aea3-3ed3-4abd-8e2c-6158db447a21',
'user-agent': 'PostmanRuntime/7.49.1'
}
Raw body: {
"title": "Slay the Dragon"
}
The body is there, but the tRPC procedure does not pick it up? I'm not sure The complicated setup is making me question my stack but I don't know what would have even been simpler
Nick
Nick3w ago
GitHub
iterate/apps/os/backend/worker.ts at main · iterate/iterate
The most hackable AI agent. Contribute to iterate/iterate development by creating an account on GitHub.
Nick
Nick3w ago
Looks like they just use our fetch adapter instead of the hono package which won’t have any real downsides I believe, possibly there’s an issue or version mismatch with that package you’re using

Did you find this page helpful?