Here at FINN small jobs, we’re huge fans of Vue.js. So, when we started reading about Nuxt.js, we got really excited, and quickly added it to our stack. In order to keep our React and JSP pages alive, however, we had to find a way to make it proxy to those pages when they’re requested. So, we tore Nuxt down, took away its 404 rendering, and injected our own logic to proxy when there’s nothing to render. Here’s how you can too!
Nuxt is a framework built on top of Vue that handles both the client-side and server-side of things. It reduces boilerplate, has server-side rendering out of the box, and lots of other great things. It helps you to get started using Vue without having to worry too much about config, and has best practices included.
Well, in our case it stemmed from wanting to proxy to our old app when requests come through that our Nuxt app doesn’t have a view for. This achieves a few things
Before we can talk about how to hack it, we need to understand how Nuxt handles requests and errors. Under the hood, Nuxt acts mostly like a normal Connect style Node.js server; all the logic is built on a set of middleware functions, including the routing of requests.
In Nuxt the way you add a middleware is by creating a middleware function, then wiring it up in your nuxt.config.js
file
// nuxt.config.js
const pkg = require('./package')
module.exports = {
...
serverMiddleware: [
'~serverMiddleware/override404.js'
],
...
}
// serverMiddleware/Override404.js
export default (req, res, next) => {
console.log("Hi Medium! This log will print for all requests")
}
Note, these middleware are Connect style, not Express style. This means that you don’t have access to all the convenience methods from Express on req and res
So far so good! Now we have a middleware that runs on all requests. Now how do we override the logic for 404?
Those of you who work a lot with Connect/Express apps might guess that you can inject a regular Connect error handling middleware (a regular middleware, just with the signature (err, req, res, next)
) however Nuxt doesn’t treat 404 like an error. That means that your error handler never sees that there’s an issue.
This is where we wander into undocumented territory of Nuxt. Extending what Nuxt renders on 404 is easily achieved. Changing its behaviour is not so.
The first step is to figure out which routes are renderable by Nuxt. Fortunately, Nuxt’s builder hooks and modules are really helpful here. We can write a module that writes all valid routes to file at build time!
// nuxt.config.js
...
modules: ['~/modules/writeRouteToFile.js']
...
// modules/writeRoutesToFile.js
import fs from 'fs'
export default function writeRouteToFile() => {
const routesFile = 'nuxtPageRoutes.json';
const fileExists = fs.existsSync(routesFile)
if(!fileExists) {
// Middlewares are evaluated during build even though requests aren't flowing through them,
// so we have to ensure the file exists otherwise override404 will throw from not finding the file
fs.writeFileSync(
routesFile,
JSON.stringify([])
)
}
this.nuxt.hook('build:extendRoutes', routes => {
// routes here is an array of nuxts internal route objects.
// On routes[x].path we find paths like '/', '/posts' etc and this is the information we're after
fs.writeFile(
routesFile,
JSON.stringify(
routes.map(route => route.path),
{ encoding: 'utf-8', flag: 'a+' }, // The flag a+ creates the file if it doesn't exist
err => {
if (err) throw err
}
)
)
}
}
Now every time the app is built, our module saves all the route paths as an array to a file called nuxtPageRoutes.json
This means that we can make the routes available to our middleware. One step closer!
Next, we have to take these path-literals and make them into regexes. Luckily for us, we can use the library that Connect and Express uses under the hood, path-to-regexp
! Run yarn add path-to-regexp
or npm install path-to-regexp
to add it to your project.
Now that we have that in place, let’s return to our override404.js
middleware and make it hijack the 404 logic!
import fs from 'fs'
import pathToRegexp from 'path-to-regexp'
import ProxyMiddleware from 'http-proxy-middleware'
const NODE_ENV = process.env.NODE_ENV ? process.env.NODE_ENV.toLoverCase() || 'development'
// Take all routes and create path regexps from them. This means that we account for requests like /posts/1 and not just /posts
const readRoutesFromFile = () => JSON
.parse(fs.readFileSync('nuxtPageRoutes.json'))
.map(pathToRegexp)
let nuxtPageRoutes = readRoutesFromFile()
const proxy = ProxyMiddleware({ target: ... }) // target is set to the location of our legacy app
export default (req, res, next) => {
if (NODE_ENV !== 'production') {
// While in development mode, the available routes might change, so we need to check them every time.
nuxtPageRoutes = readRoutesFromFile()
}
if(nuxtPageRoutes.some(route => route.exec(req.url)) {
// A valid path was found, so we let Nuxt render it!
next()
} else {
// Here we invoke our proxy middleware, but really you can put whatever it is you want here!
proxy(req, res, next)
}
}
And there it is! 🎉 We now have a fully functioning proxy that intelligently proxies our traffic, renders our new app with SSR, and lets us migrate in phases with minimal disruption to our other work.
tl;dr: write a Nuxt module that writes all routes to a file on build, read those routes in a serverMiddleware, convert them to regexes with pathToRegexp , check incoming requests against those regexes, if there’s a match pass it further down the middleware chain, if there’s no match run your own function.
☞ Vue js Tutorial Zero to Hero || Brief Overview about Vue.js || Learn VueJS 2023 || JS Framework
☞ Is Vue.js 3.0 Breaking Vue? Vue 3.0 Preview!
☞ Learn Vue.js from scratch 2018