#Fetch

1 messages · Page 1 of 1 (latest)

quaint bloom
#
export default function Page(){
  const response = await fetch(...);
  const books = await response.json():

  const createBook = (name) => {
    ......
    await fetch(....)
  } 

 return (a component that creates a new book, the list of books).

}

In a code example like that, how can i update the books (do the GET fetch of the books) after i create a new one?

round grottoBOT
#

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

#

In Next.js App Router, you cannot use await directly inside a Client Component (i.e., components with 'use client'). The pattern you posted is only valid in a Server Component, where data fetching is done on the server and the result is rendered as HTML. However, Server Components cannot handle client-side events like button clicks to create a new book and then update the UI with the new list.

To achieve the behavior you want—fetching the updated list of books after creating a new one—you should use a Client Component and fetch data on the client side. The recommended approach is to use a data-fetching library like SWR or React Query, which handles revalidation and cache updates for you. Here’s how you can do it with SWR:

'use client'

import useSWR from 'swr'

const fetcher = (...args) => fetch(...args).then(res => res.json())

export default function Page() {
  const { data: books, mutate } = useSWR('/api/books', fetcher)

  const createBook = async (name) => {
    await fetch('/api/books', {
      method: 'POST',
      body: JSON.stringify({ name }),
      headers: { 'Content-Type': 'application/json' },
    })
    // Re-fetch the books after creating a new one
    mutate()
  }

  if (!books) return <div>Loading...</div>

  return (
    <div>
      {/* Your form or button to create a book, calling createBook */}
      <ul>
        {books.map(book => (
          <li key={book.id}>{book.name}</li>
        ))}
      </ul>
    </div>
  )
}

Here, mutate() will re-fetch the books after a new one is created, updating the UI automatically. This is the recommended pattern for client-side data updates in Next.js Client Components see SWR example.

#

If you want to stick with Server Components, you would need to use a form that submits to a Server Action, and then use revalidatePath to revalidate the cache for the books list after creating a new book. However, this would involve a full page reload or navigation, not a seamless client-side update.

Summary:

  • For seamless client-side updates, use SWR or React Query in a Client Component and call mutate() after creating a new book.
  • For server-side updates, use Server Actions and revalidate the path, but this is less dynamic for interactive UIs.

Client-side data fetching with SWR
Revalidating after updates

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

#

@quaint bloom Kapa.ai is still learning and improving, please let me know how I did by reacting below.

quaint bloom
#

@round grotto im interested in For server-side updates, use Server Actions and revalidate the path, but this is less dynamic for interactive UIs.
can you talk about it and show an example, also how it works alognisde revalidation

round grottoBOT
#

Let me check the knowledge base for you again. Give me one moment! :books:

#

Certainly! In Next.js App Router, you can use Server Actions to handle server-side updates (like creating a new book) and then call revalidatePath to invalidate the cache for a specific route. This ensures that when the user is redirected or the page is revisited, the latest data is fetched from the server.

How it works

  1. Form Submission:
    The user submits a form that triggers a Server Action (a function marked with 'use server').
  2. Data Mutation:
    The Server Action performs the mutation (e.g., creates a new book in the database).
  3. Revalidation:
    The Server Action calls revalidatePath('/books') to invalidate the cache for the /books route.
  4. Redirect (optional):
    The Server Action can redirect the user to the updated page.
  5. Fresh Data:
    On the next visit to /books, the server fetches fresh data and updates the cache.

Example

Suppose you have a page that lists books at /books. Here’s how you might implement server-side creation and revalidation:

Server Action (app/actions.ts):

'use server'

import { revalidatePath } from 'next/cache'
import { redirect } from 'next/navigation'

export async function createBook(formData: FormData) {
  // 1. Mutate data (e.g., save the new book to the database)
  // await db.books.create({ name: formData.get('name') })

  // 2. Revalidate the /books path
  revalidatePath('/books')

  // 3. Redirect to the books page (optional)
  redirect('/books')
}

Form in a Server or Client Component:

import { createBook } from '@/app/actions'
#

export default function BookForm() {
return (
<form action={createBook}>
<input type="text" name="name" placeholder="Book name" />
<button type="submit">Create Book</button>
</form>
)
}

