---
order: 5
section: guides
title: API en tiempo de ejecución
description: >-
  createScribe y toda la superficie del cliente tipado: lectura, ayudantes de
  enrutamiento, relaciones, mapa del sitio, redirecciones e integración con
  frameworks.
noindex: false
canonicalPath: /es/docs/runtime-api
---
```ts
import { createScribe } from "scribe-cms/runtime";
import config from "./scribe.config";

const scribe = createScribe(config);
```

`createScribe()` devuelve un cliente tipado con un método de acceso por cada tipo de contenido (`scribe.blog`, `scribe.author`, ...) además de `scribe.sitemap()`. Todas las lecturas son síncronas y se realizan en memoria: el contenido se carga una vez, se guarda en caché y (en desarrollo) se revalida cuando cambian los archivos o el almacén de traducciones.

El cliente es Node puro, sin dependencias de frameworks. Funciona exactamente igual en Next.js, Astro, Remix, SvelteKit o en un script de compilación; los ejemplos de páginas a continuación usan las convenciones de Next.js puramente como ilustración.

Los documentos tienen esta estructura; `frontmatter` se tipa a partir de tu esquema de Zod:

```ts
{
  slug: string;        // slug in this document's locale
  enSlug: string;      // English parent slug (== slug for EN docs)
  locale: string;
  frontmatter: { ... };
  content: string;     // raw MDX body
  publishedAt?: string;
  updatedAt?: string;
  noindex: boolean;
}
```

## Lectura

### `list(locale?, options?)`

Todos los documentos para un idioma (por defecto, el idioma predeterminado), ordenados por el `orderBy` del tipo de contenido.

```ts
scribe.blog.list("fr");
scribe.blog.list("fr", { limit: 3 });
scribe.blog.list("fr", { orderBy: "slug" });   // per-call override
```

Devuelve `[]` para un idioma sin documentos.

### `get(slug, locale?)`

Búsqueda exacta del slug en un solo idioma. No hay alternativas de respaldo ni gestión de redirecciones. Devuelve el documento o `null`.

### `resolve(slug, locale)`

La resolución completa de la ruta de la solicitud; úsala en las páginas:

```ts
const r = scribe.blog.resolve(slug, locale);
// {
//   document: BlogDoc | null,
//   actualLocale: string,        // "en" when EN fallback kicked in
//   shouldRedirectTo?: string,   // 301 target (wrong-locale slug correction)
//   canonicalPath?: string,      // locale-aware pathname for the document
// }
```

