H
Hono3w ago
Vessbon

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.
6 Replies
ambergristle
ambergristle3w ago
can you share your client code as well?
Vessbon
VessbonOP3w ago
Just using postman right now for testing example: POST to http://localhost:8787/trpc/quest.create application/json raw:
{
"title": "Slay the Dragon"
}
{
"title": "Slay the Dragon"
}
ambergristle
ambergristle3w ago
does the request have a body before it hits trpcServer?
Vessbon
VessbonOP3w ago
Sorry for the wait, Yeah 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"
}
ambergristle
ambergristle3w ago
weird. so all the routing is fine, and the data is there, but somehow it's not getting to the procedure handler i've got no idea. i don't use trpc though ig you could try making a call with app.request, just in case there's something funky going on w postman, but doesn't seem like that's it
Vessbon
VessbonOP3w ago
Yeah, super weird issue. I might just change my stack all together to avoid these headaches

Did you find this page helpful?