9

Even after reading through multiple articles explaining the differences between static and SSR rendering I still don't understand how dynamic API calls work in these different modes.

I know that Nuxt has the fetch and asyncData hooks which are only called once during static generation, but what if I use dynamic HTTP requests inside component methods (e.g. when submitting a form via a POST request)? Does that even work in static sites?

I'm making a site that shows user generated content on most pages, so I have to make GET requests everytime one of those pages is visited to keep the content up to date. Can I do that with a static site or do I have to use SSR / something else? I don't want to use client side rendering (SPA mode) because it's slow and bad for SEO. So what is my best option?

kissu
  • 40,416
  • 14
  • 65
  • 133
DaDo
  • 443
  • 1
  • 6
  • 20

2 Answers2

10

There is actually no difference between either asyncData() or fetch() hooks when you do use target: static (SSG) or target: server (default, SSR).
At least, not in your use-case.

They are used mainly by your hydrated app.
As a reminder, when using either SSG or SSR, your static page will be hydrated and will become an SPA with all the dynamic functionality that we love. This combo of SSG + SPA or SSR + SPA is called an universal app (or isomorphic app).

Both asyncData() and fetch() will be called upon navigation within your client side SPA.


There are also some things happening on the server side, like fetch being called (by default) when you request the server for an SSR built app.

Or the fact that when you generate your app (if using SSG), you can reach some API and generate dynamic routes (useful in the case of a headless CMS + blog combo for example).
For performance reasons and to have a quick build time, you may pass a payload and use it in an asyncData hook in the dynamic route, as explained here


Still, a static Nuxt app, is basically just an app built ahead of time, with no need for a Node.js server, hence why an SSG app can be hosted on Netlify for free (CDN) but and SSR one needs to be hosted on something like Heroku (on a paid VPS).

