GET http://api:1337/games net::ERR_NAME_NOT_RESOLVED for nuxt.js pages using asyncData GET http://api:1337/games net::ERR_NAME_NOT_RESOLVED for nuxt.js pages using asyncData docker docker

GET http://api:1337/games net::ERR_NAME_NOT_RESOLVED for nuxt.js pages using asyncData


Visiting index page or /pages/_id pages I have no errors. But when I try to open /other-page it crashes.

To reformulate:

  • I have a main page at / that shows some links targeting pages at /pages/_id (where _id is a valid game id)
  • When I open / or /pages/_id, the content shows up
  • But if I click a link from page / targeting /pages/xxx (where xxx is a valid id), I got an error
  • Furthermore if I refresh the page, I then see the content and not the error
  • content for those pages comes from an api server. Pages are supposed to fetch the content by calling the api server and then render the page contents with the response.

What's happening here?

AsyncData

The way asyncData works in nuxt.js is the following:

on first page load

  1. the user enters the url http://yourserver/pages/123 in its browser
  2. the nuxt web server handles the request, resolve the route and mount the vue component for that page
  3. the asyncData method from the vue component is called from the nuxt.js server side
  4. the nuxt.js server (not the user browser) then fetch the content by making different call to http://api:1337/games/123, receive the response and the content renders.

when the user clicks a link for another page

Something a bit different happens now.

  1. The user is still on the page http://api:1337/games/123 which has a link to the main page listing all the games (http://yourserver/) and click it.
  2. the browser does not load any new page. Instead, the user browser makes an ajax call to http://api:1337/games to try to fetch the new content. And fails due to a name resolution error

Why?

This is a feature brought to you by nuxt.js to speed up page content loading time. from the documentation, the important bit of information is:

asyncData is called every time before loading the page component. It will be called server-side once (on the first request to the Nuxt app) and client-side when navigating to further routes.

  • server-side means the call is made from the nuxt server to the api server
  • client-side means the call is made from the user browser to the api server

Now the fun part:

  • the nuxt server is running in a first container
  • the api server is running in a second container and is listening on port 1337
  • from the nuxt container, the url for calling the api server is http://api:1337/, and this works fine
  • from the user browser, calling http://api:1337 fails (net::ERR_NAME_NOT_RESOLVED) because the user computer does not know how to translate the domain name api to an IP address. And even if it could, this IP Address would be unreachable anyway.

What can be done?

  • You need to set up a reverse proxy that will forward requests made by the user browsers to url starting with http://yourserver/api/ to the api container on port 1337.
  • And you need to configure nuxt.js so that links to the api made client-side (from the user browser) use the url http://yourserver/api instead of http://api:1337/
  • And you need to configure nuxt.js so that it keeps calling http://api:1337 for calls made server-side.

adding a reverse proxy for calls made from nuxt (server-side)

Since you are using the nuxt.js Axios module to make calls to the api container, you are half way there.

The Axios module has a proxy option that can be set to true in nuxtjs.config.js

Bellow is an example of setting up a reverse proxy for your project using Traefik, but the documentation state that the proxy is incompatible with the usage of the baseURL option. The prefix option must be used instead.

Your nuxt.config.js should then look like this:

  axios: {    prefix: '/api',    proxy: true  },   proxy: {    '/api/': {      target: 'http://localhost:1337',      pathRewrite: {        '^/api/': ''      }    }  },

This works fine from your development computer, where if strapi is running and responding at http://localhost:1337. But this won't work in a container because we there need to replace http://localhost:1337 with http://api:1337.To do so, we can introduce an environment variable (STRAPI_URL):

  axios: {    prefix: '/api',    proxy: true  },   proxy: {    '/api/': {      target: process.env.STRAPI_URL || 'http://localhost:1337',      pathRewrite: {        '^/api/': ''      }    }  },

We will later set the STRAPI_URL in the docker-compose.yml file.

adding a reverse proxy for calls made from the user browser (client-side)

Since I gave up on implementing reverse proxies with nginx when using docker, here's an example with Traefik:

docker-compose.yml:

version: '3'services:  reverseproxy:  # see https://docs.traefik.io/#the-traefik-quickstart-using-docker    image: traefik:1.7    command: --docker    ports:      - "80:80"    volumes:      - /var/run/docker.sock:/var/run/docker.sock  api:    image: strapi/strapi    environment:      - ...    expose:      - 1337    labels:      traefik.frontend.rule: PathPrefixStrip:/api      traefik.port: 1337  nuxt:    image: ...    expose:      - 3000    command:      "npm run start"    labels:      traefik.frontend.rule: PathPrefixStrip:/      traefik.port: 3000

Now all HTTP requests made by the user browser to http://yourserver will be handled by the Traefik reverse proxy.

Traefik will configure forwarding rules by looking at labels starting with traefik. on the nuxt and api containers.

What changed?

You now have 2 reverse proxies:

It's not done yet

We now need to instruct the nuxt.js Proxy module that it must forward requests to http://api:1337/. We are going to use the STRAPI_URL environment variable for that.

And we need to instruct nuxt Axios module that the user browser must call the api on http://yourserver/api. This is done with the API_URL_BROWSER environment variable.


All together

nuxt.config.js

  axios: {    prefix: '/api',    proxy: true  },   proxy: {    '/api/': {      target: process.env.STRAPI_URL || 'http://localhost:1337',      pathRewrite: {        '^/api/': ''      }    }  },

docker-compose.yml

version: '3'services:  reverseproxy:  # see https://docs.traefik.io/#the-traefik-quickstart-using-docker    image: traefik:1.7    command: --docker    ports:      - "80:80"    volumes:      - /var/run/docker.sock:/var/run/docker.sock  api:    image: strapi/strapi    environment:      - ...    expose:      - 1337    labels:      traefik.frontend.rule: PathPrefixStrip:/api      traefik.port: 1337  nuxt:    image: ...    expose:      - 3000    command:      "npm run start"    environment:      NUXT_HOST: 0.0.0.0      STRAPI_URL: http://api:1337/      API_URL_BROWSER: /api    labels:      traefik.frontend.rule: PathPrefixStrip:/      traefik.port: 3000