Plugin de base de datos
Plugin de base de datos
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.
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étodo | Ruta | Operación |
|---|---|---|
| GET | /api/database/notes | Listar filas |
| GET | /api/database/notes/:id | Obtener una fila |
| POST | /api/database/notes | Crear una fila |
| PATCH | /api/database/notes/:id | Actualizar una fila |
| DELETE | /api/database/notes/:id | Eliminar 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ón | Valor predeterminado | Efecto |
|---|---|---|
schema | Exportación nombrada en config/database/schema.ts | Anula la carga automática del esquema |
api | true | false deshabilita todas las rutas generadas |
api.tables | Todas las tablas declaradas | Limita qué tablas tienen rutas |
api.writes | true | false conserva solo las rutas de lectura |
api.writes.tables | Todas las tablas expuestas | Limita qué tablas expuestas aceptan escrituras |
api.writes.operations | create, update, delete | Limita 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.