Search code examples
node.jsherokupackage.jsonkoanuxt.js

API Koa server in the backend for Nuxt doesn't run on Heroku (production)


I'm running Nuxt in Universal Mode with Koa as API / Controller in the backend based on the Koa template. I'm deploying to Heroku. API works fine locally, but returns 404 in production. I think that the app is running as SPA when deployed as everything else works well.

Here's my server/index.js

const Koa = require('koa')
const consola = require('consola')
const Router = require('koa-router');
const { Nuxt, Builder } = require('nuxt')
const api = require('./api');

console.log('server works'); // ------> This line gets ignored by the Heroku console

const app = new Koa()
const router = new Router();

// Import and Set Nuxt.js options
const config = require('../nuxt.config.js')
config.dev = app.env !== 'production'

router.use('/api', api.routes(), api.allowedMethods());
app.use(router.routes());

async function start () {
  // Instantiate nuxt.js
  const nuxt = new Nuxt(config)

  const {
    host = process.env.HOST || '127.0.0.1',
    port = process.env.PORT || 3000
  } = nuxt.options.server

  // Build in development
  if (config.dev) {
    const builder = new Builder(nuxt)
    await builder.build()
  } else {
    await nuxt.ready()
  }

  app.use((ctx) => {
    ctx.status = 200
    ctx.respond = false // Bypass Koa's built-in response handling
    ctx.req.ctx = ctx // This might be useful later on, e.g. in nuxtServerInit or with nuxt-stash
    nuxt.render(ctx.req, ctx.res)
  })

  app.listen(port, host)
  consola.ready({
    message: `Server listening on http://${host}:${port}`, //  ------>  Neither this line appears in Heroku console
    badge: true 
  })
}

start()

Procfile

web: nuxt start

Scripts from package.json

"scripts": {
    "dev": "cross-env HOST=192.168.1.65 NODE_ENV=development nodemon server/index.js --watch server ",
    "build": "nuxt build",
    "start": "cross-env NODE_ENV=production node server/index.js",
    "generate": "nuxt generate",
    "test": "ava",
    "test:unit": "cross-env TEST=unit ava --config unit.config.js",
    "test:e2e": "cross-env TEST=e2e ava --config e2e.config.js",
    "heroku-postbuild": "nuxt build"
  }

I think I'm getting nu(x)ts after reading all these deployment docs and not seeing the obvious.

Thanks.


Solution

  • I didn't live any problem, attaching my package.json, server/index.js file and Heroku environment settings. You can check herokuapp from here

    package.json

    {
      "name": "testkoa",
      "version": "1.0.0",
      "description": "My first-class Nuxt.js project",
      "author": "Ahmet Zeybek",
      "private": true,
      "scripts": {
        "dev": "cross-env NODE_ENV=development nodemon server/index.js --watch server",
        "build": "nuxt build",
        "start": "cross-env NODE_ENV=production node server/index.js",
        "generate": "nuxt generate"
      },
      "dependencies": {
        "@nuxtjs/axios": "^5.3.6",
        "@nuxtjs/dotenv": "^1.4.0",
        "@nuxtjs/pwa": "^3.0.0-0",
        "cross-env": "^5.2.0",
        "koa": "^2.6.2",
        "koa-router": "^7.4.0",
        "nuxt": "^2.0.0"
      },
      "devDependencies": {
        "nodemon": "^1.18.9"
      }
    }
    

    server/index.js

    const Koa = require("koa");
    const Router = require("koa-router");
    const consola = require("consola");
    const { Nuxt, Builder } = require("nuxt");
    const app = new Koa();
    // Import and Set Nuxt.js options
    const config = require("../nuxt.config.js");
    config.dev = app.env !== "production";
    
    async function start() {
      app.use(async function handleError(ctx, next) {
        try {
          await next();
        } catch (err) {
          ctx.status = err.statusCode || err.status || 500;
          ctx.body = err;
        }
      });
      const router = new Router({ prefix: "/api" });
      router.get("/:name", async ctx => {
        ctx.response.body = `Hello ${ctx.params.name}`;
      });
      // Instantiate nuxt.js
      const nuxt = new Nuxt(config);
    
      const {
        host = process.env.HOST || "127.0.0.1",
        port = process.env.PORT || 3000
      } = nuxt.options.server;
    
      // Build in development
      if (config.dev) {
        const builder = new Builder(nuxt);
        await builder.build();
      } else {
        await nuxt.ready();
      }
    
      app.use(router.routes());
      app.use(router.allowedMethods());
      app.use(ctx => {
        ctx.status = 200;
        ctx.respond = false; // Bypass Koa's built-in response handling
        ctx.req.ctx = ctx; // This might be useful later on, e.g. in nuxtServerInit or with nuxt-stash
        nuxt.render(ctx.req, ctx.res);
      });
    
      app.listen(port, host);
      consola.ready({
        message: `Server listening on http://${host}:${port}`,
        badge: true
      });
    }
    
    start();
    

    Heroku Config Vars

    HOST=0.0.0.0
    NODE_ENV=production
    NPM_CONFIG_PRODUCTION=false
    

    You don't need Procfile to use your Nuxt app on Heroku with this configuration, remove it from your project folder