Search code examples
reactjsnext.jsvercelnext.js13

Comparing data fetching approaches in NextJS - conceptually why are there so many ways to do it referenced in the docs? (Page Router/pre-Next.JS 13.4)


I've worked my way through using various data fetch methods in NextJS.

In the docs, there are 4 or 5 relevant terms under Data Fetching.

  • getStaticProps
  • getStaticPaths
  • getServerSideProps
  • Incremental Static Regeneration
  • Client-side Fetching
    • “Regular” (useEffect->fetch->setData)
    • SWR

I've walked through them all and they all seem great - but it would be really helpful to have a higher level conceptual overview to better explain why they are all needed (and when)?

Note: this is pre-13.4 so the Page Router architecture would be in play here.


Solution

  • The need for the various data fetching methods created/referenced in the NextJS docs would be better understood by understanding these 7 concepts.

    CONCEPT 1:
    First off, it is important to note that NextJS is a hybrid of React along with functionality that allows you to create server-side APIs.

    So React would run on the client side (on your users’s computer/browser) but the server-side APIs would run from where you host the app - if you’re using Vercel’s hosting then it would run from their servers.

    CONCEPT 2:
    Also important to note here is that a client app calling your defined API in pages/api and then waiting to receive the results from that pages/api would take time. This is mentioned indirectly in the docs but brought up a few times under the idea of performance.

    CONCEPT 3:
    Client side code vs. API code have different security considerations. Your API code will live on Vercel’s servers — making it generally safe from prying eyes (except for real “hackers” … this is not the base case you would be considering). The client code code on the other hand will LIVE on the users computer / browser. Inspecting through this is easy so this there is a natural security devide in the sense that some of the code is very visible to your users and some is not.

    Obviously, issues can arise due to this and NextJS tries to mitigate some of this; for example, NextJS makes you prefix your client-side, environmental variables with NEXT_PUBLIC_ to prevent you from using a private environmental variable in the client side code. Not including this prefix will break the build. (This is cool - you want this, and they took it even further NextJS v.13.4+)

    CONCEPT 4:
    NextJS will remove some of the code you wrote from the bundle that gets downloaded to the client's browser. So, say you are writing your code in VSCode on a file called MyCoolPage.js. Locally, on VSCode, your code file is written and saved on MyCoolPage.js as it is normally done; however, NextJS will actually take out some of the code so it doesn’t land in the bundle that lives on the client side.

    Relevantly to this question, your code in getStaticProps and getServerSideProps gets removed. In case you're not sure about this and how your code will be handled, NextJS has created a tool that can, on the fly, show you what of your code gets eliminated. This lives here; this tool is also linked to from the docs on the getStaticProps and getServerSideProps pages.

    NextJS eliminates the code from the client code to make sure that the users can’t see these functions. This is part of the reason/benefit/convenience of NextJS creating these special functions that are listed in the data fetching section.

    CONCEPT 5:
    Second, it is important to note that there are statically generated sites and then dynamically generated sites. (Dynamically generated are basically what you would normally consider an “app” — users click on stuff, and then your app does things, like calls a database or an external API to get data, etc.)

    On the other hand, statically generated sites generally do not update based on user interaction — however, they can get data elsewhere. This data can be grabbed at build time.

    Build time? Let’s just make this a very practical, simplified example. (Assume you have a simple automatic deployment setup from Github to Vercel to build then host your app) — after you do your Github checkin to main, it will trigger an upload to Vercel, your app gets built (here you can watch it get built on the Vercel site and it will check for errors) and if it passes the checks it will be deployed, at which point users can access this new version.

    So the statically generated sites, during the above build time, will make call outs to the various APIs, get the data it needs, then the site will get built with that data in hand. Then that site gets deployed. Net net, your users will see a site pre-built with data pre-loaded -- the advantage of course being speed.

    CONCEPT 6:
    Speed matters of course, but also pre-generated static sites have SEO advantages. Web crawlers can go through your static site with the data already pre-baked into the site hence making your site relevant for search engines.

    CONCEPT 7:
    Now that you’ve understood this, you understand why there would be data fetching functionally to pre-load data at build time (getStaticProps) and now you understand why would be “regular”client-side fetching which would be useEffect -> fetch -> setData.

    Let’s call these the two extremes (fully static vs. fully dynamic). Putting them on a line for comparison, say that on the far-left you have the fully static functionality and and the far-right you have the fully dynamic functionality.

    Now to answer your question about why these others functions exist … the other functions move “inward” away from the extremes — so that there are functions on the left that are mostly static or static-ready but can accommodate some level of dynamic update and there there are functions on the right that are mostly dynamic but can handle things like intermediate caching, pre-loading data, fallback default values, and automatically refreshing after some time or some other triggers.

    Also, to add a little more complexity to this simple answer, SWR (the last method mentioned in your list) adds things like Optimistic updates where it can update your users' app state immediately after some action, and then later (sequentially) update those changes with the server (vs. updating those changes with the server first and then updating your users' app state which is the plain vanilla approach.). This brings on a whole slew of additional consideration like what to do on errors, rollbacks, and updating cache. (Why is this important to mention? Because much of the whole point of all these functions is speed and user experience related to data uploading time and coordination of refreshing data … SWR opens up a whole other world in this dimension.)

    • getStaticPaths is a little bit of a different case from the above. It doesn't exactly fit with the above, but it is used when you have a ton of items and you don't want to hardcode a lot of paths. So let's say you have an online bookstore, and you want to give each book it's own url but don't want to hardcode a new path for each book, getStaticPaths is the tool that lets you do that.