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
(wherexxx
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
- the user enters the url
http://yourserver/pages/123
in its browser - the nuxt web server handles the request, resolve the route and mount the vue component for that page
- the
asyncData
method from the vue component is called from the nuxt.js server side - 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.
- 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. - 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 serverclient-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 nameapi
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 theapi
container on port1337
. - And you need to configure nuxt.js so that links to the api made
client-side
(from the user browser) use the urlhttp://yourserver/api
instead ofhttp://api:1337/
- And you need to configure nuxt.js so that it keeps calling
http://api:1337
for calls madeserver-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:
- one for
server-side
requests (the nuxt.js Proxy module) - one for
client-side
requests (Traefik)
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