Vue cookbook · Intermediate

Validate input in Nuxt server routes

API routes trust request bodies and query strings, returning 500s on bad input.

Create a free account to save this page, take notes and track your progress. Sign in

Approach

Validate bodies, params and query strings with zod through h3's validated readers, return 400 with field errors via createError, and share schemas with the front end.

Solution

// server/api/orders/index.post.ts
import { z } from "zod";

const NewOrder = z.object({
  productId: z.string().min(1),
  quantity: z.number().int().min(1).max(10),
  note: z.string().max(200).optional(),
});

export default defineEventHandler(async (event) => {
  const result = await readValidatedBody(event, (body) => NewOrder.safeParse(body));
  if (!result.success) {
    throw createError({ statusCode: 400, statusMessage: "Invalid order", data: z.flattenError(result.error).fieldErrors });
  }
  const user = await requireUser(event);                  // your auth helper in server/utils
  const order = await createOrder(user.id, result.data);  // your data access in server/utils
  setResponseStatus(event, 201);
  return order;
});

declare function requireUser(event: unknown): Promise<{ id: string }>;
declare function createOrder(userId: string, input: z.infer<typeof NewOrder>): Promise<{ id: string }>;
// server/api/orders/index.get.ts
import { z } from "zod";

const Query = z.object({
  page: z.coerce.number().int().min(1).default(1),
  status: z.enum(["open", "shipped"]).optional(),
});

export default defineEventHandler(async (event) => {
  const query = await getValidatedQuery(event, (q) => Query.parse(q));   // throws 400 on invalid input
  return { page: query.page, status: query.status ?? "all", items: [] };
});

Pitfalls

  • Returning ZodError messages verbatim to users; map them to friendly field errors.
  • Validating only on the client.
  • Forgetting authorization checks after validation.

Variations

  • Put schemas in shared/ so pages and server routes import the same definitions.
  • nuxt-zod or valibot offer alternative validation approaches.

Further reading: Nuxt server directory

Reference: https://nuxt.com/docs/4.x/guide/directory-structure/server

More Vue recipes

Was this page helpful?