Next.js App Router


Uncategorized

Updated May 9th, 2023

The Vercel team released a video called “Introducing Next 13 App Router” here and I enjoyed the part around 2 minutes in about the verbosity of making a PUT request. I wanted to take a closer look at the code sample they used.

“Too verbose to model data mutations in React and Next.js”

import {cookies} from "next/headers"
import {NextRequest} from "next/server"
import {db} from "./db"

export async function PUT(req: NextRequest) {
  const cartId = cookies().get('cartId')?.value
  const {productId} = await req.json()

  await.db.lineItem.create({
    data: {
      productId: productId,
      cart: {
        connect: {
          id: cartId
        }
      }
    }
  })

  // ...
}

You had to create an API route, write an effect that calls the API route and stores the response in state. As a result you accidentally ended up with a lot of client side JavaScript and third party libraries.

// the effect

import {useState} from "react"
import {useCartCount} from "/.cart-count-context"

export default function AdToCart({ productId }: { productId: string}) {
  const [count, setCartCount] = useCartCount()
  const [isPending, setIsPending] = useState(false)

  async function handleAdd() {
    setIsPending(true)
    const response = await fetch('/api/cart', {
      method: 'PUT',
      body: JSON.stringify({
        productId
      })
    })
    const data = await response.json()
    setCartCount(count + 1)
    setIsPending(false)
  }

  return (
    <button onClick={handleAdd} disabled={isPending}>
      Add to Cart
    </button>
  )
}

Now with server actions in Next.js it becomes this:

import {cookies} from "next/headers"
import {db} from './db"

export default function AddToCart({productId}) {
  const cartId = cookies.get('cartId')?.value

  async function addItem() {
    'use server'

    await db.lineItem.create({
      data: {
        productId: productId,
        cart: {
          connect: {
            id: cartId
          } 
        }
      }
    })
  }

  return (
    <form>
      <button formAction={addItem}>Add to Cart</button>
    </form>
  )
}