Comprehensive Guide to Data Fetching in Next.js

Comparing fetch, axios, and SWR for optimal performance

September 3, 2024

By Sohrab Saboori

Mastering Data Fetching in Next.js With Fetch, Axios, and SWR

In the world of web development, fetching data efficiently is crucial for building responsive, user-friendly applications. In Next.js, there are multiple ways to fetch data, each with its own set of advantages and drawbacks. In this guide, we’ll dive deep into three popular methods: fetch, axios, and SWR, discussing when and how to use each, along with their pros, cons, and best practices.

Fetching Data With Fetch

The fetch API is a native JavaScript function that provides a way to make HTTP requests. It’s a low-level API with a minimalistic design, making it a great choice for straightforward tasks.

Pros:

  • Native Support: Built into JavaScript, no need for additional libraries.
  • Flexibility: Offers granular control over HTTP requests, making it suitable for complex requests.
  • Lightweight: No added dependencies, reducing the overall bundle size.

Cons:

  • Manual Parsing: fetch returns a Response object, and you need to call .json() to parse the response body into a JavaScript object.
  • No Automatic Error Handling: fetch only rejects the promise if there’s a network error. HTTP errors (like 404 or 500) don’t automatically reject the promise, so you must manually check response.ok.
  • Verbose Configuration: Setting up headers, request bodies, and handling different HTTP methods can be verbose and repetitive.

Best Practices:

  • Use fetch for Server-Side Data Fetching: Since fetch is a native API, it's a good choice for server-side operations, especially in API routes or within getServerSideProps or getStaticProps.
  • Check response.ok: Always check response.ok to handle HTTP errors properly.
  • Consider Performance: Since fetch doesn't have built-in features like automatic retries or interceptors, consider these limitations for highly complex requests.
import type { NextApiRequest, NextApiResponse } from "next";

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  const apiKey = process.env.SECURE_API_KEY; // Use environment variable for sensitive data

  try {
    const response = await fetch("https://reqres.in/api/users?page=2", {
      headers: {
        Authorization: `Bearer ${apiKey}`,
      },
    });

    if (!response.ok) {
      throw new Error(`Failed to fetch data: ${response.statusText}`);
    }

    const data = await response.json();
    res.status(200).json(data);
  } catch (error: any) {
    res.status(500).json({ error: error.message });
  }
}

Fetching Data With Axios

axios is a popular third-party HTTP client library that simplifies the process of making HTTP requests. It offers additional features and a more intuitive API compared to fetch.

Pros:

  • Automatic JSON Parsing: axios automatically parses JSON responses, so you don't need to call .json().
  • Automatic Error Handling: axios automatically rejects the promise for HTTP errors (non-2xx responses), simplifying error handling.
  • Simpler Configuration: Provides a more intuitive API for setting up headers, query parameters, and other request options.
  • Interceptors: axios supports request and response interceptors, allowing you to modify requests or responses globally.

Cons:

  • Additional Dependency: Requires installation of the axios library, adding to your project's bundle size.
  • Slight Overhead: While minimal, axios introduces additional overhead compared to the native fetch API.

Best Practices:

  • Use axios for Client-Side and Server-Side Requests: Its simplicity and features make axios a good choice for both front-end and back-end data fetching.
  • Leverage Interceptors: Use interceptors for tasks like adding authentication tokens to requests or logging responses and errors.
  • Automatic Error Handling: Take advantage of axios's automatic rejection of non-2xx responses to reduce the need for manual error checks.
import { useEffect, useState } from "react";
import axios from "axios";

interface Resource {
  id: number;
  name: string;
  year: number;
  color: string;
  pantone_value: string;
}

const AxiosExample = () => {
  const [data, setData] = useState<Resource[]>([]);
  const [loading, setLoading] = useState<boolean>(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await axios.get("https://reqres.in/api/unknown");

        setData(response.data.data);
      } catch (err) {
        setError((err as Error).message);
      } finally {
        setLoading(false);
      }
    };

    fetchData();
  }, []);

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error}</p>;

  return (
    <div>
      <h1>Axios Example</h1>
      <ul>
        {data.map((resource) => (
          <li key={resource.id} style={{ backgroundColor: resource.color }}>
            {resource.name} ({resource.year}) - Pantone:{" "}
            {resource.pantone_value}
          </li>
        ))}
      </ul>
    </div>
  );
};

