Skip to content

Server

Crea aplicaciones integrales con el framework de servidor de Nuxt. Puedes extraer datos de tu base de datos o de otro servidor, crear API o incluso generar contenido estático del lado del servidor, como un mapa del sitio o una fuente RSS, todo desde una única base de código.

sh
-| server/
---| api/
-----| hello.ts      # /api/hello
---| routes/
-----| bonjour.ts    # /bonjour
---| middleware/
-----| log.ts        # log all requests

Cada archivo debe exportar una función predeterminada definida con defineEventHandler() o eventHandler() (alias).

server/api/hello.ts

ts
export default eventHandler((event) => {
  return {
    hello: "world",
  };
});

pages/index.vue

vue
<script setup lang="ts">
const { data } = await useFetch("/api/hello");
</script>

<template>
  <pre>{{ data }}</pre>
</template>

Parámetros de ruta

Crea rutas dinámicas en el servidor utilizando corchetes en los nombres de archivo.

server/api/users/[id].ts

ts
export default eventHandler((event) => {
  const id = getRouterParam(event, "id");
  return { id };
});

Matching HTTP Method

Los nombres de archivos de manejo pueden tener el sufijo .get, .post, .put, .delete, ... para que coincidan con el método HTTP de la solicitud.

server/api/users.post.ts

ts
export default eventHandler((event) => {
  // handle POST /api/users
});

También puedes usarlo index.[method].ts dentro de un directorio para estructurar tu código de manera diferente, esto es útil para crear espacios de nombres de API.

server/api/users/index.post.ts

ts
export default eventHandler((event) => {
  // handle POST /api/users
});

Body Handling

Para manejar el cuerpo de la solicitud, usa readBody().

server/api/users.post.ts

ts
export default eventHandler(async (event) => {
  const body = await readBody(event);
  // handle body
});

app.vue

vue
<script setup lang="ts">
async function submit() {
  const { body } = await $fetch("/api/users", {
    method: "post",
    body: { test: 123 },
  });
}
</script>

Query Parameters

Para manejar los parámetros de consulta, usa getQuery().

server/api/search.ts

ts
// /api/search?term=nuxt&page=1

export default eventHandler((event) => {
  const query = getQuery(event);
  return {
    term: query.term,
    page: query.page || 1,
  };
});

Error Handling

Para manejar errores, lanza un error HTTP usando createError().

server/api/secret.ts

ts
export default eventHandler((event) => {
  const authorized = false; // replace with your auth logic
  if (!authorized) {
    throw createError({ statusCode: 401, statusMessage: "Unauthorized" });
  }
  return {
    secret: "42",
  };
});

Status Codes

Para devolver un código de estado HTTP personalizado, usa setResponseStatus().

server/api/custom-status.ts

ts
export default eventHandler((event) => {
  setResponseStatus(event, 201);
  return { message: "Created" };
});

Sending Redirect

server/api/foo.get.ts

ts
export default defineEventHandler(async (event) => {
  await sendRedirect(event, "/path/redirect/to", 302);
});

Ejemplo con NuxtUI

server\api\users\index.get.ts

ts
export default eventHandler(() => {
  return [
    { id: 1, username: "user1", email: "user1@mail.com" },
    { id: 2, username: "user2", email: "user2@mail.com" },
    { id: 3, username: "user3", email: "user3@mail.com" },
  ];
});

server\api\users[id].get.ts

ts
export default eventHandler((event) => {
  const id = Number(getRouterParam(event, "id"));

  const users = [
    { id: 1, username: "user1", email: "user1@mail.com" },
    { id: 2, username: "user2", email: "user2@mail.com" },
    { id: 3, username: "user3", email: "user3@mail.com" },
  ];

  return users.find((u) => u.id === id);
});

server\api\users\index.post.ts

ts
import { formSquema } from "~~/shared/zod/zod.user";

export default eventHandler(async (event) => {
  const body = await readBody(event);

  const parsed = formSquema.safeParse(body);
  if (!parsed.success) {
    throw createError({
      statusCode: 400,
      statusMessage: "Error Zod backend",
    });
  }

  return { ok: true };
});

shared\zod\zod.user.ts

ts
import * as z from "zod";

export const formSquema = z.object({
  username: z
    .string()
    .trim()
    .min(1, "Escribe algo por favor")
    .max(255, "Te agilaste"),
  email: z.email("Correo no weno"),
});

export type FormSquemaType = z.infer<typeof formSquema>;

Frontend

app\pages\admin\users\index.vue

vue
<script setup lang="ts">
const { data: users } = await useFetch("/api/users");
</script>

<template>
  <div>
    <h1>Users</h1>
    <p>{{ users }}</p>
  </div>
</template>

app\pages\admin\users[id].vue

vue
<script setup lang="ts">
const { params } = useRoute();

const { data: user } = useFetch(`/api/users/${params.id}`);
</script>

<template>
  <div>
    <h1>User: {{ params.id }}</h1>
    <pre>
      {{ user }}
    </pre>
  </div>
</template>

app\pages\admin\users\create.vue

vue
<script setup lang="ts">
import type { FormSubmitEvent } from "@nuxt/ui";
import type { FormSquemaType } from "~~/shared/zod/zod.user";
import { formSquema } from "~~/shared/zod/zod.user";

const state = reactive<Partial<FormSquemaType>>({
  username: "",
  email: "",
});

const toast = useToast();

const onSubmit = async (event: FormSubmitEvent<FormSquemaType>) => {
  // const { data, error } = await useFetch("/api/users", {
  //   method: "POST",
  //   body: event.data,
  // });

  // console.log({ data: data.value, error: error.value });

  // if (error) {
  //   toast.add({
  //     title: "Error",
  //     description: "Zod error",
  //     color: "error",
  //   });
  //   return;
  // }

  try {
    const data = await $fetch("/api/users", {
      method: "POST",
      body: event.data,
    });
    console.log({ data });

    toast.add({
      title: "Success",
      description: "Formulario procesado",
      color: "success",
    });
  } catch (error) {
    console.log({ error });
    toast.add({
      title: "Error",
      description: "Zod error",
      color: "error",
    });
  }
};
</script>

<template>
  <div>
    <h1>Create new User</h1>
    <UForm
      :schema="formSquema"
      :state="state"
      class="space-y-2"
      @submit="onSubmit"
    >
      <UFormField
        label="Username"
        name="username"
      >
        <UInput v-model="state.username" />
      </UFormField>

      <UFormField
        label="Email"
        name="email"
      >
        <UInput
          v-model="state.email"
          type="email"
        />
      </UFormField>

      <UButton type="submit"> Submit </UButton>
    </UForm>
  </div>
</template>