Volver al inicio

API en Next.js · Paso 9 de 9

Tu primer CRUD: productos por API

Listar y crear productos desde route.ts con validación mínima del cuerpo JSON.

El CRUD son las cuatro operaciones habituales: crear, leer, actualizar y borrar. Para no mezclar todo a la vez, aquí solo lees la lista y creas un producto: suficiente para ver el recorrido completo petición → Prisma → respuesta JSON.

Coloca el archivo en app/api/productos/route.ts. La URL pública será /api/productos.

GET — listar productos

import { NextResponse } from "next/server"

import { prisma } from "@/lib/prisma"

export async function GET() {
  const productos = await prisma.producto.findMany({
    orderBy: { id: "asc" },
  })
  return NextResponse.json(productos)
}

findMany devuelve un array; vacío al principio, hasta que insertes datos.

POST — crear un producto

El cuerpo llega como JSON. Comprueba el método y parsea con cuidado:

import { NextResponse } from "next/server"

import { prisma } from "@/lib/prisma"

export async function POST(request: Request) {
  let body: unknown
  try {
    body = await request.json()
  } catch {
    return NextResponse.json({ error: "JSON inválido" }, { status: 400 })
  }

  if (!body || typeof body !== "object") {
    return NextResponse.json({ error: "Cuerpo vacío" }, { status: 400 })
  }

  const { nombre, precio, stock } = body as {
    nombre?: unknown
    precio?: unknown
    stock?: unknown
  }

  if (typeof nombre !== "string" || nombre.trim() === "") {
    return NextResponse.json({ error: "nombre es obligatorio" }, { status: 400 })
  }
  if (typeof precio !== "number" || Number.isNaN(precio)) {
    return NextResponse.json({ error: "precio debe ser un número" }, { status: 400 })
  }

  const stockNum =
    typeof stock === "number" && !Number.isNaN(stock) ? Math.trunc(stock) : 0

  const creado = await prisma.producto.create({
    data: {
      nombre: nombre.trim(),
      precio,
      stock: stockNum,
    },
  })

  return NextResponse.json(creado, { status: 201 })
}

Los nombres producto, create, findMany coinciden con el modelo Producto en Prisma (primera letra minúscula en el cliente).

Probar en local

Con npm run dev:

  • Navegador o GET: http://localhost:3000/api/productos → lista (puede ser []).
  • POST con curl (ejemplo):
curl -X POST http://localhost:3000/api/productos \
  -H "Content-Type: application/json" \
  -d '{"nombre":"Agua","precio":1.2,"stock":24}'

Deberías recibir el objeto creado con id asignado. Repite el GET y verás el nuevo registro.

Siguiente paso natural

Cuando domines crear y listar, podrás añadir GET por id, PATCH para actualizar precio o stock y DELETE con prisma.producto.delete. El mismo patrón: función exportada por método HTTP en route.ts, validación del cuerpo y respuestas con códigos adecuados (404 si no existe el registro).

Cuando hayas leído el texto, marca la lección para seguir el progreso.