fires2rest
v0.3.0
Published
Lightweight Firestore REST API client with full transaction support for serverless environments
Downloads
691
Maintainers
Readme
Fires2REST
Fires2REST (Firestore REST with Transactions) is a TypeScript library that provides a simple and efficient way to perform transactions and other operations on Firestore documents using the REST API.
It's a drop-in replacement for the official Firebase Admin SDK (the Firestore part), see the comparison.
Why "Fires2REST"
Firestore REST with Transactions → Firestore REST → FirestoREST → Fires2REST
Features
- 🔥 Full Firestore REST API support - CRUD operations via REST
- ⚡ Transaction support - Atomic reads and writes with automatic retry
- 🔍 Query support - Filter, order, limit, and paginate collections
- 🌐 Serverless ready - Works in Cloudflare Workers, Deno, Bun, and any JS runtime
- 🧪 Emulator support - Local development with Firebase Emulator
- 📦 Lightweight - The only dependency is
josefor JWT auth - 🔄 FieldValue support - serverTimestamp, increment, delete, arrayUnion, arrayRemove
Installation
pnpm install fires2restQuick Start
import { Firestore, FieldValue } from "fires2rest";
// Initialize with service account credentials
const db = Firestore.useServiceAccount("your-project-id", {
clientEmail: "[email protected]",
privateKey: "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n",
});
// Get a document
const userRef = db.doc("users/alice");
const snap = await userRef.get();
if (snap.exists) {
console.log(snap.data()); // { name: "Alice", age: 30 }
}
// Set a document
await userRef.set({
name: "Alice",
age: 30,
createdAt: FieldValue.serverTimestamp(),
});
// Update a document
await userRef.update({
age: 31,
visits: FieldValue.increment(1),
});
// Delete a document
await userRef.delete();Transactions
Transactions provide atomic read-modify-write operations:
// Transfer balance between accounts
const result = await db.runTransaction(async (txn) => {
const fromRef = db.doc("accounts/alice");
const toRef = db.doc("accounts/bob");
const fromSnap = await txn.get(fromRef);
const toSnap = await txn.get(toRef);
const fromBalance = fromSnap.data()?.balance ?? 0;
const toBalance = toSnap.data()?.balance ?? 0;
if (fromBalance < 100) {
throw new Error("Insufficient balance");
}
txn.update(fromRef, { balance: fromBalance - 100 });
txn.update(toRef, { balance: toBalance + 100 });
return { transferred: 100 };
});API Reference
See API Reference for detailed documentation.
Firestore
Main client class.
Static Factory Methods:
// Using service account (for server-side usage)
const db = Firestore.useServiceAccount(projectId, {
clientEmail: "[email protected]",
privateKey: "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n",
}, databaseId?);
// Using Firebase Emulator (for local development)
const db = Firestore.useEmulator({
emulatorHost: "127.0.0.1:8080", // optional, defaults to "127.0.0.1:8080"
projectId: "demo-project", // optional, defaults to "demo-no-project"
databaseId: "(default)", // optional, defaults to "(default)"
admin: true, // optional, defaults to true (bypasses security rules)
});Methods:
collection(path)- Get aCollectionReferencedoc(path)- Get aDocumentReferencerunTransaction(updateFn, options?)- Run a transaction
DocumentReference<T>
Reference to a document.
Properties:
id- Document IDpath- Full document pathparent- ParentCollectionReference
Methods:
get()- Get document snapshotset(data, options?)- Set document data (options:{ merge: boolean })update(data)- Update document fieldsdelete()- Delete documentcollection(path)- Get a subcollection
CollectionReference<T>
Reference to a collection.
Properties:
id- Collection IDpath- Full collection path
Methods:
doc(id?)- Get aDocumentReference(auto-generates ID if not provided)add(data)- Add document with auto-generated IDwhere(field, op, value)- Filter by field valueorderBy(field, direction?)- Order resultslimit(n)/limitToLast(n)- Limit resultsoffset(n)- Skip resultsstartAt(...values)/startAfter(...values)- Cursor paginationendAt(...values)/endBefore(...values)- Cursor paginationselect(...fields)- Field projectionget()- Execute query and returnQuerySnapshotcount()- Count matching documents
Queries
Build queries using chainable methods:
// Filter, order, and limit
const snapshot = await db
.collection("users")
.where("age", ">=", 18)
.where("active", "==", true)
.orderBy("createdAt", "desc")
.limit(10)
.get();
// Iterate results
snapshot.forEach((doc) => {
console.log(doc.id, doc.data());
});
// Count documents
const countSnap = await db
.collection("users")
.where("active", "==", true)
.count();
console.log("Active users:", countSnap.data().count);
// Cursor-based pagination
const page2 = await db
.collection("users")
.orderBy("name")
.startAfter("Alice")
.limit(10)
.get();Supported filter operators:
==,!=- Equality<,<=,>,>=- Comparisonarray-contains,array-contains-any- Array queriesin,not-in- Inclusion queries
FieldValue
Special field values for atomic operations.
FieldValue.serverTimestamp(); // Server-generated timestamp
FieldValue.increment(n); // Increment numeric field by n
FieldValue.delete(); // Delete this field
FieldValue.arrayUnion(...elements); // Add unique elements to array
FieldValue.arrayRemove(...elements); // Remove elements from arrayGeoPoint
Represents a geographic coordinate.
const location = new GeoPoint(37.7749, -122.4194);Timestamp
Represents a timestamp with nanosecond precision.
const now = Timestamp.now();
const fromDate = Timestamp.fromDate(new Date());
const fromMillis = Timestamp.fromMillis(Date.now());Type Safety
Use generics for type-safe document data:
interface User {
name: string;
email: string;
age: number;
}
const userRef = db.doc("users/alice") as DocumentReference<User>;
const snap = await userRef.get();
const user = snap.data(); // User | undefined
console.log(user?.name); // TypeScript knows this is stringEmulator Support
Use the Firebase Emulator for local development and testing:
import { Firestore } from "fires2rest";
// Connect to local emulator with all defaults
const db = Firestore.useEmulator();
// Or customize the connection
const db = Firestore.useEmulator({
emulatorHost: "127.0.0.1:8095",
projectId: "demo-no-project",
admin: true, // bypass security rules
});Start the Firebase Emulator:
pnpm firebase emulators:startEnvironment Variables
Set up your service account credentials:
FIREBASE_PROJECT_ID=your-project-id
FIREBASE_CLIENT_EMAIL=your-service-account@project.iam.gserviceaccount.com
FIREBASE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"
# For emulator usage
FIRESTORE_EMULATOR_HOST=127.0.0.1:8095License
MIT
