#App Router - nested fetch calls in SSG

1 messages · Page 1 of 1 (latest)

worthy mountain
#

I'm building an app where I get topics you learn at school to make a page for each topic, and then for each topic I get the resources from the resources table. I'm using Flask on the BE. I want to use the topic id that I get by fetching the topic, and then put it back into the fetch request.

How do I do this given my current code below? I already have a flask route. Remember I am using the App router as recommended by next.js since version 13, and this code is currently 100% correct:

// /topic/[topicName]/page.tsx
import React from 'react';
// This import will be used for SEO optimisation
import { Metadata, ResolvingMetadata } from 'next';
import DOMPurify from 'isomorphic-dompurify';

interface Params {
topicName: string;
}

interface Topic {
id: string;
name: string;
latitude: number;
longitude: number;
description: string;
}

export async function generateStaticParams() {
const topics = await fetch(${process.env.NEXT_PUBLIC_API_BASE_URL}/topics).then(res => res.json())

return topics.map((topic: Topic) => ({
  topicName: encodeURIComponent(topic.name),
}))

}

const TopicPage = async ({ params }: { params: Params }) => {
const { topicName } = params;
const decodedTopicName = decodeURIComponent(topicName);
const topic = await fetch(${process.env.NEXT_PUBLIC_API_BASE_URL}/topics/${decodedTopicName}).then(res => res.json())
// Sanitize the description HTML content
const sanitizedDescription = DOMPurify.sanitize(topic.description);

return (
// sidebar
<div className="absolute bottom-0 left-0 sm:left-0 sm:bottom-auto sm:top-0 sidenav w-full sm:w-2/5 md:w-1/3 xl:w-1/4 h-2/5 sm:h-full p-4 overflow-y-auto bg-white shadow-xl rounded-t-md">
<h1 className="text-xl font-bold">{decodedTopicName}</h1>
<div dangerouslySetInnerHTML={{ __html: sanitizedDescription }} />
{/* More content */}
</div>
);
};

export default TopicPage;

waxen sierraBOT
#

Hi @worthy mountain. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:

#

Based on the extracts provided, there isn't a direct example that matches the specific scenario of nested fetch calls within the App Router context for Static Site Generation (SSG) in Next.js. However, I can guide you on how to approach this based on the principles and examples given in the extracts.

To fetch data for each topic and then fetch resources for each topic using the topic ID, you would typically follow these steps:

  1. Use generateStaticParams to fetch the initial list of topics. This part you've already implemented.
  2. For fetching additional data based on each topic's ID (like resources related to each topic), you would perform this operation inside your page component or a server component if you need to fetch data on the server side.

Given the structure of your current code and the information from the extracts, here's a conceptual approach to extend your TopicPage component to include fetching resources for each topic. Note that this is a conceptual guide as the exact implementation for nested fetch calls wasn't directly covered in the extracts:

// Assuming you have a Flask endpoint like `/topics/{topicId}/resources` to get resources for a topic

const TopicPage = async ({ params }: { params: Params }) => {
  const { topicName } = params;
  const decodedTopicName = decodeURIComponent(topicName);
  
  // Fetch the topic data
  const topic = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/topics/${decodedTopicName}`).then(res => res.json());
  
  // Now, use the topic.id to fetch resources for this topic
  const resources = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/topics/${topic.id}/resources`).then(res => res.json());

  // Sanitize the description HTML content
  const sanitizedDescription = DOMPurify.sanitize(topic.description);
#

// Render your component with both topic and resources data
return (
<div>
{/* Your component structure /}
<div dangerouslySetInnerHTML={{ __html: sanitizedDescription }} />
{/
Render resources */}
</div>
);
};


This approach assumes you have a RESTful endpoint to fetch resources by topic ID. The key here is chaining your fetch calls: first, to get the topic details, and then using the `id` from the topic to fetch related resources.

Remember, this is a simplified and conceptual approach. Depending on your application's structure, you might need to adjust error handling, loading states, and possibly use `getStaticProps` or `getServerSideProps` for additional data fetching needs, especially if you're dealing with dynamic routes.

For more detailed guidance on data fetching in Next.js, especially with the new App Router, I recommend reviewing the official Next.js documentation on **Data Fetching** (<https://nextjs.org/docs/app/building-your-application/data-fetching>) and **Dynamic Routes** (<https://nextjs.org/docs/app/building-your-application/routing/dynamic-routes>).
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------
#

@worthy mountain kapa.ai is still learning and improving, please let us know how it did by reacting below

worthy mountain
#

@waxen sierra and then how would I display a simple section for each resource in the array of returned resources in my html? If relevant, you can remember that I'm using typescript.

waxen sierraBOT
#

Let me check the knowledge base for you. One moment! :books:

#

Based on the information provided in the extracts, while there isn't a direct example of rendering an array of items fetched from an API in TypeScript with Next.js, I can guide you on a general approach to display a simple section for each resource in the array of returned resources, leveraging TypeScript for type safety. This approach is consistent with React and TypeScript practices, which are applicable in a Next.js context.

First, you would define a TypeScript interface for the structure of your resources to ensure type safety:

interface Resource {
  id: string;
  name: string;
  // Add other resource properties here
}

Assuming you have fetched an array of resources as shown in the conceptual guide in the previous answer, you can then map over this array to render a section for each resource. Here's how you might extend the TopicPage component to include rendering resources:

import React from 'react';
import DOMPurify from 'isomorphic-dompurify';

interface Params {
  topicName: string;
}

interface Topic {
  id: string;
  name: string;
  latitude: number;
  longitude: number;
  description: string;
}

interface Resource {
  id: string;
  name: string;
  // Define other properties of Resource here
}

const TopicPage: React.FC<{ params: Params }> = async ({ params }) => {
  const { topicName } = params;
  const decodedTopicName = decodeURIComponent(topicName);
  
  // Fetch the topic data
  const topic = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/topics/${decodedTopicName}`).then(res => res.json());
  
  // Fetch resources for this topic
  const resources: Resource[] = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/topics/${topic.id}/resources`).then(res => res.json());

  // Sanitize the description HTML content
  const sanitizedDescription = DOMPurify.sanitize(topic.description);
#

return (
<div>
<div dangerouslySetInnerHTML={{ __html: sanitizedDescription }} />
{resources.map((resource) => (
<section key={resource.id}>
<h2>{resource.name}</h2>
{/* Render other resource details here */}
</section>
))}
</div>
);
};

