Ir al contenido principal

Plugin de base de datos

Plugin de base de datos

Plugin beta

Este plugin se encuentra actualmente en fase beta. Las APIs pueden cambiar entre versiones menores. Impórtalo desde @databricks/appkit/beta. Consulta Niveles de estabilidad de los plugins.

Declara tus tablas en config/database/schema.ts y registra database() para obtener un CRUD HTTP generado y un cliente de base de datos del lado del servidor. De forma predeterminada, el CRUD está habilitado para todas las tablas declaradas. Usa api para restringir las rutas generadas sin deshabilitar el acceso del lado del servidor.

Acceso compartido a la aplicación

Este plugin usa el service principal de la aplicación en Databricks Apps desplegadas. No admite OBO ni aplica autorización por usuario ni por fila. Cualquier llamante que pueda acceder a la API generada puede ejecutar todas las operaciones habilitadas sobre todas las filas expuestas, incluida la eliminación.

Restringe el acceso a la aplicación y otorga a su service principal solo los permisos de base de datos que necesite. Si los usuarios requieren permisos distintos o comprobaciones de propiedad de las filas, deshabilita las rutas generadas correspondientes e implementa la autorización en rutas de servidor personalizadas. El acceso a la aplicación, por sí solo, no proporciona aislamiento a nivel de fila.

Uso básico

Configura un recurso postgres de Lakebase y sus variables de entorno de conexión tal como se describe en Configuración de Lakebase. Las tablas de la base de datos deben existir previamente y coincidir con el esquema declarado. Este plugin comprueba la conectividad durante la configuración; no crea ni migra tablas.

Las aplicaciones generadas con el plugin de base de datos seleccionado incluyen un archivo config/database/schema.ts vacío, de modo que database() puede iniciarse sin necesidad de tablas de ejemplo. Sustituye la declaración vacía por tus modelos cuando sus tablas de PostgreSQL estén listas.

En el desarrollo local, el nombre de usuario de PostgreSQL se obtiene de tus credenciales de Databricks cuando PGUSER y DATABRICKS_CLIENT_ID no están definidas. Un nombre de usuario configurado explícitamente tiene prioridad.

// config/database/schema.ts
import { defineSchema, id, text } from "@databricks/appkit/beta";

export const schema = defineSchema((builder) => ({
  notes: builder.table("notes", {
    id: id(),
    body: text().notNull(),
  }),
}));
// server/index.ts
import { createApp, server } from "@databricks/appkit";
import { database } from "@databricks/appkit/beta";

const AppKit = await createApp({
  plugins: [server(), database()],
});

Con el plugin de servidor habilitado, esto registra:

MétodoRutaOperación
GET/api/database/notesListar filas
GET/api/database/notes/:idObtener una fila
POST/api/database/notesCrear una fila
PATCH/api/database/notes/:idActualizar una fila
DELETE/api/database/notes/:idEliminar una fila

Una tabla sin clave primaria pública solo admite listar y crear. upsert está disponible para el código del servidor, pero no genera ninguna ruta HTTP.

Descubrimiento de esquemas y anulaciones

database() y database({}) usan los mismos valores predeterminados. Durante la configuración, el plugin carga la exportación con nombre schema desde config/database/schema.ts, relativa al directorio de trabajo de la aplicación. El archivo debe exportar un resultado finalizado de defineSchema(). Si faltan archivos, fallan las importaciones o las exportaciones no son válidas, la configuración falla antes de que el plugin cree un pool de conexiones; no se crea un esquema vacío de forma silenciosa.

Conserva config/database/schema.ts y sus importaciones locales en tu deployment. El plugin carga TypeScript mediante Jiti, por lo que un proceso de Node normal en producción no necesita un cargador de TypeScript aparte. El módulo del esquema solo debe declarar tablas; no debe conectarse a la base de datos ni iniciar la aplicación.

Si tienes una estructura distinta o un deployment que solo contiene un bundle de servidor, importa el esquema de forma explícita y pásalo al plugin:

import { schema } from "../config/database/schema";

database({ schema });

Un esquema explícito siempre tiene prioridad y omite el descubrimiento de archivos. Un esquema explícito no válido hace que falle la configuración en lugar de recurrir a otro archivo.

Ejecuta appkit generate-types para generar el registro de la base de datos. Con ese registro, la configuración sin un esquema explícito sigue infiriendo los nombres de tabla y las cargas útiles de los hooks. Un esquema proporcionado de forma explícita también valida las claves de configuración frente a sus propios nombres de tabla.

Restringir la API generada

Omitir api, o definirlo como true o {}, habilita el CRUD completo. Las restricciones son opcionales. No hay que activar la escritura por separado.

// No se generan rutas HTTP. El cliente del lado del servidor sigue funcionando.
database({ api: false });

// Rutas de solo lectura para todas las tablas.
database({ api: { writes: false } });

// CRUD completo solo para las tablas seleccionadas.
database({ api: { tables: ["notes"] } });

