0

As I understand, async fetch() in Nuxt makes server side call before render which I'm trying to use to render post data but I don't understand why <NuxtLink> and <a href... are working differently.

  • <NuxtLink> works to render content if url in async fetch /api/readpost/.. is used but it renders before finishing fetching /api/readpost, so the error 404 post flashes before updating after the data shows up
  • <a href... works correctly if url in async fetch is http://spserver:3002/api/readpost/... and correctly waits for the fetch before rendering

Client and server request urls would be different because the project is running in docker behind nginx. My concern here is to always make sure the data shows up before rendering happens and if possible make <NuxtLink> behave the same way regular <a href.. seems to be working.

So, what is causing the difference, how would I ensure async fetch always makes server side request and waits for data before rendering?


Video demo:

video demo 1

On page reload I get console errors like this:

Error: connect ECONNREFUSED 127.0.0.1:80
    at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1159:16) {
  errno: -111,
  code: 'ECONNREFUSED',
  syscall: 'connect',
  address: '127.0.0.1',
  port: 80,
  config: {
    url: '/api/readpost/23',
    method: 'get',
    headers: {
...

Code:

I have something like this in a nuxt page to render post data:

<template>
<template v-if="posts && posts.length">
  <PostRender :post="posts[0]" />
</template>
<div v-else-if="!$fetchState.pending">
404 POST NOT FOUND, PLEASE DOUBLE CHECK THE URL
</div>
</template>

<script>
  export default {
    data() {
      return {
          postid : this.$route.params.post,
          posts : null,
      }
    },
    async fetch()
    {
        let res = await this.$axios({
          method : 'GET',
          url : '/api/readpost/' + this.postid
        });

        this.posts = res.data;
    }
</script>

and this lists the posts and links

<template>
<div>
<div v-for="p in posts">
  <NuxtLink :to="'/post/' + p.id">NuxtLink</NuxtLink>
<hr>
</div>
</div>
</template>

<script>
  export default {
    data() {
      return {
        posts : null
        }
      },
    mounted() {
        this.$axios({
          method : 'GET',
          url : '/api/readlatest'
        })
        .then((res) =>
        {
            this.posts = res.data;
        })
        .catch((err) =>
        {
            console.error(err);
        });
    }
  }
</script>

And my docker compose looks something like this

version : "3"
services:
  spserver:
    build: .
    container_name: spserver
    working_dir: /app
    volumes:
      - ./server/:/app
    ports:
      - 7002:3002
    entrypoint: npm run dev
  spclient:
    image: node:14.18.1-alpine
    container_name: spclient
    working_dir: /app
    volumes:
      - ./client/:/app
    environment:
      - PORT=3001
      - HOST=0.0.0.0
      - "BASE_URL=/"
    entrypoint: sh -c "npm run build && npm run dev"
    depends_on:
      - spserver
  spnginx:
    image: nginx:1.18-alpine
    container_name: spnginx
    volumes:
      - ./nginx.conf/:/etc/nginx/nginx.conf
    ports:
      - 9001:80
    depends_on:
      - spclient
      - spserver

Relavant bit of nuxt.config.js:

axios: {
  baseURL: process.env.BASE_URL
},

Versions:

"nuxt": "^2.15.8",
"@nuxtjs/axios": "^5.13.6",

This solved my issue, although a little confused about the whole thing still:

url : (process.server ? 'http://spserver:3002/api/readpost/' : '/api/readpost/') + postid
kissu
  • 40,416
  • 14
  • 65
  • 133
Robert C. Holland
  • 1,651
  • 4
  • 24
  • 57

1 Answers1

2

Nuxt is isomorphic but will make a call to the server only on the initial render. That one will be generated on the server (supposing ssr: true) and will be sent to your browser.

There, you will only get client-side navigation on any successful route changes thanks to Vue router.

If you use an a tag, you will be nuking your whole Nuxt/Vue app and creating a brand new from scratch, hence have another backend generation (clearly not the way to go as you can guess).

If you want to have an example of data fetching using fetch() (lifecycle hook), you can check this answer. It can also be done in a blocking way by using the asyncData().

PS: I recommend you using the ones above rather than created() and mounted() in a Nuxt app + try to use async/await all the time rather than a mix between those + .then.

kissu
  • 40,416
  • 14
  • 65
  • 133
  • Thanks, so I should be using `` and show the 404 message behind `
    404
    `. But I don't understand how then should I be handling reloads or direct linking as using `` with `'/api/readpost/' + this.postid` request breaks reloads or direct links.
    – Robert C. Holland Sep 17 '22 at 22:23
  • @RobertC.Holland yeah, it's not really a "404" but more of a "I'm currently loading your data", but the idea is to use `$fetchState` when using `fetch()` indeed! Reloads should not be breaking as shown in my example: use the same principle as for the details page of your post. Accessing it directly will get the param in the route, hence produce the same result as making a client side navigation. Feel free to edit your question so that we can see what is still not working. – kissu Sep 17 '22 at 22:58
  • @RobertC.Holland quite some weird error. Try to fix the various points that I have initially suggested in my answer (`async/await` + hooks + proper usage of `$fetchState`) to be sure that it's not coming from there. Then, if you still have some issue, I may recommend using JSONplaceholder to try to see from where the issue may come from. Not sure if it's a config/server issue or something else. – kissu Sep 17 '22 at 23:26
  • 1
    Thanks for the help, I managed to get it to work like this `url : (process.server ? 'http://spserver:3002/api/readpost/' : '/api/readpost/') + postid` – Robert C. Holland Sep 18 '22 at 04:17
  • @RobertC.Holland shouldn't you have the same URL for both the server and client? Why 2 different endpoints? – kissu Sep 18 '22 at 10:41
  • 1
    I'm not fully sure given how Nuxt works but probably because the project is running inside docker and exposed outside at different port via reverse proxy through nginx. The node/express server is exposed as `https://localhost:9001/api/` outside and `http://spserver:3002/` inside docker. – Robert C. Holland Sep 18 '22 at 18:36