export default TopicPage;


In this example, `resources.map((resource) => (...))` is used to iterate over each resource in the `resources` array, rendering a `<section>` for each. The `key` prop is essential for helping React identify which items have changed, are added, or are removed, and should be a unique identifier among siblings.

This approach assumes you're familiar with React's rendering of lists and TypeScript interfaces. It's a common pattern for rendering dynamic data fetched from an API in a React (and by extension, Next.js) application.

For more detailed examples and best practices, the official Next.js documentation on **Data Fetching** (<https://nextjs.org/docs/app/building-your-application/data-fetching>) and the React documentation on **Lists and Keys** (<https://reactjs.org/docs/lists-and-keys.html>) are excellent resources.
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------
#

@worthy mountain kapa.ai is still learning and improving, please let us know how it did by reacting below

worthy mountain
#

@waxen sierra so actually this is my resources model:

class Resource(db.Model): # type: ignore
tablename = "resources"

id = db.Column(db.UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
topic_id = db.Column(
    db.UUID(as_uuid=True), db.ForeignKey("topics.id"), nullable=False
)
user_id = db.Column(db.UUID(as_uuid=True), db.ForeignKey("users.id"), nullable=False)
description = db.Column(db.Text, nullable=False)
upvotes = db.Column(db.Integer, nullable=False, default=0)
downvotes = db.Column(db.Integer, nullable=False, default=0)
url = db.Column(db.String, nullable=False)
inserted = db.Column(
    db.DateTime(timezone=True), server_default=func.now(), nullable=False
)
updated = db.Column(db.DateTime(timezone=True), onupdate=func.now(), nullable=True)

# Serialize method
def serialize(self) -> Dict[str, str]:
    return {
        "id": str(self.id),
        "topic_id": str(self.topic_id),
        "user_id": str(self.user_id),
        "description": self.description,
        "upvotes": self.upvotes,
        "downvotes": self.downvotes,
        "url": self.url,
        "inserted": self.inserted.isoformat() if self.inserted else None,
        "updated": self.updated.isoformat() if self.updated else None,
    }
waxen sierraBOT
#

Let me check the knowledge base for you. One moment! :books:

#

Given the detailed structure of your Resource model, you can extend the TypeScript interface for Resource to match this structure. This will ensure type safety when you work with the resources in your Next.js application. Here's how you can define the TypeScript interface based on the model you provided:

interface Resource {
  id: string;
  topic_id: string;
  user_id: string;
  description: string;
  upvotes: number;
  downvotes: number;
  url: string;
  inserted: string | null;
  updated: string | null;
}

With this interface in place, you can now safely access the properties of each resource in your React component. For example, if you want to display the description, upvotes, downvotes, and URL for each resource, you can modify the rendering logic in your TopicPage component like so:

// Inside your TopicPage component

return (
  <div>
    <div dangerouslySetInnerHTML={{ __html: sanitizedDescription }} />
    {resources.map((resource) => (
      <section key={resource.id}>
        <h2>Description</h2>
        <p>{resource.description}</p>
        <p>Upvotes: {resource.upvotes}</p>
        <p>Downvotes: {resource.downvotes}</p>
        <a href={resource.url} target="_blank" rel="noopener noreferrer">Resource Link</a>
        {/* Display other resource details as needed */}
      </section>
    ))}
  </div>
);

This example assumes that you have fetched the resources as an array of Resource objects and that each object adheres to the TypeScript interface defined above. The map function iterates over each resource, creating a <section> for it. Inside each section, you can display the resource's description, upvotes, downvotes, and a link to the resource.

#

Remember, the dangerouslySetInnerHTML attribute is used to render HTML directly from a string. It's important to ensure that the content is sanitized to prevent XSS attacks, which you've already addressed with DOMPurify.

This approach allows you to dynamically render content based on the structure of your backend model, leveraging TypeScript for type safety and ensuring that your frontend and backend are aligned in terms of data structure.

For more information on rendering lists in React, you can refer to the React documentation on Lists and Keys (https://reactjs.org/docs/lists-and-keys.html). Unfortunately, I can't provide direct links to specific Next.js documentation that wasn't listed in the extracts provided.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: