3

I want to display the latest video from a YouTube channel on a website. The channel uploads at maximum once per day, so I'm caching the response of my API route for 1 day (86400 seconds) in my vercel.json like so:

{
    "headers": [
        {
            "source": "/(.*)",
            "headers": [
                {
                    "key": "access-control-allow-origin",
                    "value": "*"
                },
                {
                    "key": "Cache-Control",
                    "value": "s-maxage=86400"
                }
            ]
        }
    ]
}

I want to use getStaticProps with incremental static regeneration so that my API route only gets sent requests at most once per day, but I'm not sure how to write the request to my API route.

The Next.js docs say:

Note: You should not use fetch() to call an API route in getStaticProps. Instead, directly import the logic used inside your API route. You may need to slightly refactor your code for this approach.

Fetching from an external API is fine!

What does this mean? Is my current method of writing my request wrong?

// /pages/index.js

import Header from '../components/header/header'
import MainContent from '../components/main-content/main-content'
import Footer from '../components/footer/footer'

export default function Index({ videoTitle, videoURL, videoThumbnailData }) {
    return (
        <>
            <Header />

            <MainContent
                videoTitle={videoTitle}
                videoURL={videoURL}
                videoThumbnailData={videoThumbnailData}
            />

            <Footer />
        </>
    )
}

// Called at build time, and response revalidated after 1 day (86400 seconds)
// since internal API response is cached on Vercel Edge network for 1 day (see /pages/api/get-latest-video.js)
export async function getStaticProps() {
    // Fetch YouTube videos from internal API route /pages/api/get-latest-video.js
    const res = await fetch(`${process.env.API_ROUTES_URL}/api/get-latest-video`)
    const data = await res.json()

    // Returned as props to page
    return {
        props: {
            videoTitle: data.videoTitle,
            videoURL: data.videoURL,
            videoThumbnailData: data.videoThumbnailData
        },
        revalidate: 86400
    }
}
// /components/main-content/main-content.js

import Section from './section'
import Image from 'next/image'

export default function MainContent({ videoTitle, videoURL, videoThumbnailData }) {
    return (
        <main>
            <Section>
                <a href={videoURL}>
                    {videoTitle}
                </a>
                <Image
                    src={videoThumbnailData.url}
                    width={videoThumbnailData.width}
                    height={videoThumbnailData.height}
                />
            </Section>
        </main>
    )
}
Aryan Beezadhur
  • 4,503
  • 4
  • 21
  • 42

2 Answers2

2

Your request /api/get-latest-video is supposed to be send from the browser to the server, then the server probably has some kind of a route handler like:

routeMatches('/api/get-latest-video', ( req, res )=>{
    requestDB('latestVideos').then( latestVideos => {
        respondWithLatestVideos( req, res, latestVideos );
    })
});

Now, getStaticProps runs on the server side. So you can request your DB directly inside getStaticProps, instead of sending a request to the server which requests the DB.

export async function getStaticProps() {
    // Fetch YouTube videos from internal API route /pages/api/get-latest-video.js
    // const res = await fetch(`${process.env.API_ROUTES_URL}/api/get-latest-video`)
    // const data = await res.json()
    const data = await requestDB('latestVideos')
    ...
}

There is a slightly more informative note "write server side code directly" further down on the same NextJs docs page:

Note that getStaticProps runs only on the server-side. It will never be run on the client-side. It won’t even be included in the JS bundle for the browser. That means you can write code such as direct database queries without them being sent to browsers. You should not fetch an API route from getStaticProps — instead, you can write the server-side code directly in getStaticProps.

kca
  • 4,856
  • 1
  • 20
  • 41
-1

That means you just have to import your API function into getStaticProps instead of using Fetch.

// /pages/index.js

import Header from "../components/header/header";
import MainContent from "../components/main-content/main-content";
import Footer from "../components/footer/footer";

export default function Index({ videoTitle, videoURL, videoThumbnailData }) {
  return (
    <>
      <Header />

      <MainContent
        videoTitle={videoTitle}
        videoURL={videoURL}
        videoThumbnailData={videoThumbnailData}
      />

      <Footer />
    </>
  );
}

// Called at build time, and response revalidated after 1 day (86400 seconds)
// since internal API response is cached on Vercel Edge network for 1 day (see /pages/api/get-latest-video.js)
export async function getStaticProps() {
  // Fetch YouTube videos from internal API route /pages/api/get-latest-video.js
  const res = await getApiData(); // import your api function here
  const data = await res.json();

  // Returned as props to page
  return {
    props: {
      videoTitle: data.videoTitle,
      videoURL: data.videoURL,
      videoThumbnailData: data.videoThumbnailData,
    },
    revalidate: 86400,
  };
}

Aryan Beezadhur
  • 4,503
  • 4
  • 21
  • 42
enoch
  • 2,587
  • 2
  • 15
  • 22