**Books Page (app/books/page.tsx):**
```typescript
export default async function BooksPage() {
  // Fetch books from the database or API
  // const books = await db.books.findMany()
  return (
    <ul>
      {/* {books.map(book => <li key={book.id}>{book.name}</li>)} */}
    </ul>
  )
}

How Revalidation Works

  • When revalidatePath('/books') is called, the cache for /books is marked as stale.
  • The next time /books is visited, Next.js fetches fresh data and updates the cache.
  • If you use redirect('/books') after the mutation, the user is sent to the /books page, which will now show the updated list.

Note: The revalidation does not immediately update the UI in-place (like client-side state management would). Instead, it ensures that the next server render (after navigation or refresh) will have the latest data see docs see also.

This approach is ideal for traditional form submissions and workflows where a full page reload or navigation is acceptable after a mutation.


References:


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

#

@quaint bloom Kapa.ai is still learning and improving, please let me know how I did by reacting below.

quaint bloom
#

@round grotto

export default async function Page({
params,
}: {
params: Promise<{ roundId: number }>;
}) { ....}

i have a couple questions with params for nextjs app router, last version. is it a promise? should all be typed as string and then do the type check?

round grottoBOT
#

Let me check the knowledge base for you again. Give me one moment! :books:

#

Yes, in the latest versions of Next.js (v15+), the params prop in App Router pages is a Promise. You should type it as Promise<{ roundId: string }> (not number), because all dynamic route parameters are always passed as strings, regardless of their intended use. You can then parse or validate the value as needed inside your component.

Example:

export default async function Page({
  params,
}: {
  params: Promise<{ roundId: string }>;
}) {
  const { roundId } = await params;
  const roundIdNumber = Number(roundId); // Convert to number if needed
  // ...rest of your code
}

This is confirmed in the official documentation and multiple sources, which state that params is now a Promise and all route parameters are strings (see docs, dynamic routes, upgrade guide). If you use a type like Promise<{ roundId: number }> you may run into type errors, since the value will always be a string.

Summary:

  • params is a Promise in Next.js 15+.
  • All route params are strings; convert them as needed.
  • Type as Promise<{ roundId: string }> and parse inside your function.

Next.js page file conventions

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

#

@quaint bloom Kapa.ai is still learning and improving, please let me know how I did by reacting below.

quaint bloom
#

@round grotto

export default async function Round({
  params,
}: {
  params: Promise<{ roundId: number }>;
}) {
  const { roundId } = await params;
  const roundIdNumber = Number(roundId);

  const invalidRound =
    roundIdNumber === 0 ||
    isNaN(roundIdNumber) ||
    !Number.isInteger(roundIdNumber);

  return (

is this 100% handlng possible wrong roundIdNumbers? so i can render conditionally something if the param is wrong.

round grottoBOT
#

Let me check the knowledge base for you again. Give me one moment! :books:

#

Your approach to validating roundId is mostly correct, but there are a couple of important details to consider based on the official Next.js documentation:

  1. Type of roundId:
    All dynamic route parameters in Next.js are passed as strings, not numbers. So your type should be Promise<{ roundId: string }> instead of number. This is explicitly stated in the documentation and examples (see docs, dynamic routes).

  2. Validation Logic:
    Your validation logic:

    const invalidRound =
      roundIdNumber === 0 ||
      isNaN(roundIdNumber) ||
      !Number.isInteger(roundIdNumber);
    

    will catch:

    • Non-numeric values (e.g., /round/abc)
    • Zero (if you want to disallow it)
    • Non-integer numbers (e.g., /round/1.5)

    This is a reasonable way to check for invalid IDs, assuming you want to disallow zero and non-integers.

  3. Conditional Rendering:
    You can safely use this logic to render a fallback or error UI if invalidRound is true.

Example (with correct typing):

export default async function Round({
  params,
}: {
  params: Promise<{ roundId: string }>;
}) {
  const { roundId } = await params;
  const roundIdNumber = Number(roundId);

  const invalidRound =
    roundIdNumber === 0 ||
    isNaN(roundIdNumber) ||
    !Number.isInteger(roundIdNumber);

  if (invalidRound) {
    return <div>Invalid round ID</div>;
  }

  // ...rest of your code
}

Summary:

  • Use string for the param type, not number.
  • Your validation logic is sound for catching invalid IDs.
  • This approach is supported by the official documentation (see dynamic route params).
#

If you need more advanced validation (e.g., positive numbers only), you can extend the logic as needed.

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