Research
Security News
Malicious npm Packages Inject SSH Backdoors via Typosquatted Libraries
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
@firecode/admin
Advanced tools
Firecode is a Node.js library that lets you efficiently traverse Firestore collections.
When you have millions of documents in a collection, you can't just get all of them at once as your program's memory usage will explode. Firecode's configurable traverser objects let you do this in a simple, intuitive and memory-efficient way using batching.
Firecode is an extremely light, well-typed, zero-dependency library that is useful in a variety of scenarios. You can use it in database migration scripts (e.g. when you need to add a new field to all docs) or a scheduled Cloud Function that needs to check every doc in a collection periodically or a locally run script that retrieves some data from a collection.
Firecode is designed to work with the Firebase Admin SDK so if you haven't already installed it, run
npm install firebase-admin
Then run
npm install @firecode/admin
There are only 2 kinds of objects you need to be familiar with when using this library:
Traverser: An object that walks you through a collection of documents (or more generally a Traversable).
Migrator: A convenience object used for database migrations. It lets you easily write to the documents within a given traversable and uses a traverser to do that. You can easily write your own migration logic in the traverser callback if you don't want to use a migrator.
Suppose we have a users
collection and we want to send an email to each user. This is how easy it is to do that efficiently with a Firecode traverser:
import { firestore } from 'firebase-admin';
import { createTraverser } from '@firecode/admin';
const usersCollection = firestore().collection('users');
const traverser = createTraverser(usersCollection);
const { batchCount, docCount } = await traverser.traverse(async (batchDocs, batchIndex) => {
const batchSize = batchDocs.length;
await Promise.all(
batchDocs.map(async (snapshot) => {
const { email, firstName } = snapshot.data();
await sendEmail({ to: email, content: `Hello ${firstName}!` });
})
);
console.log(`Batch ${batchIndex} done! We emailed ${batchSize} users in this batch.`);
});
console.log(`Traversal done! We emailed ${docCount} users in ${batchCount} batches!`);
We are doing 3 things here:
users
collectioncreateTraverser()
function.traverse()
with an async callback that is called for each batch of document snapshotsThis pretty much sums up the core functionality of this library! The .traverse()
method returns a Promise that resolves when the entire traversal finishes, which can take a while if you have millions of docs. The Promise resolves with an object containing the traversal details e.g. the number of docs you touched.
const projectsColRef = firestore().collection('projects');
const traverser = createFastTraverser(projectsColRef, {
batchSize: 500,
// This means we are prepared to hold 500 * 20 = 10,000 docs in memory
maxConcurrentBatchCount: 20,
});
const { docCount } = await traverser.traverse(async (_, batchIndex) => {
console.log(`Gonna process batch ${batchIndex} now!`);
// ...
});
console.log(`Traversed ${docCount} projects super-fast!`);
const projectsColRef = firestore().collection('projects');
const migrator = createBatchMigrator(projectsColRef);
const { migratedDocCount } = await migrator.update('isCompleted', false);
console.log(`Updated ${migratedDocCount} projects!`);
type UserDoc = {
firstName: string;
lastName: string;
};
const usersColRef = firestore().collection('users') as firestore.CollectionReference<UserDoc>;
const migrator = createBatchMigrator(usersColRef);
const { migratedDocCount } = await migrator.update((snap) => {
const { firstName, lastName } = snap.data();
return {
fullName: `${firstName} ${lastName}`,
};
});
console.log(`Updated ${migratedDocCount} users!`);
const projectsColRef = firestore().collection('projects');
const fastTraverser = createFastTraverser(projectsColRef, { maxConcurrentBatchCount: 25 });
const fastMigrator = createBatchMigrator(fastTraverser);
const { migratedDocCount } = await fastMigrator.update('isCompleted', false);
console.log(`Updated ${migratedDocCount} projects super-fast!`);
const walletsWithNegativeBalance = firestore().collection('wallets').where('money', '<', 0);
const migrator = createBatchMigrator(walletsWithNegativeBalance, {
// We want each batch to have 500 docs. The size of the very last batch may be less than 500
batchSize: 500,
// We want to wait before moving to the next batch
sleepBetweenBatches: true,
// We want to wait 500ms before moving to the next batch
sleepTimeBetweenBatches: 500,
});
// Wipe out their debts!
const { migratedDocCount } = await migrator.set({ money: 0 });
console.log(`Updated ${migratedDocCount} wallets!`);
type PostDoc = {
text: string;
postedAt?: firestore.Timestamp;
};
const postsColGroup = firestore().collectionGroup('posts') as firestore.CollectionGroup<PostDoc>;
const migrator = createBatchMigrator(postsColGroup);
const { migratedDocCount } = await migrator
.withPredicate(
// Ignore if it doesn't have a `postedAt` field
(snap) => snap.data().postedAt !== undefined
)
.update((snap) => {
const { postedAt } = snap.data();
return {
publishedAt: postedAt!, // Safe to assert now
postedAt: firestore.FieldValue.delete(),
};
});
console.log(`Updated ${migratedDocCount} posts!`);
You can find the full API reference for @firecode/admin
here. Here are some of the core functions that this library provides.
Creates a traverser that facilitates Firestore collection traversals. When traversing the collection, this traverser invokes a specified async callback for each batch of document snapshots and waits for the callback Promise to resolve before moving to the next batch.
batchSize
) * (Q(batchSize
) + C))batchSize
* D + S)where:
batchSize
): average batch query timeCreates a fast traverser that facilitates Firestore collection traversals. When traversing the collection, this traverser invokes a specified async callback for each batch of document snapshots and immediately moves to the next batch. It does not wait for the callback Promise to resolve before moving to the next batch so there is no guarantee that any given batch will finish processing before a later batch. This traverser uses more memory but is significantly faster than the default traverser.
batchSize
) * Q(batchSize
))maxConcurrentBatchCount
* (batchSize
* D + S))where:
batchSize
): average batch query timeCreates a migrator that facilitates database migrations. The migrator accepts a custom traverser to traverse the collection. Otherwise it will create a default traverser with your desired traversal config. This migrator does not use atomic writes so it is possible that when a write fails other writes go through.
traverser
) where C = W(batchSize
)traverser
) where S = O(batchSize
)where:
batchSize
): average batch write timetraverser
): time complexity of the underlying traversertraverser
): space complexity of the underlying traverserCreates a migrator that facilitates database migrations. The migrator accepts a custom traverser to traverse the collection. Otherwise it will create a default traverser with your desired traversal config. This migrator uses batch writes so the entire operation will fail if a single write isn't successful.
traverser
) where C = W(batchSize
)traverser
) where S = O(batchSize
)where:
batchSize
): average batch write timetraverser
): time complexity of the underlying traversertraverser
): space complexity of the underlying traverserThis project is still very new and we have a lot to work on. We will be moving fast and until we release v1, there may be breaking changes between minor versions (e.g. when upgrading from 0.4 to 0.5). However, all breaking changes will be documented and you can always use our Releases page as a changelog.
This project is made available under the MIT License.
FAQs
A collection traversal library for Firestore
The npm package @firecode/admin receives a total of 802 weekly downloads. As such, @firecode/admin popularity was classified as not popular.
We found that @firecode/admin demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.