Skip to content

How to Choose Between Data Fetching Options in Next.js

This article was written over 18 months ago and may contain information that is out of date. Some content may be relevant but please refer to the relevant official documentation or available resources for the latest information.

A few days ago, we published another blog post related to Next.js: Understanding Next.js Data Fetching for Beginners.

In that blog post, we show the functions this framework provides us to perform different data fetching options. I hope you already have a basic understanding of Next.js. If not, I highly recommend that you first read the article linked above.

Let's get started.

There are multiple strategies for us to choose from, and it gets confusing at first to apply the correct solution in the correct place. We will take a real-life application, and understand which strategy works best for which scenarios.

The special functions that are used for pre-rendering in Next.js are:

  • getStaticProps
  • getStaticPaths
  • getServerSideProps

We will see in which scenarios you can apply these functions, and learn the best practices for doing so.

We are talking about an Online Store

Let's assume that we want to build an Online Store to display some products, a product page (and details), and an accounts page (order details, personal info, etc). Let's also assume that we don't want to use React because we want to optimize SEO.

Let's start thinking about this.

Landing Page (getStaticProps)

The landing of our OnlineStore is where you can see two things, the list of some products you want to highlight could be:

  • Featured Products
  • Popular products
  • Discounted Products
  • Upcoming Products

Whatever the case, the main thing is you already know which API you will be using, and probably that API will not change in the short term. In these scenarios, only getStaticProps is enough.

As we see in our previous post:

export async function getStaticProps() {
    const url = 'https://our-api-products.com/';
    const res = await fetch(url, {
        headers: {
        Accept: 'application/json',
        },
    });
    const data = await res.json();

    return {
        props: {
            products: data.products,
        },
    };
}

Now, our landing page will be pre-generated ahead of time and won't change. For further details and a breakdown of the getStaticProps method let's check our previous post getStaticProps.

Landing Page, but dynamic (getStaticProps + revalidate).

What happens if the landing page you are building is frequently changing the products based on user preferences? Then, you don't want to show the same page all the time.

In that case, you need to make sure that the generated pages will be updated at a regular interval.

With that in mind, you have to use the revalidate property. This property is defined in seconds to indicate the refresh interval.

export async function getStaticProps() {
    const url = 'https://our-api-products.com/';
    const res = await fetch(url, {
        headers: {
        Accept: 'application/json',
        },
    });
    const data = await res.json();

    return {
        props: {
            products: data.products,
        },
        revalidate: 20
    };
}

We gave our revalidate property the value of 20. It means we are still pre-generating our landing page, but it will be updated every 10 seconds. As we see in our previous post, this is called Incremental Static Regeneration.

Product detail (getStaticPaths)

Now, if for example, our customers click on one product, we have to show a details page of the product. We're calling a PI, like the product/?id=678.

What is interesting here, is we don't know which id we are looking for. So our server needs to know all of the valid ids ahead of time.

This is where getStaticPaths comes in. It tells the server all the available routes that we need to pre-generate.

export async function getStaticPaths() {
    const idList = getproducts().map(product => product.id)
    const params = idList.map(id => ({params: {productId: id}}))

    return {
        fallback: false,
        paths: params,
    };
}

export async function getStaticProps(context: GetStaticPropsContext) {
    const {params: { productId }} = context
    const productDetails = await getProductDetails(productId);

    return {
        props: { productDetails, }
    };
}

Now, only the pages with a valid id will be pre-generated.

Accounts Details (getServerSideProps)

Now, what if our customer wants to see her last purchases, or change her address or other personal info? After that, we need to validate some things.

Or, we want to make sure that the user has the right permissions to view the page. In these scenarios, we use getServerSideProps.

If you remember, in our previous post we talk about getServerSideProps, this option will not pre-generate any page. Instead, it will run the code inside the method every time the page is requested. Therefore this is more secure, but unfortunately less efficient.

export async function getServerSideProps() {
  const url = 'https://our-api-products.com/';
  const res = await fetch(url, {
    headers: {
      Accept: 'application/json',
    },
  });
  const data = await res.json();

  return {
    props: {
      products: data.products,
    },
  };
}

Conclusion

That's it. First, we saw different data-fetching options. Now, we see how to choose between them, in real-life scenarios probably we are facing every day.

Now you should have a little understanding of how we can use NextJS for improved performance and SEO-Friendly in your application.

So far, we learned:

  • If we had a page in which the content will be the same and will remain the same for a long time, it's better to use getStaticProps.

  • If we need a page whose content will be dynamic based on some conditions, it's better to use getStaticProps with a revalidating option.

  • On the other hand, if we want to build a page and be sure that the id items are all valid, it's more likely to use getStaticPaths.

  • But, if we need to make a secure but dynamic page, we will definitely need getServerSideProps.

