vike
vike copied to clipboard
Allow supplying a custom `pageContextInit` when using the Vite CLI
Description
I think it's overkill to require users to roll their own entire Express server just to do small stuff like pass a custom fetch implementation. I would love to see a new option on the VPS plugin config like this:
import fetch from 'node-fetch'
ssr({
// Other options, like `prerender: true`...
pageContextInit(req) {
const userAgent = req.headers['user-agent']
const pageContextInit = { urlOriginal: req.originalUrl || req.url, userAgent, fetch }
return pageContextInit
}
})
It takes the Vite dev server req as it's only parameter and must return an object to be used as pageContextInit for renderPage during development when using the Vite CLI.
Hopefully this would be pretty easy to add. I'd like to use this for the project I'm building on Cloudflare Workers. In my case, I'm actually creating my own extended fetch implementation that additionally allows you to call it with a string like /fn/whatever without an origin, in which case my worker just "calls itself" internally to save a round-trip to the internet. I don't want to clutter my project with an entirely custom server, though, and I want to keep my dependency list as lean as possible, since I intend to share it as a template for other people to use and I want it to be super clean.
I agree and it's on the radar.
The plan is to be able to use vite-plugin-ssr with HatTip in a straightforward and easy way.
I actually found a fairly clean approach to achieve this by dipping my toes into custom Vite plugins.
For anyone else stumbling across this issue trying to do the same thing—all you need to do is make a minimal Vite plugin like this and put it right before vite-plugin-ssr in your vite.config.js plugin list:
{
name: "call-it-whatever-you-want",
configureServer(server) {
return () => {
server.middlewares.use(async (request, response, next) => {
if (response.headersSent) return next();
if (!(request.originalUrl || request.url)) return next();
const initialPageContext = {
urlOriginal: request.originalUrl || request.url,
userAgent: request.headers["user-agent"],
// Add whatever additional data you want.
test: "WOWZERS"
};
const pageContext = await renderPage(initialPageContext);
if (!pageContext.httpResponse) return next();
response.setHeader("Content-Type", pageContext.httpResponse.contentType);
response.writeHead(pageContext.httpResponse.statusCode);
pageContext.httpResponse.pipe(response);
});
};
}
}
- This is very similar to how you integrate
vite-plugin-ssrinto any arbitrary deployment platform. - It just mimics the code in the original
vite-plugin-ssrmiddleware you can see here.
The only thing that is weird about this is that if if (!pageContext.httpResponse) return next(); is hit (which as far I know should never happen since vite-plugin-ssr falls back to an error page) then the original middleware will run. But even if it does, it should be working with the same routes, so I'd expect it to pass through via next() in exactly the same way.
I just tried this solution and so far I'm not seeing any issues with it. I'll definitely follow-up on this if I do.
Wouldn't it be better to expose the request to the server renderer(s) instead of defining PageContextInit in the vite config?
I fear that defining custom PageContext in the Vite config leads to additional complexity to understand how vite-plugin-ssr works/how an implementation of VPS works. Defining custom PageContext in the _default renderers is straightforward. Whether a PageContextInit is defined in the vite config or the PageContext is supplemented in a _renderer should make no difference, right?
I agree. One goal of the new VPS design is to move all options defined in vite.config.js to _define.js.
A new hook _define.js#onRequest would do the trick. (Or onRequestVite/onRequestHattip for better TS support.)
Closing for lack of interest. (I actually do think that some hook is missing here. But I'm waiting for enough users to complain and/or a strong use case for it.)
I just hit this problem.
I need to access some headers ('accept-language', 'sec-ch-*') and cookies in +onRenderHtml to decide what initial view to render for the client.
It works well with a custom express server, but I think it would require a full replacement of the Vike middleware entry in vite.config in order to work.
A custom pageContextInit with access to request would solve this elegantly.
@pdanpdan I ain't sure I understand your point. If you access headers then you, de facto, are using a server. So AFAICT you can simply access the headers at your Vike middleware as described in the docs. Feel free to create a new GitHub discussion to elaborate.
A custom
pageContextInitwith access to request would solve this elegantly.
You can actually already achieve this setting pageContextInit.headers at your Vike middleware, while using one global onBeforeRender() hook and multiple per-page data() hooks for data fetching.