NashTech Insights

Eager Loading in Web Technology

Em Ha Tuan
Em Ha Tuan
Table of Contents
Eager Loading

In the ever-evolving landscape of web development, optimizing the performance of web applications is paramount. One key strategy that plays a crucial role in enhancing efficiency is Eager Loading. Let’s explore how it works and why it’s a game-changer in web technology.

1. What is Eager Loading?

Eager Loading is a technique that enhances data retrieval in web applications by fetching associated or dependent data upfront rather than on demand. It’s a proactive approach to loading data that anticipates the application’s needs and fetches related information in advance.

2. Why is it necessary to load patterns?

N + 1

The answer is N + 1. Eager Loading becomes particularly relevant when dealing with databases and object-relational mapping (ORM) systems. Consider a scenario where a web application needs to display a list of items, each associated with additional details or related entities. A common problem known as the N+1 query issue can arise without it. The N+1 query problem involves fetching a collection of entities (the “N” entities) and then making separate queries for each related entity. This can result in many database queries, leading to performance bottlenecks. Eager Loading steps in as the solution by retrieving all the necessary data in a single query, mitigating the N+1 problem and improving the overall performance of the web application.

Not only related to databases, it extends its influence to various aspects of web technology. One notable application is loading assets, such as images, scripts, and stylesheets, within HTML documents.

3. Advantages of Eager Loading

Eager loading offers several advantages as a performance optimization technique in specific contexts. Here are some of the key benefits:

  • Improved Performance:
    • Reduced Latency: Eager loading minimizes the delay associated with lazy loading. By fetching data or resources in advance, eager loading contributes to faster response times and a more responsive user experience.
  • Predictable Loading Behavior:
    • Consistent User Experience: Eager loading ensures a consistent user experience, especially when visibility or access to certain data is predictable. Users encounter a smoother flow within the application, as resources are readily available when required.
  • Optimized User Interactions:
    • Quick Access to Data: In situations where user interactions are dynamic and unpredictable, eager loading can provide quick access to related data. This is particularly beneficial for applications that rely on dynamic content or involve frequent user interactions.
  • Bandwidth Efficiency:
    • Reduced Round-Trips: Eager loading reduces the number of round-trip requests between the client and server. By fetching multiple pieces of data in a single request, it minimizes the overhead associated with multiple requests, making more efficient use of available bandwidth.
  • Better Resource Utilization:
    • Efficient Data Retrieval: It can lead to more efficient data retrieval by fetching all necessary information in a single query. This can be advantageous in scenarios where the cost of making individual queries outweighs the benefit of lazy loading.
  • Enhanced User Satisfaction:
    • Faster Page Load Times: It contributes to faster initial page load times, crucial for user satisfaction. Users are more likely to engage with a web application that responds quickly to their actions and provides a seamless experience.
  • Optimal for Critical Resources:
    • Prioritizing Important Content: Eager loading is effective for prioritizing the loading of critical resources. For example, critical images or scripts essential for rendering the main content of a page can be eagerly loaded to ensure a swift and complete initial presentation.
  • Simplified Client-Side Logic:
    • Reduced Client-Side Processing: This can simplify client-side logic by providing the necessary data upfront. This can lead to cleaner and more straightforward client-side code, as there is less need for complex asynchronous handling of resource loading.
  • Addressing N+1 Query Problem:
    • Mitigation of Performance Issues: In database contexts, eager loading addresses the N+1 query problem by fetching related data in advance, reducing the number of queries and mitigating performance issues associated with lazy loading in a loop.

4. How does it work in the real world?

Let’s create some examples to see how this works.

Example 1: Query the database with Eager Loading patterns.

In this example, I have created an application with Next.js 14 to display a list of posts and comments related by postId.

1. Create a Card component:

// app/components/Card/Card.jsx
import Image from "next/image";

export default function Card({ post }) {
  return (
    <div className="border rounded-lg p-4 flex flex-col gap-2">
      <div className="shadow-sm w-full h-80 relative">
        <Image
          src={post.image}
          alt={post.title}
          sizes="(max-width: 768px) 100vw, 33vw"
          loading="eager"
          className="rounded-lg absolute"
          style={{
            width: "100%",
            objectFit: "cover",
          }}
          fill
        />
      </div>
      <h2 className="text-2xl">{post.title}</h2>
      <div className="text-sm">{post.content}</div>
      <h3 className="text-xl">Comments:</h3>
      <hr className="py-1" />
      <div className="flex flex-col text-xs gap-2">
        {post.comments.length === 0 && (
          <span className="border rounded-lg px-3 py-2">Zero comments.</span>
        )}
        {post.comments.map((c) => (
          <span key={c.id} className="border rounded-lg px-3 py-2">
            {c.content}
          </span>
        ))}
      </div>
    </div>
  );
}

2. Create an action to make a query to get the list of posts and comments:

// app/actions/get-posts-eager-loading.js
export const getPostsEagerLoading = async () => {
  // Create a fake posts table.
  const posts = [
    {
      id: 1,
      title: "Post 1",
      image: "https://picsum.photos/1081",
      content: "Lorem ipsum dolor sit amet ...",
      dateCreated: "2023-10-01",
    },
    ...
  ];

  // Create a fake comments table.
  const comments = [
    {
      id: 1,
      postId: 1,
      content: "Lorem ipsum dolor sit amet.",
      dateCreated: "2023-10-01",
    },
    ...
  ];

  // Create a fake query to get the list of post.
  const fetchData = () =>
    new Promise((resolve, reject) => {
      try {
        setTimeout(() => {
          const data = posts.map((post) => {
            return {
              ...post,
              comments: comments.filter((c) => c.postId === post.id),
            };
          });
          return resolve(data);
        }, 2000); // Assuming that the query is completed in 2s.
      } catch (error) {
        return reject(error);
      }
    });

  const data = await fetchData();
  return data;
};

3. Create a new page to display the list of posts and comments.

// app/eager-loading/page.js
import { getPostsEagerLoading } from "../actions/get-posts-eager-loading";
import Card from "../components/Card/Card";

const EagerLoadingPage = async () => {
  const posts = await getPostsEagerLoading();
  return (
    <div>
      <h1 className="text-3xl">Eager Loading</h1>
      <div className="py-6 w-[50%]">
        <div className="grid grid-cols-1 gap-4">
          {posts.map((post) => (
            <Card key={post.id} post={post} />
          ))}
        </div>
      </div>
    </div>
  );
};

export default EagerLoadingPage;

Now, let’s look at the log in the terminal:

Eager Loading - Query executed

The page only executes one query to get the list of posts from the database. What’s the benefit of this? If I use separate queries on this page, like querying only the list of posts and then, once we have the data of the list of posts, continuing to query the comments table to get related data with postId, it would take 2 queries to get all necessary data. Now, imagine if we have 1000 user requests to this page. We would take 2000 queries to the database instead of 1000 queries. That would lead us to a bottleneck issue if the number of requests per user increases – maybe. The Eager Loading Pattern ensures that all the data and related data are retrieved at once and delivered to the user, reducing the query execution time or IOPS in the database, saving costs, and avoiding bottlenecks.

Example 2: Use the Eager Loading Pattern for loading image assets.

Back to the Card component, we can see that the Card image (<img />) has the ‘loading’ attribute set to ‘eager.’ But what does this mean? In HTML, the <img> loading attribute has two options: eager and lazy. The default value is eager. Eager loading instructs the browser to load the image after processing the <img> element. This means the image will load and display immediately after the page loads. It also loads related entities, even if the visitor never scrolls down. You can see the image below, which I captured in the list of loaded images in the Network tab of Chrome Dev Tools:

Loaded images

If I set loading=’lazy’, what will happen? The answer is the image will only be loaded when it is about to come into the user’s viewport. That is another Data Fetching Pattern – the Lazy Loading Pattern.

5. Disadvantages of Eager Loading

While Eager Loading can be a powerful optimization strategy, it comes with its own set of disadvantages. Here are some common drawbacks:

  • Increased Initial Load Time: That can lead to increased page load times, especially when dealing with large amounts of data or resources. Fetching all related data upfront means more data must be transmitted over the network, potentially slowing down the initial rendering of the page.
  • Over-fetching of Data: Eager loading may result in fetching more data than is needed. This can lead to unnecessary resource consumption, especially if a significant portion of the eagerly loaded data is not used during the user’s session.
  • Resource Utilization: This consumes more server and network resources than lazy loading. While it can improve user experience by reducing latency for certain interactions, it might not be the most efficient choice for all scenarios, especially in environments with limited bandwidth.
  • Impact on Scalability: In applications with many users or requests, eager loading can significantly impact scalability. The server may need to handle a higher load due to the increased requests for eagerly loaded resources.
  • Complexity of Implementation: This often requires careful consideration and planning, particularly in complex systems. It may involve optimizing queries, managing relationships between entities, and ensuring that the application’s architecture can support the increased load associated with eager loading.
  • Potential for Outdated Data: If the eagerly loaded data is not frequently updated or changes dynamically, there’s a risk that users might be presented with outdated information, as the data was loaded in advance.
  • Limited Flexibility: It might not be suitable for all scenarios or user interactions. In some cases, eager and lazy loading strategies might be needed to balance performance and resource efficiency.

Conclusion

Eager Loading is a proactive data retrieval technique. In web applications, developers use eager loading to fetch associated or dependent data upfront. Above all, it addresses the N+1 query problem in databases. Additionally, it optimizes asset loading, prioritizing critical resources for a faster and more responsive user experience.
That is recommended for scenarios where performance optimization is critical, such as displaying collections with associated details. However, careful consideration is needed to balance its advantages against potential drawbacks, and it may not be suitable for all use cases.

Thank you for reading.

Em Ha Tuan

Em Ha Tuan

Leave a Comment

Your email address will not be published. Required fields are marked *

Suggested Article

%d