This Dot is a consultancy dedicated to guiding companies through their modernization and digital transformation journeys. Specializing in replatforming, modernizing, and launching new initiatives, we stand out by taking true ownership of your engineering projects.

We love helping teams with projects that have missed their deadlines or helping keep your strategic digital initiatives on course. Check out our case studies and our clients that trust us with their engineering.

You might also like

Next.js + MongoDB Connection Storming cover image

Next.js + MongoDB Connection Storming

Building a Next.js application connected to MongoDB can feel like a match made in heaven. MongoDB stores all of its data as JSON objects, which don’t require transformation into JavaScript objects like relational SQL data does. However, when deploying your application to a serverless production environment such as Vercel, it is crucial to manage your database connections properly. If you encounter errors like these, you may be experiencing Connection Storming: * MongoServerSelectionError: connect ECONNREFUSED <IP_ADDRESS>:<PORT> * MongoNetworkError: failed to connect to server [<hostname>:<port>] on first connect * MongoTimeoutError: Server selection timed out after <x> ms * MongoTopologyClosedError: Topology is closed, please connect * Mongo Atlas: Connections % of configured limit has gone above 80 Connection storming occurs when your application has to mount a connection to Mongo for every serverless function or API endpoint call. Vercel executes your application’s code in a highly concurrent and isolated fashion. So, if you create new database connections on each request, your app might quickly exceed the connection limit of your database. We can leverage Vercel’s fluid compute model to keep our database connection objects warm across function invocations. Traditional serverless architecture was designed for quick, stateless web app transactions. Now, especially with the rise of LLM-oriented applications built with Next.js, interactions with applications are becoming more sequential. We just need to ensure that we assign our MongoDB connection to a global variable. Protip: Use global variables Vercel’s fluid compute model means all memory, including global constants like a MongoDB client, stays initialized between requests as long as the instance remains active. By assigning your MongoDB client to a global constant, you avoid redundant setup work and reduce the overhead of cold starts. This enables a more efficient approach to reusing connections for your application’s MongoDB client. The example below demonstrates how to retrieve an array of users from the users collection in MongoDB and either return them through an API request to /api/users or render them as an HTML list at the /users route. To support this, we initialize a global clientPromise variable that maintains the MongoDB connection across warm serverless executions, avoiding re-initialization on every request. ` Using this database connection in your API route code is easy: ` You can also use this database connection in your server-side rendered React components. ` In serverless environments like Vercel, managing database connections efficiently is key to avoiding connection storming. By reusing global variables and understanding the serverless execution model, you can ensure your Next.js app remains stable and performant....

The Future of Dates in JavaScript: Introducing Temporal cover image

The Future of Dates in JavaScript: Introducing Temporal

The Future of Dates in JavaScript: Introducing Temporal What is Temporaal? Temporal is a proposal currently at stage 3 of the TC39 process. It's expected to revolutionize how we handle dates in JavaScript, which has always been a challenging aspect of the language. But what does it mean that it's at stage 3 of the process? * The specification is complete * It has been reviewed * It's unlikely to change significantly at this point Key Features of Temporal Temporal introduces a new global object with a fresh API. Here are some important things to know about Temporal: 1. All Temporal objects are immutable 2. They're represented in local calendar systems, but can be converted 3. Time values use 24-hour clocks 4. Leap seconds aren't represented Why Do We Need Temporal? The current Date object in JavaScript has several limitations: * No support for time zones other than the user's local time and UTC * Date objects can be mutated * Unpredictable behavior * No support for calendars other than Gregorian * Daylight savings time issues While some of these have workarounds, not all can be fixed with the current Date implementation. Let's see some useful examples where Temporal will improve our lives: Some Examples Creating a day without a time zone is impossible using Date, it also adds time beyond the date. Temporal introduces PlainDate to overcome this. ` But what if we want to add timezone information? Then we have ZonedDateTime for this purpose. The timezone must be added in this case, as it also allows a lot of flexibility when creating dates. ` Temporal is very useful when manipulating and displaying the dates in different time zones. ` Let's try some more things that are currently difficult or lead to unexpected behavior using the Date object. Operations like adding days or minutes can lead to inconsistent results. However, Temporal makes these operations easier and consistent. ` Another interesting feature of Temporal is the concept of Duration, which is the difference between two time points. We can use these durations, along with dates, for arithmetic operations involving dates and times. Note that Durations are serialized using the ISO 8601 duration format ` Temporal Objects We've already seen some of the objects that Temporal exposes. Here's a more comprehensive list. * Temporal * Temporal.Duration` * Temporal.Instant * Temporal.Now * Temporal.PlainDate * Temporal.PlainDateTime * Temporal.PlainMonthDay * Temporal.PlainTime * Temporal.PlainYearMonth * Temporal.ZonedDateTime Try Temporal Today If you want to test Temporal now, there's a polyfill available. You can install it using: ` Note that this doesn't install a global Temporal object as expected in the final release, but it provides most of the Temporal implementation for testing purposes. Conclusion Working with dates in JavaScript has always been a bit of a mess. Between weird quirks in the Date object, juggling time zones, and trying to do simple things like “add a day,” it’s way too easy to introduce bugs. Temporal is finally fixing that. It gives us a clear, consistent, and powerful way to work with dates and times. If you’ve ever struggled with JavaScript dates (and who hasn’t?), Temporal is definitely worth checking out....

Understanding Next.js Data Fetching for Beginners cover image

Understanding Next.js Data Fetching for Beginners

When I started to learn Next.Js, I felt a little bit lost with some abbreviations they use to make the data fetching. All of them look pretty simmilar, but they have different meanings. If you feel the same, you are not alone. Let's discover what CSR, SSR, SSG, and ISR means, and how we can use them. It is quite confusing because I, as an Angular developer, only knew one strategy to fetch the data:HttpClient. But for example, if you are more familiar with React, you probably use a different strategy: useEffect. So, let's get started. Vercel's Next.js framework has four different data fetching options despite being known as a server-side render framework: - CSR - Client-Side Rendering is a common data fetching method using useEffect. It will fetch the data from the API every single page request on the client-side (after the page is rendered, then the function will run). - SSR - Server-Side Rendering will run a special function to fetch data from the API with every page request on the server-side (before the page is loaded, that special function will run first, creating a delay, then after that, it will serve the page). - SSG - Static Site Generation will run a special function to fetch data once when that page builds. - ISR – Incremental Static Regeneration offers a new method. Shortly put, it is a combination of SSG, and SSR, where it served statically, but at a certain time and certain condition, that page will rebuild and fetch the data from the API again. Don't worry if you don't understand any of the concepts. I will explain a little bit further shortly. CSR (Client-Side Rendering) This is a basic example of how you can use Client-Side Rendering. I'm using the well known useEffect method from React. ` You can see how it works on Stackblitz. We can highlight 2 things here: - The useEffect method to make the retrieve of the name every time the page loads. - A loading indicator, this is used to wait for the fetching data, because the data is fetched after the page is rendered. SSR (Server-Side Rendering) Just to keep in mind, when you are using Server-Side rendering, and using the method getServerSideProps(), Next.js will pre-render this page on each request using the data returned by getServerSideProps. ` You can see how it works: Stackblitz We had 3 highlights here: - Method getServerSideProps(), is a function indicating that a page is using Server-Side Rendering. - We had a delay before render, and NO loading indicator. The data is fetched before the page is rendered, so that's why we can see a little delay. - Data is fetched on every request, that's why every time we reload the page, is showing a different dad joke. SSG (Static Site Generator) If we take a look at the code below, we will see that it is pretty similar to the SSR code, but the difference here is the getStaticProps method. This method will only fetch when the app is building. ` The highlights here are: - getStaticProps is a method indicating that the page is using Static Site Generation. - Data will be fetched only when you run yarn build. The API will be hit only when the app is building. - Data will NOT change because there is no further fetch. You can see how it works: Stackblitz ISR (Incremental Static Regeneration) This strategy works pretty simmilar like SSG, but here we need to add a revalidate prop to our main getStaticProps function. ` There are a few keys to highlight here: - When you set the revalidate prop to certain amount of time (in our example we set it to 20), reloading doesn't trigger changes. The page will remain in a cooldown state. - Is the page rebuilt every 20 seconds then? No. When the cooldown state is off, if no one visits the page, it will not rebuild even after the 20s pass. But the first person who enters the page when the cooldown state is off will trigger a rebuild. However, that person will not see those changes. But the changes will be applied to the next full reload. You can see how it works: Stackblitz Conclusion Understand how these abbreviations work, and how we can use it in a Next.js app will help us develop more robust solutions, and use Next.js' full potential. I know these terms can sound overwhelming and difficult to understand, but with a little bit of practicing, we can see in action in a few steps. In future blog posts, we will see the best approach to identify and choose between them....

Vercel BotID: The Invisible Bot Protection You Needed cover image

Vercel BotID: The Invisible Bot Protection You Needed

Nowadays, bots do not act like “bots”. They can execute JavaScript, solve CAPTCHAs, and navigate as real users. Traditional defenses often fail to meet expectations or frustrate genuine users. That’s why Vercel created BotID, an invisible CAPTCHA that has real-time protections against sophisticated bots that help you protect your critical endpoints. In this blog post, we will explore why you should care about this new tool, how to set it up, its use cases, and some key considerations to take into account. We will be using Next.js for our examples, but please note that this tool is not tied to this framework alone; the only requirement is that your app is deployed and running on Vercel. Why Should You Care? Think about these scenarios: - Checkout flows are overwhelmed by scalpers - Signup forms inundated with fake registrations - API endpoints draining resources with malicious requests They all impact you and your users in a negative way. For example, when bots flood your checkout page, real customers are unable to complete their purchases, resulting in your business losing money and damaging customer trust. Fake signups clutter the app, slowing things down and making user data unreliable. When someone deliberately overloads your app’s API, it can crash or become unusable, making users angry and creating a significant issue for you, the owner. BotID automatically detects and filters bots attempting to perform any of the above actions without interfering with real users. How does it work? A lightweight first-party script quickly gathers a high set of browser & environment signals (this takes ~30ms, really fast so no worry about performance issues), packages them into an opaque token, and sends that token with protected requests via the rewritten challenge/proxy path + header; Vercel’s edge scores it, attaches a verdict, and checkBotId() function simply reads that verdict so your code can allow or block. We will see how this is implemented in a second! But first, let’s get started. Getting Started in Minutes 1. Install the SDK: ` 1. Configure redirects Wrap your next.config.ts with BotID’s helper. This sets up the right rewrites so BotID can do its job (and not get blocked by ad blockers, extensions, etc.): ` 2. Integrate the client on public-facing pages (where BotID runs checks): Declare which routes are protected so BotID can attach special headers when a real user triggers those routes. We need to create instrumentation-client.ts (place it in the root of your application or inside a src folder) and initialize BotID once: ` instrumentation-client.ts runs before the app hydrates, so it’s a perfect place for a global setup! If we have an inferior Next.js version than 15.3, then we would need to use a different approach. We need to render the React component inside the pages or layouts you want to protect, specifying the protected routes: ` 3. Verify requests on your server or API: ` - NOTE: checkBotId() will fail if the route wasn’t listed on the client, because the client is what attaches the special headers that let the edge classify the request! You’re all set - your routes are now protected! In development, checkBotId() function will always return isBot = false so you can build without friction. To disable this, you can override the options for development: ` What happens on a failed check? In our example above, if the check failed, we return a 403, but it is mostly up to you what to do in this case; the most common approaches for this scenario are: - Hard block with a 403 for obviously automated traffic (just what we did in the example above) - Soft fail (generic error/“try again”) when you want to be cautious. - Step-up (require login, email verification, or other business logic). Remember, although rare, false positives can occur, so it’s up to you to determine how you want to balance your fail strategy between security, UX, telemetry, and attacker behavior. checkBotId() So far, we have seen how to use the property isBot from checkBotId(), but there are a few more properties that you can leverage from it. There are: isHuman (boolean): true when BotID classifies the request as a real human session (i.e., a clear “pass”). BotID is designed to return an unambiguous yes/no, so you can gate actions easily. isBot (boolean): We already saw this one. It will be true when the request is classified as automated traffic. isVerifiedBot (boolean): Here comes a less obvious property. Vercel maintains and continuously updates a comprehensive directory of known legitimate bots from across the internet. This directory is regularly updated to include new legitimate services as they emerge. This could be helpful for allowlists or custom logic per bot. We will see an example in a sec. verifiedBotName? (string): The name for the specific verified bot (e.g., “claude-user”). verifiedBotCategory? (string): The type of the verified bot (e.g., “webhook”, “advertising”, “ai_assistant”). bypassed (boolean): it is true if the request skipped BotID check due to a configured Firewall bypass (custom or system). You could use this flag to avoid taking bot-based actions when you’ve explicitly bypassed protection. Handling Verified Bots - NOTE: Handling verified bots is available in botid@1.5.0 and above. It might be the case that you don’t want to block some verified bots because they are not causing damage to you or your users, as it can sometimes be the case for AI-related bots that fetch your site to give information to a user. We can use the properties related to verified bots from checkBotId() to handle these scenarios: ` Choosing your BotID mode When leveraging BotID, you can choose between 2 modes: - Basic Mode: Instant session-based protection, available for all Vercel plans. - Deep Analysis Mode: Enhanced Kasada-powered detection, only available for Pro and Enterprise plan users. Using this mode, you will leverage a more advanced detection and will block the hardest to catch bots To specify the mode you want, you must do so in both the client and the server. This is important because if either of the two does not match, the verification will fail! ` Conclusion Stop chasing bots - let BotID handle them for you! Bots are and will get smarter and more sophisticated. BotID gives you a simple way to push back without slowing your customers down. It is simple to install, customize, and use. Stronger protection equals fewer headaches. Add BotID, ship with confidence, and let the bots trample into a wall without knowing what’s going on....

Let's innovate together!

We're ready to be your trusted technical partners in your digital innovation journey.

Whether it's modernization or custom software solutions, our team of experts can guide you through best practices and how to build scalable, performant software that lasts.

Prefer email? hi@thisdot.co