@maykonpaulo/maestro-provider-firestore
v1.0.3
Published
Firestore provider for @maykonpaulo/maestro-core — a document-family provider, per ADR 0007. Real CRUD, sampling-based introspection with native-type mapping (Timestamp/GeoPoint/DocumentReference), and a documented no-op ensureEntity — Firestore has no ex
Downloads
1,647
Readme
@maykonpaulo/maestro-provider-firestore
📖 Guia completo — criar o admin de qualquer sistema: está no README do pacote
@maykonpaulo/maestro-serverno npm → https://www.npmjs.com/package/@maykonpaulo/maestro-server
A Firestore provider for @maykonpaulo/maestro-core — continuing ADR 0007 — Universal Provider Strategy into the document-family, alongside provider-mongodb/provider-couchbase. It implements all three of the core's provider contracts against a real Firestore database (or emulator):
IntrospectionProvider— discovers top-level collections and infers each one's shape by sampling documents, mapping Firestore's nativeTimestamp/GeoPoint/DocumentReferencetypes to dedicatedFieldTypes where one exists. Every entity is reported withschemaless: true.DatasourceProvider— reallist/findById/create/update/delete/countagainst the native@google-cloud/firestoreSDK.SchemaWriteProvider—ensureEntityis a documented no-op (see below).
Installation
npm install @maykonpaulo/maestro-provider-firestore @maykonpaulo/maestro-core@maykonpaulo/maestro-core and @google-cloud/firestore are peer dependencies — install the versions your project already uses.
Usage
import { runIntrospectionProvider } from '@maykonpaulo/maestro-core';
import { createFirestoreProvider } from '@maykonpaulo/maestro-provider-firestore';
const provider = createFirestoreProvider({ projectId: 'my-gcp-project' });
// Introspection — samples documents, validated/normalized via the core
const { result } = await runIntrospectionProvider(provider);
// Real CRUD against the same database
const created = await provider.create({ table: 'users', primaryKey: 'id', data: { name: 'Ada' } });
const page = await provider.list({ table: 'users', primaryKey: 'id', pagination: { strategy: 'page', page: 1, pageSize: 20 } });Configuration
export interface FirestoreProviderOptions {
firestore?: Firestore; // an already-constructed client — for tests/embedding; caller owns its lifecycle
projectId?: string; // the GCP project ID
host?: string; // emulator host, e.g. 'localhost' — omit to use real Firestore with Application Default Credentials
port?: number; // emulator port
ssl?: boolean; // set false when pointing at a local emulator over plain HTTP
maxDocuments?: number; // caps documents read per top-level collection during introspection — unset by default (scans the whole collection)
}Exactly one of firestore or projectId must be provided.
id handling
Firestore always assigns a native auto-ID to a document unless the caller supplies one. This provider follows that default:
create()without a value atdata[primaryKey]callscollection.add(data), letting Firestore generate the ID.create()with a value atdata[primaryKey]callscollection.doc(id).set(data)instead.findById/update/deletealways address the document by its Firestore document ID.
What is introspected
Since there is no system catalog, introspection reads every document in every top-level collection by default (maxDocuments is unset unless you explicitly cap it) and infers fields, nullability and types the same way provider-mongodb does, with native-type awareness:
Timestampfields are reported as'datetime'.GeoPointandDocumentReferencefields are reported as'json'(see "known limitations").- Every other value maps the same way Mongo's sampling does (arrays, booleans, integers vs. floats, nested objects as
'json'). schemaless: trueon every entity, for the same honesty reasonprovider-mongodb's README documents.
What is not introspected (known limitations)
- Relations — Firestore has no foreign-key constraint to read, same reasoning as every other NoSQL provider in this monorepo;
relationsis always[]. - Subcollections — only top-level collections are discovered; subcollection recursion is out of scope for v1.
GeoPoint— normalized to a plain{ latitude, longitude }object; there is no native geoFieldType.DocumentReference— normalized to{ path }; the reference path is preserved but not resolved or typed further.
Schema creation (ensureEntity)
Firestore has no explicit "create collection" API and no non-circular "does this collection exist" signal (listCollections() only lists collections that already contain at least one document — checking existence by writing a sentinel document would itself create the collection, and is destructive/side-effecting). ensureEntity therefore always reports created: false; the collection "exists" the moment its first document is written via create(). See src/FirestoreSchemaWrite.ts for the full reasoning.
Testing
Unit tests (tests/firestore-provider-construction.test.ts, tests/firestore-values.test.ts) cover constructor validation and native-type normalization/inference in isolation, no Docker required. Integration tests (tests/firestore-integration.test.ts) run against a real Firestore emulator started per test run via @testcontainers/gcloud's FirestoreEmulatorContainer — run them with pnpm test:integration (requires a running Docker daemon).
Turnkey admin (point at your database, get a UI)
This provider plugs straight into the Maestro turnkey layer: install
@maykonpaulo/maestro-server and
@maykonpaulo/maestro-admin, point them at your database and get a
full governed admin (list/CRUD/RBAC/audit + a metadata-driven UI) with no
per-entity code.
import { createFirestoreProvider } from '@maykonpaulo/maestro-provider-firestore';
import { createMaestroServer, createIntrospectedEngine } from '@maykonpaulo/maestro-server';
const provider = createFirestoreProvider({ projectId: 'my-gcp-project' });
const engine = await createIntrospectedEngine({ provider, access: 'full' });
await createMaestroServer({ engine }).listen(3000);See the Turnkey admin guide.