The main questions to ask yourself here are:

  • do you need to have some content protected? Like some video courses, private user info etc...already in your Nuxt project (if SSG, disabling the JS will give access to the generated content)
  • is your first page a login? Is it mandatory to access the rest of the content? Like an admin dashboard (you cannot generate content ahead of time if the data is private, think of Facebook's feed being generated for every account, not feasible and not secure as above)
  • is my API updating super often and do I need to have some super quick build time (limitation on free tiers essentially)? (SSG will need a re-generation each time the API changes)

If none of those are relevant, you can totally go SSG.
If one of those is important to you, you may consider SSR.


I do recommend trying all of them:

  • SSR (ssr: true + target: server) with yarn build && yarn start
  • SSG (ssr: true + target: static) with yarn generate && yarn start
  • SPA only (ssr: false + either target: static, target: server also work but who wants to pay for an SPA?!) with yarn generate && yarn start

Try to host it on some platforms too, if you want to be sure to understand the differences beyond your local build.
You can use this kind of extension to also double-check the behavior of having JS enabled or not.

I will probably recommend to take the SSG path. Even tho, if your content is always changing you will probably not benefit much from SEO (eg: Twitter or Facebook).

This github answer could maybe help you understand things a bit better (it does have some videos from Atinux).


PS: I did a video about this on the latest Nuxtnation that you can find here.

kissu
  • 40,416
  • 14
  • 65
  • 133
  • Thank you for this detailed answer, but I'm still confused about some things. The first is you said that asyncData and fetch hooks are called on route changes in the SPA even with an SSG app, but the documentation says something different: "For static hosting, the fetch hook is only called during page generation, and the result is then cached for use on the client". The second is you wrote `ssr: false` + `target: static` for SPA but this stackoverflow answer says that's not possible: https://stackoverflow.com/a/63638062/13575631 – DaDo Aug 19 '21 at 08:24
  • @DaDo if you read the answer closely, you'll find that a comment of mine saying that this one is faulty. Looking at the sentence you quoted, available here: https://nuxtjs.org/docs/2.x/features/data-fetching#the-fetch-hook It is comparing both the behavior of `fetch()` during SSR and during SSG. When you SSR, it will generate the thing each time you reach the server (page reload or typing the URL), but if you SSG it, it will create it as static, hence no need to generate the page on the server each time aka `the fetch hook is only called during page generation` + on client after hydration ofc – kissu Aug 19 '21 at 08:37
  • ok I saw your comment on that other answer now. Regarding fetch, does that mean when I first visit the page (or refresh it) it will show potentially outdated content but when I navigate to that same page from inside the app it will show updated content? – DaDo Aug 19 '21 at 08:48
  • @DaDo first off, if you use `asyncData()` on first page visit or refresh, this hook will just not be triggered (it does only during page transitions). Meanwhile, if you use `fetch()`, it will totally be called once your hydration is done and it will get the latest content, up to date. The cached version of the `fetch()` hook can maybe sometimes help performance-wise I guess. I am using a middleware in our app, so I cannot be totally sure of the behavior if the data is different from server to client. But I don't see why it won't be updated. Here, you'll need to try it yourself IMO. – kissu Aug 19 '21 at 09:09
  • Sorry for necroing this old question, but I have yet another question regarding your previous comments. You said `fetch()` will be called after hydration on client side in SSG mode, adding onto the cached data if any new content was found (or overriding the cache, not sure about that). But in [this official Nuxt article](https://nuxtjs.org/announcements/going-full-static/#crazy-fast-static-applications) there's this quote on the new `nuxt-generate`: **"no more HTTP calls to your API on client-side navigation"**. Were you talking about the old static generation? – DaDo Nov 04 '21 at 19:35
  • @DaDo if you're accessing the data that Nuxt have generated statically, you will not make any HTTP calls indeed. Like in my answer [here](https://stackoverflow.com/a/68431975/8816585). Meanwhile, if you're reaching for Google (for example) or any other external API via an axios request, you will need to make an HTTP call. – kissu Nov 05 '21 at 10:30
  • But if that external API call to Google (for example) is inside a `fetch()` or `asyncData()` then that will be statically generated, or not? The only HTTP calls during runtime should be those **outside** of the Nuxt hooks, like in `mounted()` or any method. Or are there exceptions even to `fetch()`? – DaDo Nov 05 '21 at 13:29
  • @DaDo sorry, I'm not sure of having the answer exactly here. For me, you should not make exceptions on what you do put inside of `fetch()` or `asyncData()`. Also, the best solution is to try the calls you're wishing to have. Either inspect the `/dist` directory or look in your devtools network tab to see what will be called. – kissu Nov 05 '21 at 15:39
  • Ok thank you anyway, it's a very confusing topic. I'll continue this discussion on the Nuxt Discord. – DaDo Nov 05 '21 at 16:05
  • @DaDo please feel free to report back your findings here! – kissu Nov 05 '21 at 17:15
  • @DaDo I believe I'm trying to get an answer for the same question. I've asked this on GitHub but unfortunately, so far without a response. https://github.com/nuxt/nuxt.js/discussions/9825 – Jakub Duchon Dec 04 '21 at 11:14
8

I use dynamic HTTP requests inside component methods (e.g. when submitting a form via a POST request)? Does that even work in static sites?

The short answer to this question is that yes, it does work. In fact you can have http requests in any life cycle hooks or methods in your code, and they all work fine with static mode too.

Static site generation and ssr mode in Nuxt.js are tools to help you with SEO issues and I will explain the difference with an example.

Imagine you have a blog post page at a url like coolsite.com/blogs with some posts that are coming from a database.

SPA

In this mode, when a user visits the said URL server basically responds with a .js file, then in the client this .js file will be rendered. A Vue instance gets created and when the app reaches the code for the get posts request for example in the created hook, it makes an API call, gets the result and renders the posts to the DOM.

This is not cool for SEO since at the first app load there isn't any content and all search engine web crawlers are better at understanding content as html rather than js.

SSR

In this mode if you use the asyncData hook, when the user requests for the said URL, the server runs the code in the asyncData hook in which you should have your API call for the blog posts. It gets the result, renders it as an html page and sends that back to the user with the content already inside it (the Vue instance still gets created in the client). There is no need for any further request from client to server. Of course you still can have api calls in other methods or hooks.

The drawback here is that you need a certain way for deployment for this to work since the code must run on the server. For example you need node.js web hosting to run your app on the server.

STATIC

This mode is actually a compromise between the last two. It means you can have static web hosting but still make your app better for SEO.

The way it works is simple. You use asyncData again but here, when you are generating your app in your local machine it runs the code inside asyncData, gets the posts, and then renders the proper html for each of your app routes. So when you deploy and the user requests that URL, she/he will get a rendered page just like the one in SSR mode.

But the drawback here is that if you add a post to your database, you need to generate your app in your local machine, and update the required file(s) on your server with newly generated files in order for the user to get the latest content.

Apart from this, any other API call will work just fine since the code required for this is already shipped to the client.

Side note: I used asyncData in my example since this is the hook you should use in page level but fetch is also a Nuxt.js hook that works more or less the same for the component level.

Sharkfin
  • 128
  • 1
  • 7
hamid niakan
  • 2,668
  • 1
  • 12
  • 25
  • But if API calls are still made from the static site, can't I simply put the GET /blogposts request in the mounted() hook instead of the asyncData() hook and thus keep the content up to date without having to generate it again? – DaDo Aug 19 '21 at 08:10
  • 1
    @DaDo you can totally also make the call each time if you want something really dynamic yeah. Meanwhile, prefer using either `asyncData()` or `fetch()` since those 2 are aimed towards this usage and, are available here. Even in a VueJS app, I'd recommend running async calls in a `created()` hook, not a `mounted()`. – kissu Aug 19 '21 at 08:21
  • 1
    @DaDo you can! but client doesn't get a page populated with content, it gets the page, then gets the posts then populate the page with content. the whole point of `asyncData` here is to deliver a page filled with content so it performs better when it comes to SEO – hamid niakan Aug 19 '21 at 08:24
  • @hamidniakan so a static site with API calls is basically the same as an SPA but with slightly better SEO because you're still getting the whole HTML structure just not filled with dynamic content yet (?) – DaDo Aug 19 '21 at 08:53
  • @DaDo in the bigger picture yeah. It's a bit more complex looking at what is happening behind the curtains but an universal app is basically an SPA on steroids yeah. And _static_ could be named _ready for CDN_ (lol), because it usually confuses people thinking that it's just a hardcoded static file while it is `static + JS`, with `static` meaning that the file is already generated but JS can do totally dynamic things. So `generated ahead of time with Single Page App hydration` is the more accurate naming for this one. – kissu Aug 19 '21 at 09:13
  • @DaDo yes a static site with API calls is the same as a SPA, in terms of SEO I'm not sure if you send a html page to the client but with no content filled in it in its first load how much better it would be ¯\_(ツ)_/¯ . to get a better understanding of this concept and know how to use the full potential of a `static` site generated with nuxt i recomment you to check this video https://noti.st/debbie/wH1qH8 – hamid niakan Aug 19 '21 at 09:17
  • @hamidniakan you do actually send raw HTML to the end user yeah. Try disabling your JS to be sure! Of course, it also depends on what you do have in your page. And yeah, Debbie did some awesome videos/talks. :D – kissu Aug 19 '21 at 09:21
  • 1
    @kissu yes I'm sure you send raw HTML to the client ^_~ what I'm not sure about is how much difference is between a raw HTML which should get the content after loaded to the client and populate the page and the all js version in a full SPA app in terms of SEO. I guess the first one might be better! if I were to chose static generation, I would choose it in situation where I would use `asyncData` and if the content needs to be updated I would generate the site again. of course I'm talking about a content-full site not an admin panel or alike. this way I make sure to take full advantage for SEO – hamid niakan Aug 19 '21 at 09:47
  • 1
    @hamidniakan you don't really have any SEO in an SPA. Or at least, Google will give you a far better score if it doesn't need to understand the JS but just to read the HTML (web metrics can be found on web.dev). I do prefer `fetch()` all day vs `asyncData()` tbh. – kissu Aug 19 '21 at 09:51
  • 1
    @DaDo I think that hamidniakan's answer deserves to be accepted. It really gives a good perspective on a multifaceted topic, and it helped me a lot – Sharkfin Nov 20 '21 at 13:47
  • Have tried to edit the answer to improve style and punctuation, but it seems the edit queue is permanently full? – Sharkfin Nov 21 '21 at 06:31
  • @Sharkfin try it another time? Also, what edit do you want to do? Minor details like punctuation and alike are not good enough to be considered as a good edit usually. – kissu Nov 21 '21 at 06:45