BurnKit is a powerful, decorator-based TypeScript SDK for Firebase that provides a clean, type-safe API for working with both Firestore and the Realtime Database. Designed with a forward-thinking approach, BurnKit simplifies data modeling, validation, and querying while embracing modern best practices.
- TypeScript Decorators for intuitive entity and field mapping
- Type-Safe Queries with a robust, chainable query builder
- Automatic Data Validation & Transformation using class-transformer-validator
- Repository Pattern for clean, maintainable data access
- Batch Operations for efficient bulk updates
- Enhanced Subcollection Support with dedicated decorators and metadata utilities
- Nested Entity Support for managing complex relationships
- Realtime Database Integration with comprehensive CRUD and query capabilities
npm install burnkit firebase-admin reflect-metadata
# or
yarn add burnkit firebase-admin reflect-metadata
Note: BurnKit requires reflect-metadata for decorators to function properly. Ensure it is imported once in your application's entry point.
import * as admin from "firebase-admin";
import "reflect-metadata"; // Ensure this is imported once
admin.initializeApp({
credential: admin.credential.cert(require("./serviceAccountKey.json")),
});
BurnKit leverages decorators to define your Firestore and Realtime Database entities effortlessly. For example:
import {
Collection,
Field,
ID,
CreatedAt,
UpdatedAt,
Subcollection,
} from "burnkit";
@Collection("users")
class User {
@ID()
id: string;
@Field()
name: string;
@Field({ index: true })
email: string;
@Field()
age: number;
@Field({
transformer: {
toFirestore: (roles: string[]) => roles.join(","),
fromFirestore: (value: string) => (value ? value.split(",") : []),
},
})
roles: string[];
@CreatedAt()
createdAt: Date;
@UpdatedAt()
updatedAt: Date;
// Optional nested entity
profile?: UserProfile;
}
@Collection("posts")
class Post {
@ID()
id: string;
@Field()
title: string;
@Field()
content: string;
@Field()
authorId: string;
@CreatedAt()
createdAt: Date;
@UpdatedAt()
updatedAt: Date;
// Non-persisted field to hold comments
comments?: Comment[];
}
// Define a subcollection for comments using the new Subcollection decorator
@Subcollection(Post)
class Comment {
@ID()
id: string;
@Field()
content: string;
@Field()
authorId: string;
@CreatedAt()
createdAt: Date;
@UpdatedAt()
updatedAt: Date;
}
// Customize the subcollection name if desired
@Subcollection(Post, "post-comments")
class CustomComment {
// Define fields as needed
}
BurnKit's repository pattern simplifies CRUD operations and query building:
import { BurnKit } from "burnkit";
// Obtain a repository for the User entity
const userRepo = BurnKit.getRepository(User);
// Create a new user
async function createUser() {
const newUser = await userRepo.create({
name: "John Doe",
email: "[email protected]",
age: 30,
roles: ["user"],
});
console.log(`Created user with ID: ${newUser.id}`);
return newUser;
}
// Retrieve a user by ID
async function findUser(id: string) {
const user = await userRepo.findById(id);
if (user) {
console.log(`Found user: ${user.name}`);
}
return user;
}
// Query users with type-safe queries
async function findActiveUsers() {
const users = await userRepo
.query()
.where("age", ">", 18)
.where("roles", "array-contains", "active")
.orderBy("name")
.limit(10)
.get();
console.log(`Found ${users.length} active users`);
return users;
}
// Update a user's details
async function updateUser(id: string) {
await userRepo.update(id, {
name: "John Smith",
age: 31,
});
console.log("User updated");
}
// Delete a user
async function deleteUser(id: string) {
await userRepo.delete(id);
console.log("User deleted");
}
BurnKit streamlines working with subcollections using dedicated decorators and helper functions. In addition to using the subcollection decorator, you can generate Firestore paths using metadata utilities:
import { BurnKit } from "burnkit";
// Get the repository for posts
const postRepo = BurnKit.getRepository(Post);
// Create a post
const post = await postRepo.create({
title: "Getting Started with BurnKit",
content: "BurnKit is an innovative TypeScript SDK for Firebase...",
authorId: "user123",
});
// Retrieve a repository for the comments subcollection of a specific post
const commentsRepo = BurnKit.getSubcollectionRepository(Comment, post.id);
// Add a comment to the post
const comment = await commentsRepo.create({
content: "Great article!",
authorId: "user456",
});
// Retrieve all comments for the post
const comments = await commentsRepo.findAll();
For cases where you need to generate a subcollection path manually, use:
import { buildSubcollectionPath } from "burnkit";
const path = buildSubcollectionPath(Comment, "postId123");
console.log("Subcollection path:", path);
BurnKit also supports managing nested entities, making it easy to model and interact with related data:
import { NestedEntityRepository, BurnKit } from "burnkit";
const userProfileRepo = new NestedEntityRepository(
User, // Parent entity class
UserProfile, // Child entity class
"profile", // Field name in User to store the profile
(userId) => `users/${userId}/profile` // Path builder for the nested collection
);
async function createUserWithProfile() {
const userData = {
name: "Jane Doe",
email: "[email protected]",
age: 28,
roles: ["user"],
};
const profileData = {
bio: "Software engineer and avid hiker",
avatarUrl: "https://example.com/avatar.jpg",
phoneNumber: "+1234567890",
};
const user = await userProfileRepo.createWithNested(userData, profileData);
console.log(`Created user with profile: ${user.id}`);
return user;
}
async function loadUserWithProfile(userId: string) {
const user = await userProfileRepo.loadWithNested(userId);
if (user && user.profile) {
console.log(`User: ${user.name}`);
console.log(`Profile Bio: ${user.profile.bio}`);
}
return user;
}
BurnKit's Realtime Database integration provides comprehensive CRUD operations and advanced query capabilities:
import { RealtimeRepository } from "burnkit";
interface ChatMessage {
message: string;
sender: string;
timestamp: number;
isRead?: boolean;
}
const chatRepo = new RealtimeRepository<ChatMessage>("chats");
// Add a new chat message
async function sendMessage(sender: string, message: string) {
const newMessage = await chatRepo.push({
message,
sender,
timestamp: Date.now(),
});
console.log(`Message sent with ID: ${newMessage.id}`);
return newMessage;
}
// Listen for real-time updates
function listenForMessages(callback: (messages: ChatMessage[]) => void) {
const unsubscribe = chatRepo
.query()
.orderByChild("timestamp")
.onValue(callback);
return unsubscribe;
}
Marks a class as a Firestore collection.
@Collection("users")
class User {}
Marks a class as a Firestore subcollection. The subcollection name defaults to the lowercase class name if not specified.
// Basic usage
@Subcollection(Post)
class Comment {}
// With custom collection name
@Subcollection(Post, "post-comments")
class CustomComment {}
Marks a property as a Firestore document field.
@Field({ index: true })
email: string;
Options include:
- name?: string — Custom field name in Firestore
- index?: boolean — Enable indexing for this field
- transformer?: { toFirestore?, fromFirestore? } — Custom transformers
Marks a property as the document ID.
@ID()
id: string;
Automatically sets the creation timestamp.
@CreatedAt()
createdAt: Date;
Automatically updates the timestamp on modifications.
@UpdatedAt()
updatedAt: Date;
Retrieves metadata for a subcollection from a class.
Generates a Firestore path to a subcollection for a given parent document.
import { buildSubcollectionPath } from "burnkit";
const path = buildSubcollectionPath(Comment, "parentDocId");
Retrieves a repository for a specified entity.
const userRepo = BurnKit.getRepository(User);
Retrieves a repository for a subcollection belonging to a specific parent document.
const commentsRepo = BurnKit.getSubcollectionRepository(Comment, postId);
Clears the internal repository cache.
BurnKit.clearCache();
- create(data: Partial, id?: string): Promise — Create a new entity.
- findById(id: string): Promise<T | null> — Retrieve an entity by ID.
- getById(id: string): Promise — Retrieve an entity by ID (throws if not found).
- update(id: string, data: Partial): Promise — Update an entity.
- delete(id: string): Promise — Delete an entity.
- findAll(): Promise<T[]> — Retrieve all entities in a collection.
- query(): QueryBuilder — Create a query builder for advanced queries.
- batch(operations: (batch: FirestoreBatchHelper) => void): Promise — Execute batch operations.
- where(field: string, operator: FirestoreOperator, value: any): QueryBuilder — Add filtering criteria.
- orderBy(field: string, direction?: "asc" | "desc"): QueryBuilder — Sort query results.
- limit(limit: number): QueryBuilder — Limit the number of documents.
- offset(offset: number): QueryBuilder — Skip a number of documents.
- get(): Promise<T[]> — Execute the query.
- getFirst(): Promise<T | null> — Retrieve the first matching document.
- count(): Promise — Count matching documents.
- push(data: Partial): Promise<T & { id: string }> — Create a new entry with an auto-generated ID.
- set(id: string, data: Partial): Promise — Create or update an entry.
- update(id: string, data: Partial): Promise — Update an existing entry.
- get(id: string): Promise<(T & { id: string }) | null> — Retrieve an entry by ID.
- getAll(): Promise<(T & { id: string })[]> — Retrieve all entries.
- remove(id: string): Promise — Delete an entry.
- query(): RealtimeQueryBuilder — Create a query builder for the Realtime Database.
- orderByChild(child: string): RealtimeQueryBuilder — Order results by a specific child key.
- equalTo(child: string, value: string | number | boolean | null): RealtimeQueryBuilder — Filter results by equality.
- between(child: string, startValue: any, endValue: any): RealtimeQueryBuilder — Filter results within a range.
- limitToFirst(limit: number): RealtimeQueryBuilder — Limit to the first N results.
- limitToLast(limit: number): RealtimeQueryBuilder — Limit to the last N results.
- startAt(value: any): RealtimeQueryBuilder — Start results at a specific value.
- endAt(value: any): RealtimeQueryBuilder — End results at a specific value.
- get(): Promise<(T & { id: string })[]> — Execute the query.
- onValue(callback: (data: (T & { id: string })[]) => void): () => void — Listen for real-time updates.
- Create a Firebase Project: Visit the Firebase Console.
- Generate a Service Account Key:
- Navigate to Project Settings > Service Accounts.
- Click "Generate New Private Key" and download the JSON file.
- Configure Firebase:
npm run setup-firebase
- Test Firebase Connection:
npm run test-firebase
- Run Example Files:
ts-node examples/main-collection-example.ts
Security Note: The serviceAccountKey.json file contains sensitive information and should not be committed to version control. It is excluded via .gitignore.
BurnKit utilizes GitHub Actions for continuous integration and deployment.
- Testing & Linting: On each push or pull request to the main branch:
- Unit tests and Firebase connectivity tests are run.
- Code linting and TypeScript checks are executed.
- Deployment: When a new version tag (e.g., v1.0.0) is pushed:
- All tests and linting are performed.
- If successful, the package is published to npm.
Configure the following GitHub secrets:
- FIREBASE_SERVICE_ACCOUNT_KEY: Base64 encoded Firebase service account key JSON.
- NPM_TOKEN: Your npm access token.
- Update the version in package.json.
- Commit changes: git commit -am "Release v1.0.0".
- Tag the release: git tag v1.0.0.
- Push commits and tags: git push && git push --tags.
- GitHub Actions will automatically publish the package to npm.
This section is automatically maintained by BurnKit documentation.