Ir al contenido principal

Generación de tipos

Generación de tipos

AppKit puede generar automáticamente tipos de TypeScript para tus queries SQL, lo que aporta seguridad de tipos de extremo a extremo, desde la base de datos hasta la UI.

Objetivo

Generar declaraciones de TypeScript con tipado seguro para claves de query, parámetros y filas de resultados.

Todos los archivos generados residen en shared/appkit-types/, uno por cada ámbito: analytics.d.ts (tipos de queries SQL), serving.d.ts (tipos de endpoints de serving de modelos) y metric-views.d.ts (tipos de metric views). Un solo comando (y el plugin de Vite) los genera todos en una única pasada; consulta Tipos de metric view. Los archivos usan declare module para ampliar interfaces existentes, de modo que los tipos se aplican de forma global: nunca necesitas importarlos. TypeScript los detecta automáticamente mediante "include": ["shared/appkit-types"] en tu tsconfig.

Plugin de Vite: appKitTypesPlugin

El enfoque recomendado es usar el plugin de Vite, que supervisa tus archivos SQL y regenera los tipos automáticamente durante el desarrollo.

Configuración

  • outFile?: string: ruta del archivo de salida (valor predeterminado: shared/appkit-types/analytics.d.ts)
  • watchFolders?: string[]: carpetas en las que se buscarán archivos SQL (valor predeterminado: ["../config/queries"])

Ejemplo

// client/vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { appKitTypesPlugin } from "@databricks/appkit";

export default defineConfig({
  plugins: [
    react(),
    appKitTypesPlugin({
      watchFolders: ["../config/queries"],
    }),
  ],
});

Detalle importante

Cuando el frontend se sirve a través de AppKit en modo de desarrollo, el servidor de desarrollo de AppKit ya incluye appKitTypesPlugin() internamente. Aun así, conviene mantenerlo en el pipeline de compilación del cliente si ejecutas vite build por separado.

CLI: npx @databricks/appkit generate-types

Para generar tipos manualmente o en pipelines de CI/CD, usa el siguiente comando de la CLI:

# Requiere DATABRICKS_WAREHOUSE_ID (o pásalo como tercer argumento)
npx @databricks/appkit generate-types [rootDir] [outFile] [warehouseId]

Ejemplos

  • Generar tipos tomando el ID del warehouse del entorno

    npx @databricks/appkit generate-types . shared/appkit-types/analytics.d.ts
  • Generar tipos indicando el ID del warehouse de forma explícita

    npx @databricks/appkit generate-types . shared/appkit-types/analytics.d.ts abc123...
  • Forzar la regeneración (omitir la caché)

    npx @databricks/appkit generate-types --no-cache

Disponibilidad del warehouse y la opción --wait

De forma predeterminada, generate-types no bloquea: nunca espera a tu SQL warehouse ni falla por su causa. Escribe de inmediato los mejores tipos que puede (reutilizando los tipos en caché cuando la query no ha cambiado o, si no, result: unknown) y luego lanza un worker independiente en segundo plano que actualiza los tipos reales en cuanto el warehouse está listo. Así, npm install (postinstall) y npm run dev (predev) siguen siendo rápidos y tolerantes ante un warehouse frío o inaccesible durante unos instantes. El plugin de Vite para desarrollo se comporta igual: los tipos aparecen al instante y se actualizan sobre la marcha en cuanto el warehouse está activo.

Usa --wait en las compilaciones de CI y de producción, donde los tipos correctos deben estar disponibles antes de que la compilación continúe:

npx @databricks/appkit generate-types --wait

Resiliencia en CI: tipos versionados como respaldo

En modo bloqueante (--wait), el generador intenta obtener los tipos reales desde tu warehouse, pero recurre a los archivos de tipos versionados (shared/appkit-types/analytics.d.ts y, cuando hay Metric Views configuradas, shared/appkit-types/metric-views.d.ts) como respaldo cuando el warehouse no está accesible. Estos archivos generados deben formar parte de tu repositorio. En un checkout limpio de CI, cada compilación intenta ejecutar DESCRIBE contra el warehouse; los tipos versionados solo se usan cuando eso no llega a completarse.

El generador nunca sobrescribe los tipos versionados con tipos degradados (result: unknown): o escribe tipos reales, o no escribe nada.

Una taxonomía de fallos en dos categorías determina si la compilación se interrumpe o recurre a los tipos versionados:

  • Fallos deterministas (siempre interrumpen la compilación): errores de sintaxis SQL en tus queries (fallo real de DESCRIBE contra un warehouse accesible), HTTP 404 (ID de warehouse incorrecto o desconocido), HTTP 400 (petición malformada). Son errores del desarrollador o de configuration que los tipos versionados no deben ocultar.
  • Fallos del entorno (condicionados a los tipos versionados): fallos de autenticación (401/403), red inaccesible, warehouse no disponible (frío, en eliminación o eliminado), tiempo de espera agotado en RUNNING, o cualquier fallo no reconocido. Si existen todos los archivos de tipos que la aplicación necesita, la compilación los conserva, emite una advertencia bien visible en stderr y finaliza correctamente (exit 0). Si falta algún archivo requerido, la compilación se interrumpe con un mensaje que te indica ejecutar npx @databricks/appkit generate-types --wait en local (contra un warehouse accesible) y versionar los archivos de tipos generados.

