npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@mondomob/gae-js-firestore

v17.0.0

Published

Use Firestore in Native mode as your app db including [DataLoader](https://github.com/graphql/dataloader) implementation GraphQL and request level caching. ## Installation

Downloads

169

Readme

GAE JS FIRESTORE

Use Firestore in Native mode as your app db including DataLoader implementation GraphQL and request level caching.

Installation

npm install @mondomob/gae-js-firestore

Components

FirestoreProvider

Initialise Firestore to be accessed elsewhere in your app.

// On app startup
firestoreProvider.init();

// Anywhere else in your app
const firestore = firestoreProvider.get();
const doc = await firestore.doc('my-items/id123').get();

FirestoreLoader

Dataloader implementation to help batch and cache db requests. Used internally by FirestoreRepository

// Apply middleware to create a new dataloader on each request
app.use(firestoreLoader());

FirestoreRepository

Access your collections through typed repositories.

// Define your class entity
interface DemoItem {
  id: string;
  name: string;
}

// Initialise repository for the collection we want to access data in
const repository = new FirestoreRepository<DemoItem>("demo-items");

// OR define a custom class first
class DemoItemRepository extends FirestoreRepository<DemoItem> {
  constructor() {
    super("demo-items");
  }
}
const repository = new DemoItemsRepository();

// Save an item
await repository.save({ id: "id123", name: "test item" });

// Get an item
const item = await repository.get("id123");

// Delete item with single id
await repository.delete("id123");

// Delete items with array of ids
await repository.delete(["id123", "id234"]);

// Supply options to delete (documents MUST exist)
await repository.delete(["id123", "id234"], { exists: true });

// Delete all items in collecitons and recursively delete decendants. Uses https://cloud.google.com/nodejs/docs/reference/firestore/latest/firestore/firestore#_google_cloud_firestore_Firestore_recursiveDelete_member_1_
await repository.delete("id123", "id234");
// Delete all will FAIL when inside a transaction because it does not work within transactions. The underlying node lib streams batches of queries and deletions. Transactions requrie all reads before writes and have volume limitations.

// Query items
const list = await repository.query();

// Query items with ordering
const results = repository.query({
 sort: {
   property: "owner",
   direction: "desc",
 }
})

// Query items for specific fields (i.e. projection)
const results = await repository.query({
  filters: [
    {
      fieldPath: "owner",
      opStr: "==",
      value: "user1",
    },
  ],
  select: ["name", "otherProp"],
});

// Query items using Firebase Filter, allows for OR queries
const results = await repository.query({
  filters: Filter.or(
    Filter.where("id", "==", "123"), 
    Filter.where("id", "==", "567"))
  }
)

// Query items for ids only (empty projection)
const results = await repository.query({
  filters: [
    {
      fieldPath: "owner",
      opStr: "==",
      value: "user1",
    },
  ],
  select: [],
});

// Result: [{ id: "your-id" }, ...]


// Query items with limit/offset
const results = await repository.query({
  filters: [
    {
      fieldPath: "owner",
      opStr: "==",
      value: "user1",
    },
  ],
  limit: 2,
  offset: 2,
});

// Query items with cursors
const results = await repository.query({
  sort: { property: "owner" },
  startAfter: ["user2"],
});

// Count items in a collection using aggregation query https://firebase.google.com/docs/firestore/query-data/aggregation-queries
const count = await repository.count();

// Count items matching filter (only "filters" property may be supplied)
const countWithFilter = await repository.count({
 filters: [
     {
       fieldPath: "owner",
       opStr: "==",
       value: "user1",
     },
   ],
});

// Query only the ids using a projection query
const results = await repository.queryForIds({
  sort: { property: "owner" },
  startAfter: ["user2"],
  // All query options supported, except for the "select" prop
});
// Results would just be an array of the id strings

TimestampedRepository

Convenience Repository type to auto-populate createdAt/updatedAt timestamps on insert/save/update

// Define your class entity
interface DemoItem extends TimestampedEntity {
  name: string;
}

// Initialise repository
const repository = new TimestampedRepository<DemoItem>("demo-items");

// Create a new item with helper method
await repository.save({ ...newTimestampedEntity("id123"), name: "test item" });

// Save an item and updatedAt will get set to current time
const item = await repository.get("id123");
await repository.save({ ...item, name: "updated item" });

Note: you can disable updating updatedAt for existing entities if required by doing the following (e.g. for data migrations). For newly created entities, the createdAt and updatedAt fields will always be set to the current date, regardless of this setting (unless these fields have a valid Date already set on them).

import { DISABLE_TIMESTAMP_UPDATE } from "./timestamped-repository";
import { runWithRequestStorage, setRequestStorageValue } from "@mondomob/gae-js-core";

await runWithRequestStorage(async () => {
  setRequestStorageValue(DISABLE_TIMESTAMP_UPDATE, true);
  return repository.save(item);
});

@Transactional

Annotate functions to make them transactional.

NOTE: Requires "experimentalDecorators": true set in your tsconfig.json

class UserService {
  constructor(
    public userRepo: FirestoreRepository<User>,
  ) {}

  @Transactional()
  async addCredits(userId: string, credits: number): Promise<User> {
    const user = this.userRepo.get(userId);
    user.credits = user.credits + credits;
    return this.userRepo.save(user);
  }
}