Next.js Example

Data Fetching

Learn different methods of fetching data in Next.js

Client-side Fetching

Fetching data on the client using SWR or React Query

Using SWR

import useSWR from 'swr'

function Profile() {
  const { data, error, isLoading } = useSWR('/api/user', fetcher)

  if (error) return 
Failed to load
if (isLoading) return
Loading...
return (

Hello {data.name}!

Role: {data.role}

) } // Global SWR configuration export const SWRConfig = { fetcher: (url) => fetch(url).then((res) => res.json()), revalidateOnFocus: false, revalidateOnReconnect: true }

Using React Query

import { useQuery } from '@tanstack/react-query'

function TodoList() {
  const { data, isLoading, error } = useQuery({
    queryKey: ['todos'],
    queryFn: () => fetch('/api/todos').then((res) => res.json())
  })

  if (isLoading) return 
Loading todos...
if (error) return
Error: {error.message}
return (
    {data.map((todo) => (
  • {todo.title}
  • ))}
) }

Key Points

  • Automatic caching and revalidation
  • Real-time data updates
  • Optimistic UI updates
  • Error handling and loading states

Server-side Rendering

Fetching data at request time

getServerSideProps

// pages/dashboard.tsx
export default function Dashboard({ user, analytics }) {
  return (
    

Welcome back, {user.name}

Views: {analytics.views}

Conversions: {analytics.conversions}

) } export async function getServerSideProps(context) { // Check authentication const session = await getSession(context) if (!session) { return { redirect: { destination: '/login', permanent: false, }, } } // Fetch data const user = await fetchUser(session.userId) const analytics = await fetchAnalytics(user.id) return { props: { user, analytics, }, } }

API Routes

// pages/api/analytics.ts
import { NextApiRequest, NextApiResponse } from 'next'
import { getSession } from 'next-auth/react'

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  const session = await getSession({ req })
  if (!session) {
    return res.status(401).json({ error: 'Unauthorized' })
  }

  const analytics = await prisma.analytics.findMany({
    where: { userId: session.user.id },
    orderBy: { date: 'desc' },
    take: 7
  })

  res.status(200).json(analytics)
}

Key Points

  • Access to request/response cycle
  • Authentication checks
  • Database access
  • SEO-friendly content

Static Generation

Pre-rendering pages at build time

Static Site Generation

// pages/blog/[slug].tsx
export default function BlogPost({ post }) {
  return (
    

{post.title}

) } export async function getStaticPaths() { const posts = await getAllPosts() const paths = posts.map((post) => ({ params: { slug: post.slug } })) return { paths, fallback: 'blocking' } } export async function getStaticProps({ params }) { const post = await getPostBySlug(params.slug) return { props: { post }, revalidate: 60 // ISR: update every minute } }

On-demand Revalidation

// pages/api/revalidate.ts
export default async function handler(req, res) {
  // Check for secret to confirm this is a valid request
  if (req.query.secret !== process.env.REVALIDATE_TOKEN) {
    return res.status(401).json({ message: 'Invalid token' })
  }

  try {
    // Revalidate the home page
    await res.revalidate('/')
    
    // Revalidate a blog post
    await res.revalidate(`/blog/${req.query.slug}`)
    
    return res.json({ revalidated: true })
  } catch (err) {
    return res.status(500).send('Error revalidating')
  }
}

Key Points

  • Fastest page loads
  • Incremental Static Regeneration (ISR)
  • On-demand revalidation
  • Perfect for content sites