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>
)
}