export default AxiosExample;

Fetching Data With SWR

SWR (stale-while-revalidate) is a React hook library for data fetching developed by Vercel, the team behind Next.js. It’s designed for client-side data fetching with caching and revalidation features.

Pros:

  • Caching: SWR automatically caches the data from API requests and reuses the cache on subsequent renders or requests.
  • Revalidation: Automatically revalidates data in the background, ensuring the UI is always up-to-date.
  • Optimistic Updates: Allows the UI to update optimistically before the API call is confirmed, providing a smoother user experience.
  • Built-in Pagination and Infinite Loading: SWR simplifies implementing pagination and infinite loading, which can be complex with fetch or axios.
  • Automatic Retry: SWR can automatically retry failed requests, reducing the need for custom retry logic.

Cons:

  • Client-Side Only: SWR is designed to run on the client side, so it’s not suitable for server-side rendering scenarios where you need data before rendering the page.
  • Additional Dependency: Like axios, SWR is a third-party library, adding to your project's dependencies.

Best Practices:

  • Use SWR for Client-Side Fetching: Perfect for front-end scenarios where you need features like caching, revalidation, and optimistic updates.
  • Handle Dynamic Content: SWR excels in scenarios with frequently changing or user-specific data.
  • Combine with API Routes: Use SWR in conjunction with Next.js API routes for secure and efficient data fetching.
import Image from "next/image";
import useSWR from "swr";

const fetcher = (url: string) => fetch(url).then((res) => res.json());

const SecureFetchExample = () => {
  const { data, error, isLoading } = useSWR("/api/secure-data", fetcher);

  if (isLoading) return <p>Loading...</p>;
  if (error) return <p>Error: {error.message}</p>;

  return (
    <div>
      <h1>Secure Fetch Example with SWR</h1>
      <ul>
        {data.data.map((resource: any) => (
          <li key={resource.id}>
            <Image
              src={resource.avatar}
              width={50}
              height={50}
              alt={resource.first_name}
            />
            {resource.email} ({resource.id}) - Pantone: {resource.pantone_value}
          </li>
        ))}
      </ul>
    </div>
  );
};

export default SecureFetchExample;

When to Use Each Method

  • Server-Side with fetch: Use for secure API calls in API routes or when fetching dynamic data that needs to be up-to-date on every request (getServerSideProps).
  • Server-Side with axios: Ideal for more complex API calls with easier configuration and automatic error handling, used in both getServerSideProps and getStaticProps.
  • Client-Side with SWR: Best for fetching data on the front end with caching, revalidation, and real-time updates. Ideal for user-specific or frequently changing data.

Best Practices for Server-Side vs. Client-Side Data Fetching

  • Server-Side Fetching:
    • Use getServerSideProps for Dynamic Content: Fetch data on each request for content that needs to be up-to-date.
    • Use getStaticProps for Static Content: Ideal for content that doesn't change often, improving performance with static generation.
    • Secure API Calls: Use API routes with fetch to keep sensitive data like API keys secure.
  • Client-Side Fetching:
    • Leverage SWR for Dynamic or User-Specific Data: Use SWR to handle client-side data that needs to be revalidated or cached.
    • Handle Real-Time Updates: Use SWR for scenarios where the data changes frequently and you need to keep the UI updated.

Final Thoughts on Data Fetching in Next.js

Understanding when to use fetch, axios, and SWR in your Next.js applications can significantly enhance both the performance and user experience of your projects. By following the best practices outlined in this guide, you can ensure that your data fetching strategy is secure, efficient, and perfectly suited to your application's needs.

References



Sohrab

Sohrab Saboori

Senior Full-Stack Developer

Sohrab is a Senior Front-End Developer with extensive experience in React, Next.js, JavaScript, and TypeScript. Sohrab is committed to delivering outstanding digital solutions that not only meet but exceed clients' expectations. His expertise in building scalable and efficient web applications, responsive websites, and e-commerce platforms is unparalleled. Sohrab has a keen eye for detail and a passion for creating seamless user experiences. He is a problem-solver at heart and enjoys working with clients to find innovative solutions to their digital needs. When he's not coding, you can find him lifting weights at the gym, pounding the pavement on the run, exploring the great outdoors, or trying new restaurants and cuisines. Sohrab believes in a healthy and balanced lifestyle and finds that these activities help fuel his creativity and problem-solving skills.