Esa advertencia es una única línea en stderr, fácil de localizar con grep, que indica la causa general (autenticación bloqueada / warehouse inaccesible / warehouse no disponible) y el ID del warehouse, de modo que los logs de CI dejen claro que la compilación recurrió a los tipos versionados.

En una aplicación con Metric Views, metric-views.d.ts debe existir previamente para que un fallo del entorno pueda recurrir al respaldo con éxito: analytics.d.ts por sí solo no cumple la condición.

El template de la aplicación ya lo deja configurado por ti: postinstall y predev ejecutan el comportamiento predeterminado no bloqueante, mientras que prebuild ejecuta --wait.

Tipos de metric view

generate-types (y el plugin de Vite) emiten los tipos de metric view de forma aditiva: no hay un comando aparte. Cuando existe un archivo config/metric-views/definitions.json, la misma ejecución que genera tus tipos de query también aplica DESCRIBE a cada UC Metric View declarada y escribe dos artefactos:

  • shared/appkit-types/metric-views.d.ts: amplía la interfaz MetricRegistry para que useMetricView('<key>', …) tenga autocompletado y verificación de tipos. Las medidas, las dimensiones y sus metadatos semánticos (tipo SQL, nombre visible, formato, granularidades temporales) de cada view se codifican a nivel de tipos. Las claves de fila seleccionadas usan el tipo de valor real que se transmite en JSON_ARRAY (string | null); el tipo SQL sigue disponible en los metadatos para analizarlo o formatearlo de forma deliberada.
  • config/metric-views/metadata.generated.json: la mitad de runtime de esa misma pasada, que lleva esos metadatos por columna como valor, junto a tu definitions.json escrito a mano. La ruta de métricas lo descubre automáticamente y adjunta los metadatos de las columnas solicitadas a su carga de respuesta, de modo que no hace falta conectar ningún plugin. Súbelo al repositorio junto con tus tipos generados; es un archivo generado, así que no lo edites a mano.

Si config/metric-views/definitions.json no está presente, la ruta de métricas permanece inactiva (no se emite nada). Cuando sí lo está, sigue el mismo contrato de disponibilidad del warehouse que los tipos de query: en la ejecución no bloqueante predeterminada, una view que todavía no se puede describir —por un warehouse en frío o una fuente inválida o inaccesible— se escribe con tipos permisivos y una advertencia, mientras que con --wait las metric views se rigen por la taxonomía de dos categorías (los fallos del entorno recurren a los tipos ya versionados en metric-views.d.ts y emiten una advertencia; los fallos deterministas, como definiciones mal formadas, hacen fallar la compilación). Un definitions.json mal formado (JSON inválido o una fuente que no sea un FQN de UC de tres partes) falla de inmediato en todos los modos.

definitions.json se indexa por clave de métrica; cada entrada indica el FQN de UC de tres partes de la view y, opcionalmente, el ejecutor con el que se ejecuta (app_service_principal, el valor predeterminado, o user):

{
  "$schema": "https://databricks.github.io/appkit/schemas/metric-source.schema.json",
  "metricViews": {
    "revenue": { "source": "catalog.schema.revenue_metrics" },
    "customers": {
      "source": "catalog.schema.customer_metrics",
      "executor": "user"
    }
  }
}

La línea opcional $schema habilita el autocompletado en el editor y la validación frente al esquema publicado.

Cómo funciona

El generador de tipos:

  1. Analiza la carpeta config/queries/ en busca de archivos .sql
  2. Interpreta las anotaciones de parámetros SQL (por ejemplo, -- @param startDate DATE)
  3. Se conecta a tu SQL Warehouse de Databricks para inferir los tipos de las columnas del resultado
  4. Genera interfaces de TypeScript para los parámetros y los resultados de las queries
  5. Crea un tipo QueryRegistry para ejecutar queries con seguridad de tipos

Parámetros durante DESCRIBE QUERY

La generación de tipos describe cada query sin enlazar parámetros reales, por lo que sustituye cada :param por un valor predeterminado de marcador de posición (por ejemplo, '' para una cadena). Esto rompe las queries cuya forma depende de un valor, sobre todo los nombres de tabla dinámicos mediante IDENTIFIER(:catalog || '.schema.table'). Anota esos parámetros con un valor de ejemplo (-- @param catalog STRING = main) para que la llamada de descripción pueda resolver una tabla real. El valor de ejemplo solo se usa al generar los tipos; en runtime, la query sigue enlazando el parámetro real. Consulta Parámetros SQL → Valores de ejemplo.

Uso de los tipos generados

Una vez generados los tipos, tu IDE ofrecerá autocompletado y comprobación de tipos:

import { useAnalyticsQuery } from "@databricks/appkit-ui/react";
import { sql } from "@databricks/appkit-ui/js";

// TypeScript sabe que "users_list" es una clave de query válida
// y qué parámetros espera
const { data } = useAnalyticsQuery("users_list", {
  status: sql.string("active"),
  limit: sql.number(50),
});

// TypeScript conoce la estructura de las filas del resultado
data?.forEach((row) => {
  console.log(row.email); // ✓ el autocompletado funciona
});

Véase también

Databricks Developer Hub

¿Todo listo para lanzar tu próxima aplicación basada en agentes en minutos?

Leer la documentación