@interlinked-svcs/firebase-plugin
v0.0.5
Published
Core interlinked-svcs lib for connecting to firebase services.
Downloads
587
Readme
@interlinked-svcs/firebase-plugin
Firestore + Firebase Auth helpers for Node, with OData-shaped query parsing aligned to connectSpec REST conventions (same options surface as @interlinked-svcs/core GetOptions / APIResource.getQuery). Use this to turn $filter, $orderby, $top, and $skip into firebase-admin queries.
Requirements: Node 20+, Firebase project credentials (environment / ADC as usual for firebase-admin).
Install
npm install @interlinked-svcs/firebase-plugin firebase-admin fastifyPeer usage also expects @interlinked-svcs/core if you reuse buildOpenIdArrayElementMatchFilter or GetOptions types from that package.
Fastify plugin
Register once; decorators expose the Admin SDK app, Firestore, Auth, and CRUD helpers.
import Fastify from "fastify";
import { firebase } from "@interlinked-svcs/firebase-plugin";
const app = Fastify();
await app.register(firebase);
// Examples (after configuring firebase-admin credentials in your environment):
const user = await app.auth.getUser(uid);
await app.firestoreOps.create("orders", { customerId: "c1", total: 100 });
const doc = await app.firestoreOps.read("orders", documentId);Listing with OData-style options
Pass ListOptions (where / orderBy / limit / offset) directly to firestoreOps.list:
await app.firestoreOps.list("services", {
where: [{ field: "status", operator: "==", value: "active" }],
orderByClauses: [{ field: "updatedAt", direction: "desc" }],
limit: 50,
offset: 0,
});Or build ListOptions from a query string / object shaped like OData query parameters:
import { parseODataToListOptions } from "@interlinked-svcs/firebase-plugin";
const options = parseODataToListOptions({
$filter: "status eq 'active'",
$orderby: "updatedAt desc",
$top: "50",
$skip: "10",
});
const rows = await app.firestoreOps.list("services", options);Equivalent with a raw query substring (leading ? is optional):
parseODataToListOptions("?$filter=typeCode eq 'Session'&$top=100");OData → Firestore (without Fastify)
Parse $filter only
import { parseWhereFilter } from "@interlinked-svcs/firebase-plugin";
const conditions = parseWhereFilter(`tier ne 'trial' and count ge 10`);
// Maps to Firestore-compatible where clauses before running a query builder.Apply options to any collection reference
import { getFirestore } from "firebase-admin/firestore";
import { applyFirestoreListOptions, parseGetOptionsToListOptions } from "@interlinked-svcs/firebase-plugin";
import type { GetOptions } from "@interlinked-svcs/core/types";
const db = getFirestore();
const opts: GetOptions = {
$filter: "status eq 'open'",
$orderby: "createdAt asc",
$top: 25,
};
const listOpts = parseGetOptionsToListOptions(opts);
const snapshot = await applyFirestoreListOptions(db.collection("orders"), listOpts).get();Shortcut that parses GetOptions and runs get():
import { queryCollectionFromGetOptions } from "@interlinked-svcs/firebase-plugin";
import type { GetOptions } from "@interlinked-svcs/core/types";
const orders: Array<{ id: string } & Record<string, unknown>> = await queryCollectionFromGetOptions(getFirestore(), "orders", {
$filter: `amount lt 500 and region eq 'US'`,
$top: 10,
});Ignore path on GetOptions here: that field is only for HTTP routes in APIResource.getQuery. You always pass the Firestore collection ID as the second argument above.
OpenId arrays and @interlinked-svcs/core
If $filter uses the OData fragment produced by buildOpenIdArrayElementMatchFilter (Party / Service id arrays), it looks like:
id/any(i: i/content eq '…' and i/schemeId eq 'service-name')
That form is rewritten internally to array_contains with a JSON map so Firestore can match one element:
import { buildOpenIdArrayElementMatchFilter } from "@interlinked-svcs/core/utils";
import { parseWhereFilter } from "@interlinked-svcs/firebase-plugin";
const $filter =
"(" +
buildOpenIdArrayElementMatchFilter({
content: "my-service",
schemeId: "service-name",
collectionPath: "id",
}) +
") and typeCode eq 'Session'";
parseWhereFilter($filter);You can also express the same predicate directly with array_contains + a JSON object in $filter (see tests in test/).
What is supported vs not
Supported on parseGetOptionsToListOptions / queryCollectionFromGetOptions:
| Input | Maps to |
|------------------|--------------------------------------------------------|
| $filter | where[] (see parser rules below) |
| $orderby | Multiple fields, field asc / desc, comma-separated |
| $top / $skip | limit / offset |
These GetOptions fields are rejected with ODataParseError (they do not map to a single bare collection get): $select, $expand, $search, $count.
$filter grammar (combined with and only; or is rejected):
Comparators
eqneltlegtgefield in (...)/field not in (...)Extensions:
array_contains(field, literal | {…}),array_contains_any(field, …)Slash paths
a/bbecome dotted Firestore pathsa.b
Scripts
npm run build # compile to dist/
npm run test # jest
npm run typecheck # tsc --noEmit over src — see tsconfig.test.json for testsLicense
MIT. See package.json.
