Context method to send request to self
Sometimes in a view route, I want to call an another route handler to get data. It would be awesome if Hono has this built in.
Runtimes like Cloudflare workers do not allow workers to "fetch" themselves over the network so this would be a nice work around that would allow hono to skip the network entirely and just call itself.
Proposed API
const app = new Hono():
app.get("/api/data", async c => {
// get some data
return c.json(data);
});
app.get("/view", async c => {
const resp = await c.fetch("/api/data", c.req.raw); // follows `Fetch` API
const data = await resp.json();
return c.render(<SomeComponent data={data} />);
});
I'd be happy to send a PR if maintainers are open to this change.
Originally posted by @nicksrandall in https://github.com/honojs/hono/discussions/1675
Hi, @nicksrandall
I think we can do it with this code:
app.get('/api', (c) => {
return c.json({ foo: 'bar' })
})
app.get('/', async (c) => {
const res = await app.request('/api')
return c.json(await res.json())
})
Thanks for responding @yusukebe
As I understand it, that code does not keep track of the execution context or environment variables so it doesn't work for my use-case.
@yusukebe to be more specific, the code you suggest does work for very simple use-cases but it doesn't not work if the API route is using ENV variables or execution context (in a cloudflare worker). That is why I think it makes sense for a new method to live on the "context" because we want to execute the handler with the current context. While I think c.fetch(...) name makes the most sense to me, we could just as easily name the method c.request(...) if you felt like that was more consistent. Thoughts?
If we made that change, your example would look something like this:
app.get('/api', (c) => {
return c.json({ foo: 'bar' })
})
app.get('/', async (c) => {
const res = await c.request('/api')
return c.json(await res.json())
})
Again thanks for your help with this!
I'm not run it yet, but I think this would work with ENV:
app.get('/', (c) => {
app.request('/api', c.req.raw, c.env)
return c.json(0)
})
@yusukebe Your suggestion does pass provides the env variables but it still doesn't pass the execution context.
Hi.
You can also pass the execution context like the following:
app.request('/api', c.req.raw, c.env, c.executionCtx)
That works, however, this error is thrown in the console:
Your worker created multiple branches of a single stream (for instance, by calling response.clone()orrequest.clone()) but did not read the body of both branches. This is wasteful, as it forces the system to buffer the entire stream of data in memory, rather than streaming it through. This may cause your worker to be unexpectedly terminated for going over the memory limit. If you only meant to copy the request or response headers and metadata (e.g. in order to be able to modify them), use the appropriate constructors instead (for instance, new Response(response.body, response), new Request(request), etc).
@Zerebokep
That warning is annoying, but you can ignore it.