React server-side rendering support for Fastify with Next.js framework. This library is for letting an existing Fastify server utilize NextJS, not for replacing NextJS' internal webserver with Fastify.
npm i @fastify/nextjs next react react-dom
Since Next.js needs some time to be ready on the first launch, you must declare your routes inside the after callback, after you registered the plugin.
The plugin will expose the next API in Fastify that will handle the rendering for you.
constfastify=require('fastify')()fastify.register(require('@fastify/nextjs')).after(()=>{fastify.next('/hello')})fastify.listen({port: 3000},err=>{if(err)throwerrconsole.log('Server listening on http://localhost:3000')})All your server rendered pages must be saved in the folder pages, as you can see in the Next.js documentation.
// /pages/hello.jsexportdefault()=><div>hello world</div>If you need to pass custom options to next just pass them to register as second parameter.
fastify.register(require('@fastify/nextjs'),{dev: true})If you need to handle the render part yourself, just pass a callback to next:
fastify.next('/hello',(app,req,reply)=>{// your code// `app` is the Next instanceapp.render(req.raw,reply.raw,'/hello',req.query,{})})If you need to render with Next.js from within a custom handler, use reply.nextRender
app.setErrorHandler((err,req,reply)=>{returnreply.nextRender('/a')})If you need to render a Next.js error page, use reply.nextRenderError
app.setErrorHandler((err,req,reply)=>{returnreply.status(err.statusCode||500).nextRenderError(err)})If you need to handle HEAD routes, you can define the HTTP method:
fastify.next('/api/*',{method: 'GET'});fastify.next('/api/*',{method: 'HEAD'});By default plugin handle route ${basePath}/_next/* and forward to Next.js.
If you have custom preprocessing for _next/* requests, you can prevent this this handling with noServeAssets: true property for plugin options:
fastify.register(require('@fastify/nextjs'),{noServeAssets: true}).after(()=>{fastify.next(`${process.env.BASE_PATH||''}/_next/*`,(app,req,reply)=>{// your codeapp.getRequestHandler()(req.raw,reply.raw).then(()=>{reply.hijack()})})})The plugin includes under-pressure, which can be configured by providing an underPressure property to the plugin options.
Using under-pressure allows implementing a circuit breaker that returns an error when the health metrics are not respected.
Because React server side rendering is a blocking operation for the Node.js server, returning an error to the client allows signalling that the server is under too much load.
The available options are the same as those accepted by under-pressure.
For example:
fastify.register(require('@fastify/nextjs'),{underPressure: {exposeStatusRoute: true}})underPressure-bool|object- (default) when false,
under-pressureis not registered - when true,
under-pressureis registered with default options - when it is an object,
under-pressureis registered with the provided options
- (default) when false,
If you want to share custom objects (for example other fastify plugin instances - e.g. @fastify/redis) across the server/client with each page request, you can use the onRequest hook to add it to the request object.
Here is an example on how to do it:
constFastify=require('fastify')constFastifyRedis=require('@fastify/redis')constFastifyNextJS=require('@fastify/nextjs')constfastify=Fastify()fastify.register(FastifyRedis,{host: '127.0.0.1'})fastify.register(FastifyNextJS)fastify.register(function(instance){// for performance reasons we do not want it to run on every request// only the nextjs one should runinstance.addHook('onRequest',function(request,reply,done){// define a custom property on the requestrequest.raw.customProperty={hello: "world"}// OR make the instance of @fastify/redis available in the requestrequest.raw.redisInstance=instance.redisdone()})instance.next('/',function(app,request,reply){// your custom property containing the object will be available here// request.raw.customProperty// OR the redis instance// request.raw.redisInstanceapp.render(request.raw,reply.raw,'/hello',request.query,{})})},{prefix: '/hello'})In the example above we made the customProperty and redisInstance accessible in every request that is made to the server. On the client side it can be accessed like in this example:
constCustomPropPage=({ cp, ri })=><div>custom property value: {cp} | redis instance: {ri}</div>;exportdefaultCustomPropPage;exportconstgetServerSideProps=asyncfunction(ctx){return{props: {cp: ctx.req.customProperty,ri: ctx.req.redisInstance,}};};The default timeout for plugins in Fastify is 10000ms, which can be a problem for huge Next.js Projects where the initial build time is higher than that. Usually, you will get an error like this:
Error: ERR_AVVIO_PLUGIN_TIMEOUT: plugin did not start in time: /app/node_modules/@fastify/nextjs/index.js. You may have forgotten to call 'done' function or to resolve a Promise
The workaround or fix is to increase the plugin timeout:
constisDev=process.env.NODE_ENV!=='production';constfastify=Fastify({pluginTimeout: isDev ? 120_000 : undefined});CI currently runs npm@6 so when upgrading packages, please use this version.
This project is kindly sponsored by:
Past sponsors:
- LetzDoIt
Licensed under MIT.