// Permite lecturas, creación y actualización, pero no eliminación.
database({
  api: { writes: { operations: ["create", "update"] } },
});

// Lee todas las tablas, pero permite escrituras solo en notes.
database({
  api: { writes: { tables: ["notes"] } },
});
OpciónValor predeterminadoEfecto
schemaExportación nombrada en config/database/schema.tsAnula la carga automática del esquema
apitruefalse deshabilita todas las rutas generadas
api.tablesTodas las tablas declaradasLimita qué tablas tienen rutas
api.writestruefalse conserva solo las rutas de lectura
api.writes.tablesTodas las tablas expuestasLimita qué tablas expuestas aceptan escrituras
api.writes.operationscreate, update, deleteLimita qué escrituras están habilitadas

api.tables: [] deshabilita todas las rutas generadas. Una lista vacía de tablas de escritura o de operaciones de escritura conserva las lecturas y deshabilita las escrituras. Las tablas omitidas en api.tables tampoco pueden incluirse mediante relaciones en las tablas expuestas. Estas restricciones se aplican únicamente a HTTP, no al cliente del lado del servidor ni a los hooks.

Una restricción omitida usa su valor predeterminado. Una restricción mal formada hace fallar la configuración. Por ejemplo, { api: { write: false } } es un error, no un permiso para generar todas las escrituras. Las tablas desconocidas, los nombres duplicados y las operaciones no admitidas también hacen fallar la configuración antes de que el plugin cree un pool de conexiones.

Reemplaza la antigua opción crudRoutes por api. El nombre anterior se rechaza en runtime para que una exclusión antigua no habilite la API de forma silenciosa. Para conservar el comportamiento de solo lectura, especifica api: { writes: false }.

Nombres de tablas y errores de configuración

Los nombres de rutas generados deben:

  • Comenzar con una letra ASCII.
  • Contener únicamente letras ASCII, dígitos, guiones bajos o guiones.
  • Tener como máximo 64 caracteres.
  • Ser únicos sin distinguir mayúsculas de minúsculas, ya que las rutas de Express no distinguen entre mayúsculas y minúsculas.

La API predeterminada valida todas las tablas declaradas. Una tabla como _events provoca un error en la configuración en lugar de omitirse en silencio. El error indica el nombre de la tabla y sugiere renombrarla, excluirla con api.tables o deshabilitar las rutas con api: false. Una tabla excluida sigue estando disponible para el código del servidor.

Los errores de configuración incluyen detalles accionables en el mensaje de error del lado del servidor. Los mensajes dirigidos al cliente no exponen esos detalles.

Validación y columnas privadas

La API generada valida los cuerpos de las solicitudes y rechaza los campos desconocidos o de solo lectura. Las columnas marcadas con .private() no están disponibles en las rutas generadas. Las claves primarias generadas por la base de datos, incluida uuid().primaryKey().defaultRandom(), no pueden ser proporcionadas por los clientes HTTP. Sí se puede proporcionar una clave primaria natural al crear un registro, pero las claves primarias no se pueden actualizar mediante HTTP.

La validación no es autorización. Una solicitud válida puede leer o modificar cualquier fila expuesta. Usa rutas personalizadas con autorización cuando ese no sea el modelo de acceso deseado.

Hooks de mutación

Una tabla puede declarar beforeCreate, afterCreate, beforeUpdate, afterUpdate, beforeUpsert, afterUpsert, beforeDelete y afterDelete. Los hooks y la mutación se ejecutan dentro de una misma transacción de base de datos. Las escrituras relacionadas que se realizan a través de ctx.app.database se incorporan a esa misma transacción. Esto no convierte en transaccionales las escrituras que se realizan a través de otros plugins o servicios externos.

import { DatabaseValidationError } from "@databricks/appkit";

database({
  hooks: {
    notes: {
      beforeCreate(values) {
        if (typeof values.body === "string" && values.body.length > 5_000) {
          throw new DatabaseValidationError("Note too long", [
            { path: ["body"], message: "Must be at most 5000 characters" },
          ]);
        }
      },
    },
  },
});

Un hook before* puede devolver valores de reemplazo, que se validan de nuevo antes de persistirse. DatabaseValidationError produce un HTTP 422 con incidencias limitadas a las columnas públicas. Los demás fallos de los hooks devuelven un error de servidor opaco.

Mantén los hooks breves y espera a que finalice todo el trabajo con la base de datos. Una transacción tiene un plazo de 30 segundos para el callback, un presupuesto compartido de 100 operaciones de base de datos y una profundidad máxima de anidamiento de mutaciones de 8. Se rechaza repetir la misma entidad y operación de mutación en una cadena de hooks anidada. PostgreSQL también aplica un statement_timeout de 30 segundos y un idle_in_transaction_session_timeout de 30 segundos.

El plazo del callback no cancela el JavaScript arbitrario, las solicitudes HTTP ni otros efectos secundarios externos. Evita incluir efectos secundarios externos en hooks que requieran semántica de reversión en la base de datos.

Referencia de la API

Databricks Developer Hub

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

Leer la documentación