Hey folks! As some of you know, I’m kind of active on the Nuxt.js Discord. While answering questions there, one of the most common questions that’s neither covered in the docs (because you can’t generalize the answer) nor covered otherwise is: "What approach should I take when using Nuxt 2 with my (self-built) API?"
This blog post will cover my take on that question. Disclaimer: My take does not mean “always the right way”. It’s just what I found out to be the best way for me ☺️ Also be aware that this post is not about Nuxt 3.
There are three main approaches when it comes to using an API with Nuxt.js. Before telling you my opinion I’ll go through each of them.
Nuxt.js provides so-called serverMiddleware
which is a convenient interface to change the behavior of the underlying connect server instance. You can also early return a result so Nuxt.js doesn’t handle the path like it’d usually do but your custom serverMiddleware
does. With this API you can add more custom functionalities to Nuxt and even leverage Express (or any other Node.js framework) inside Nuxt (for example to send emails through Nuxt.js)!
Another use case would be a server-side logger which is closer to the “middleware” term you might be familiar with.
You don’t need another server running for it and can combine Nuxt and your features in one instance.
A small example (play around with it here):
serverMiddleware/ok.js
export default {
path: '/test',
handler(req, res) {
res.end('Everything ok!')
}
}
nuxt.config.js
export default {
serverMiddleware: [
'~/serverMiddleware/ok'
]
}
Hitting /test
will show you 'Everything ok!'
. The request won’t go through Nuxt.js at all (as long as it’s the initial request and not rendered client-side).
In the serverMiddleware
chapter I said it’s possible to use Express inside Nuxt. With the programmatic approach we will do the opposite and use Nuxt inside Express (or, as stated before, any other Node.js framework).
If you already have an Express instance running and need Nuxt only for parts of your routes (for example while transitioning from your old stack) this seems like a compelling idea!
Nuxt will be used as a middleware in such cases. Below is an example with Express:
const express = require('express')
const consola = require('consola')
const { Nuxt, Builder } = require('nuxt')
const app = express()
const host = process.env.HOST || '127.0.0.1'
const port = process.env.PORT || 3000
app.set('port', port)
// Import and Set Nuxt.js options
let config = require('../nuxt.config.js')
config.dev = !(process.env.NODE_ENV === 'production')
async function start() {
// Init Nuxt.js
const nuxt = new Nuxt(config)
// Build only in dev mode
if (config.dev) {
const builder = new Builder(nuxt)
await builder.build()
}
// Give nuxt middleware to express
app.use(nuxt.render)
// Listen the server
app.listen(port, host)
consola.ready({
message: `Server listening on http://${host}:${port}`,
badge: true
})
}
start()
In case you are using another programming language for you API (say PHP, Python or Go), the only option you have is to separate the API and the Nuxt instance and to call your API via axios
or fetch
. Most of the time, the API and Nuxt will run on different servers then. This approach is probably the most common one as APIs are often already existing.
Okay, there we go! My workflow and practices with APIs depend largely on:
While 3 months ago, most of my pages were powered by Nuxt’s dynamic SSR
mode, I’ve converted the majority of them to static pages because they did not rely on highly dynamic data. Some examples (also open-sourced) are developmint.de, thanks.lichter.io and this blog. I’ll write more about the reasons, motivation, and general feelings in an upcoming blog post (so say tuned!).
When using static sites though, the API is lightweight most of the time. In such cases, I prefer to use serverless functions to isolate my API and remove the need for a server completely. A great example is a contact form endpoint, which was previously powered through Nuxt’s serverMiddleware but is now decoupled as a serverless function.
For apps that are highly dynamic (like brotli.pro) but have a small number of API endpoints or for projects that are either proof of concepts or small-scaled I tend to use Nuxt’s built-in serverMiddleware
option. It allows me to not care about another app to set up and to route (keep in mind that I manage all my SSR-apps on my own VPS) and it also allows me fast prototyping. But as soon as the API grows, I can (more or less smooth) switch over to a standalone server (or many of them).
Most of my projects involving more complex or a non-trivial amount of API endpoints are built on Laravel
(the PHP framework). But even for those that are powered by Javascript (where I’d have all three mentioned options) I chose the same one: A standalone API server decoupled from the Nuxt instance.
I see some huge benefits in using this approach:
When using a standalone API (and having control over the server), please do not forget to set up a reverse-proxy, like NGINX, in front of your Nuxt instance and API, so you can:
/api
or whatever route you want to avoid CORS issues (use the official proxy module in dev mode to mimic the behavior)(and so on). As I’m hosting all my SSR-based apps and APIs on my own servers, this is a crucial point for me and improves perf a lot if done right.
Some of you might have noticed that I use only two out of the three ways I introduced. The one that is missing is using Nuxt programmatically. But why is that the case?
Well, because I do not see any advantage in it. Quite the opposite! To me, it feels a bit awkward mixing Nuxt and a framework like Express. For example, because you can’t use the ESM syntax (import/export) in your nuxt.config.js
out of the box or that you have to orchestrate the Nuxt Builder on your own.
Especially newcomers could easily come to wrong conclusions (like that Express will handle all route calls, even when the app is past the SSR step and behaves like a traditional SPA). Also, there is no “common interface” to communicate between the underlying framework and Nuxt (which doesn’t mean it’s impossible).
There are some exceptions where this combination does make, for example when using Nuxt.js with AWS or in another serverless context. If you know more, please tweet me!
You can’t go wrong with using a standalone API. It could mean a bit more effort to configure all the things but it will be worth it in the long run. If you need a way to bootstrap your app fast (eg. working on an MVP), and want to use Javascript in the backend too, utilize Nuxt’s serverMiddleware
capacities.
But as usual, I suggest try out all approaches once to see what fits best for you. If you’ve found out the approach you like most, feel free to check out how to organize APIs in Nuxt.js properly!
Did I miss something or do you have a question? Tweet me at @TheAlexLichter, reach out on the Vue/Nuxt Discord or write me a mail (blog at lichter dot io).
Hope this article cleared up the ways to integrate an API with Nuxt.js!
I'm Alex, a German web engineering consultant and content creator. Helping companies with my experience in TypeScript, Vue.js, and Nuxt.js is my daily business.
More about meBrotli, a somewhat new compression from Google is getting more and more traction. To minify your Nuxt.js application as good as possible, you should set up Brotli compression for it as well! Learn how to in this article.
SSR comes with certain caveats, including no access to APIs like the local storage on server-side. But what if you could enable SSR only for pages where SEO is needed and use the "traditional" SPA mode elsewhere? You can! Learn how in this article.