Gestiona, en este orden: acierto directo, redirección por slug en el idioma incorrecto, [cadena de idiomas de respaldo](/docs/configuration#locale-fallback-chains) (activada por defecto) e idioma inglés de respaldo (cuando `indexFallback: "en"`). Cuando un idioma de respaldo sirve la página, `actualLocale` es ese idioma y la corrección del slug tiene en cuenta la cadena completa. Las migraciones de slugs y los documentos retirados se gestionan mediante las reglas de `_redirects.json` en tu proxy o mapa de redirecciones estáticas, no a través de `resolve()`. Un manejador de páginas típico (ejemplo con Next.js):

```ts
const resolved = scribe.blog.resolve(slug, locale);
if (resolved.shouldRedirectTo) permanentRedirect(resolved.shouldRedirectTo);
if (!resolved.document) notFound();
```

## Ayudantes de enrutamiento

### `staticParams(options?)`

Todos los pares `{ locale, slug }` para pre-renderizar; colócalo en el hook de rutas estáticas de tu framework (`generateStaticParams` en Next.js, `getStaticPaths` en Astro, `entries` en SvelteKit, ...):

```ts
export function generateStaticParams() {
  return scribe.blog.staticParams();
}

// Restrict locales (e.g. an OG-image route that only supports Latin scripts):
scribe.blog.staticParams({ locales: ["en", "fr", "de"] });
```

Para un idioma sin traducción, el slug pre-renderizado proviene del primer idioma en la [cadena de idiomas de respaldo](/docs/configuration#locale-fallback-chains) que tenga uno (y luego el slug en inglés), de modo que las URL pre-renderizadas coincidan con lo que sirve `resolve()`.

### `alternates(doc)`

El mapa hreflang para un documento: idioma relativo a la ruta, para cada idioma que tenga una traducción (el idioma por defecto siempre se incluye):

```ts
scribe.blog.alternates(doc);
// { en: "/blog/hello-world", fr: "/fr/blog/bonjour-le-monde" }
```

Pásalo a tus etiquetas hreflang, por ejemplo, en `metadata.alternates.languages` de Next.js o en elementos de enlace renderizados a mano.

### `translation(doc, targetLocale)`

El mismo documento en otro idioma, o `null` si no está traducido. Quienes lo llaman suelen recurrir al documento que ya tienen:

```ts
const frDoc = scribe.blog.translation(doc, "fr") ?? doc;
```

### `url(slug, locale)`

Construye la ruta a partir de la plantilla `path` del tipo:

```ts
scribe.blog.url("hello-world", "fr");  // "/fr/blog/hello-world"
```

`staticParams`, `alternates` y `url` lanzan un error para los tipos que son solo de referencia (sin `path`).

## Relaciones

### `related(doc, field, locale?)`

Extrae la referencia de un campo frontmatter `field.relation()` hacia los documentos del tipo de destino. El tipo de retorno se infiere del esquema:

```ts
scribe.blog.related(doc, "author");        // AuthorDoc           (required single)
scribe.vertical.related(doc, "blogSlug");  // BlogDoc | null      (optional single)
scribe.glossary.related(doc, "terms");     // GlossaryDoc[]       (multiple)
```

- Extrae la referencia en `locale ?? doc.locale`, recurriendo al documento en inglés cuando el destino no está traducido.
- Una relación **requerida** nunca devuelve `null`: `scribe validate` bloquea la compilación si hay relaciones requeridas colgantes, y `related()` lanza un error si alguna se cuela.
- Las relaciones opcionales devuelven `null`; las relaciones múltiples descartan silenciosamente los destinos faltantes.
- Solo se exponen los campos del esquema de nivel superior (las relaciones anidadas se siguen validando, pero debes extraer sus referencias manualmente).

## Mapa del sitio

```ts
const entries = await scribe.sitemap({
  baseUrl: "https://example.com",
  typeDefaults: {
    blog: { priority: 0.7, changeFrequency: "monthly" },
  },
});
```

Devuelve entradas JSON sencillas (`url`, `lastModified`, `changeFrequency`, `priority`, y `alternates.languages` para hreflang, incluyendo `x-default`). La estructura coincide con `MetadataRoute.Sitemap` de Next.js y se serializa directamente en un XML de mapa del sitio para cualquier otra pila tecnológica. Hay una entrada por documento en inglés en todos los tipos enrutables; omite los documentos `noindex` y los slugs de origen de redirección provenientes de `_redirects.json`.

Opciones: `baseUrl` (requerido), `contentTypes`, `typeDefaults`, `resolveUrl`, `resolvePathname`, `excludeNoindex` (verdadero por defecto), `includeXDefault` (verdadero por defecto).

## Introspección

Más allá de los métodos de acceso por tipo, el cliente expone la configuración resuelta:

```ts
scribe.config;              // resolved ScribeConfig
scribe.getType("blog");     // one resolved content type
scribe.listTypes();         // all content types
scribe.listRoutableTypes(); // only types with a path template
```

Útil para herramientas genéricas, por ejemplo, iterar sobre cada tipo enrutable para construir la navegación o los feeds.

## Redirecciones

Para las reglas de redirección exportadas estáticamente (`_redirects.json`, slugs multilingües), utiliza el punto de entrada del script de compilación:

```ts
// scripts/generate-redirects.ts (run before the app build)
import {
  buildAllContentRedirects,
  createProject,
  createUrlBuilder,
  loadConfigSync,
} from "scribe-cms";

const config = loadConfigSync();
const project = createProject(config);
const urlBuilder = createUrlBuilder(project.config);
const rules = buildAllContentRedirects(project, {
  prefixedLocales: urlBuilder.prefixedLocales,
});
// [{ source, destination, permanent: true }, ...]
// Matches Next.js redirect config directly; map to nginx rules, _redirects,
// vercel.json, or your framework's equivalent as needed.
```

## Exportaciones estáticas sin procesar

Para servir archivos MDX sin procesar como activos estáticos amigables con LLMs y rastreadores (por ejemplo, `/blog/my-post.mdx`):

```bash
scribe export-static --out public
```

O de forma programática:

```ts
import { writeStaticRawExports, createProject, loadConfigSync } from "scribe-cms";

writeStaticRawExports(createProject(loadConfigSync()), { outDir: "public" });
```

`getStaticExportRoots(project)` devuelve los directorios raíz gestionados para limpiarlos antes de escribir (por ejemplo, `blog/`, `fr/blog/`). Los documentos listados como orígenes de redirección en `_redirects.json` se omiten cuando `excludeRedirected` es verdadero (su valor por defecto). Los documentos `noindex` se incluyen a menos que se configure `excludeNoindex`. Consulta la [referencia de la CLI](/docs/cli) para ver las banderas del comando.

## Vía de escape

`scribe.<type>.load()` devuelve el índice sin procesar `Map<locale, { bySlug, byEnSlug }>`. Es preferible usar `list`, `get` o `resolve`; `load()` existe para herramientas específicas y patrones de acceso inusuales.

## Integración con frameworks

Scribe es independiente del framework; el entorno de ejecución es Node puro y funciona en cualquier pila renderizada en el servidor o construida de forma estática. Se aplican tres reglas en todos los casos:

1. Importa desde **`scribe-cms/runtime`** en el código de la aplicación (esto excluye las rutas de la CLI y del traductor del rastreo del empaquetador); usa `scribe-cms` en los scripts de compilación.
2. Mantén **`better-sqlite3`** (un módulo nativo) y Scribe como externos a tu empaquetador, por ejemplo usando `serverExternalPackages` en Next.js, `ssr.external` en Vite/Astro o `external` en esbuild.
3. Guarda el cliente en la caché como un singleton del módulo:

```ts
// src/lib/scribe.ts
import { createScribe } from "scribe-cms/runtime";
import type { ScribeClient } from "scribe-cms/runtime";
import config from "../../scribe.config";

let cached: ScribeClient<typeof config> | null = null;
export function getScribe() {
  return (cached ??= createScribe(config));
}
```

Condiciona las compilaciones a la integridad del contenido: `"build": "scribe validate && <framework build>"`.

### Ejemplo con Next.js

```ts
// next.config.ts
const nextConfig = {
  serverExternalPackages: ["better-sqlite3", "scribe-cms", "scribe-cms/runtime"],
  // Required on Vercel: Scribe reads content/ and .scribe/ at runtime.
  outputFileTracingIncludes: {
    "/**": ["./content/**/*", "./.scribe/**/*"],
  },
  // Also required on Vercel: Scribe's runtime file reads make the tracer
  // sweep Next's webpack build cache into every serverless function,
  // blowing past the 250 MB limit. The cache is never needed at runtime.
  outputFileTracingExcludes: {
    "/**": ["./.next/cache/**"],
  },
};
```

`staticParams()` se conecta con `generateStaticParams()`, `alternates()` con `metadata.alternates.languages`, `scribe.sitemap()` con `app/sitemap.ts`, y `buildAllContentRedirects()` con `redirects()`.
