Skip to content
John R Cottam

Running Nuxt in Parallel with Express

How to wire Nuxt and Express together so you get server-side rendering for the frontend and a real API layer on the backend — both in one Node process.

John Ryan Cottam

1 min read

Running Nuxt in Parallel with Express

Nuxt is my go-to when I need server-side rendering for a Vue app. Client-side Vue works fine, but if SEO matters — crawlers need something to index — Nuxt renders each route from the server and hands them a fully-formed page. Problem solved.

But server-side rendering means you’re already running Node. So you might as well use it. That’s where Express comes in: Nuxt owns the view, Express owns the API. Database queries, third-party requests, email — all of it lives behind Express routes. Clean separation, single process.

Setup

Bootstrap the project and choose Express as the server framework:

yarn create nuxt-app nuxt-with-express

When prompted:

  • Server-side framework: Express
  • Modules: Axios

Then:

cd nuxt-with-express
yarn dev

If http://localhost:3000/ loads, you’re ready.

The key detail: route order

Open server/index.js. You’ll see Nuxt rendering gets mounted at the bottom with app.use(nuxt.render). Express routes must go above this line — otherwise Nuxt intercepts the request first and your API never runs.

const express = require('express')
const { Nuxt, Builder } = require('nuxt')
const app = express()
const config = require('../nuxt.config.js')

config.dev = process.env.NODE_ENV !== 'production'

async function start() {
  const nuxt = new Nuxt(config)

  if (config.dev) {
    const builder = new Builder(nuxt)
    await builder.build()
  }

  // Express routes go here — before nuxt.render
  app.get('/api/movie', async (req, res) => {
    const axios = require('axios')
    const { data } = await axios.get('http://www.omdbapi.com/', {
      params: { t: req.query.title, apikey: process.env.OMDB_API_KEY }
    })
    res.json(data)
  })

  // Nuxt handles everything else
  app.use(nuxt.render)

  app.listen(3000)
}

start()

The Nuxt side

Create pages/index.vue and call your Express endpoint with Axios:

<template>
  <div>
    <input v-model="title" placeholder="Movie title..." />
    <button @click="search">Search</button>
    <div v-if="movie">
      <h2>{{ movie.Title }} ({{ movie.Year }})</h2>
      <p>{{ movie.Plot }}</p>
    </div>
  </div>
</template>

<script>
export default {
  data: () => ({ title: '', movie: null }),
  methods: {
    async search() {
      const { data } = await this.$axios.get('/api/movie', {
        params: { title: this.title }
      })
      this.movie = data
    }
  }
}
</script>

That’s the whole pattern. Nuxt renders the view, Express serves the data, and both run in one process on one port.

The full working example is on GitHub.

Subscribe to my notes

Stay in the loop on what I'm building and thinking about.