Confira tudo que foi anunciado no Firebase Summit e saiba como usar nossa plataforma para acelerar o desenvolvimento de apps e executar os aplicativos com confiança. Saiba mais

Exiba conteúdo agrupado do Firestore de uma CDN

Mantenha tudo organizado com as coleções Salve e categorize o conteúdo com base nas suas preferências.

Muitos aplicativos fornecem o mesmo conteúdo para todos os usuários no carregamento da primeira página. Por exemplo, um site de notícias pode mostrar as últimas notícias, ou um site de comércio eletrônico pode mostrar os itens mais vendidos.

Se esse conteúdo for veiculado no Cloud Firestore, cada usuário emitirá uma nova consulta para os mesmos resultados ao carregar o aplicativo. Como esses resultados não são armazenados em cache entre os usuários, o aplicativo é mais lento e mais caro do que o necessário.

Solução: Pacotes

Os pacotes do Cloud Firestore permitem que você monte pacotes de dados a partir de resultados de consultas comuns no back-end usando o SDK Admin do Firebase e veicule esses blobs pré-computados armazenados em cache em uma CDN. Isso oferece aos usuários uma experiência de primeiro carregamento muito mais rápida e reduz os custos de consulta do Cloud Firestore.

Neste guia, usaremos o Cloud Functions para gerar pacotes e o Firebase Hosting para armazenar em cache e veicular conteúdo de pacote dinamicamente. Mais informações sobre pacotes estão disponíveis nos guias .

Primeiro, crie uma função HTTP pública simples para consultar as 50 "histórias" mais recentes e servir o resultado como um pacote.

Node.js
exports.createBundle = functions.https.onRequest(async (request, response) => {
  // Query the 50 latest stories
  const latestStories = await db.collection('stories')
    .orderBy('timestamp', 'desc')
    .limit(50)
    .get();

  // Build the bundle from the query results
  const bundleBuffer = db.bundle('latest-stories')
    .add('latest-stories-query', latestStories)
    .build();

  // Cache the response for up to 5 minutes;
  // see https://firebase.google.com/docs/hosting/manage-cache
  response.set('Cache-Control', 'public, max-age=300, s-maxage=600');

  response.end(bundleBuffer);
});
      
Java

package com.example;

import com.google.auth.oauth2.GoogleCredentials;
import com.google.cloud.firestore.Firestore;
import com.google.cloud.firestore.FirestoreBundle;
import com.google.cloud.firestore.Query.Direction;
import com.google.cloud.firestore.QuerySnapshot;
import com.google.cloud.functions.HttpFunction;
import com.google.cloud.functions.HttpRequest;
import com.google.cloud.functions.HttpResponse;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
import com.google.firebase.cloud.FirestoreClient;
import java.io.BufferedWriter;
import java.io.IOException;

public class ExampleFunction implements HttpFunction {

  public static FirebaseApp initializeFirebase() throws IOException {
    if (FirebaseApp.getApps().isEmpty()) {
      FirebaseOptions options = FirebaseOptions.builder()
          .setCredentials(GoogleCredentials.getApplicationDefault())
          .setProjectId("YOUR-PROJECT-ID")
          .build();

      FirebaseApp.initializeApp(options);
    }

    return FirebaseApp.getInstance();
  }

  @Override
  public void service(HttpRequest request, HttpResponse response) throws Exception {
    // Get a Firestore instance
    FirebaseApp app = initializeFirebase();
    Firestore db = FirestoreClient.getFirestore(app);

    // Query the 50 latest stories
    QuerySnapshot latestStories = db.collection("stories")
        .orderBy("timestamp", Direction.DESCENDING)
        .limit(50)
        .get()
        .get();

    // Build the bundle from the query results
    FirestoreBundle bundle = db.bundleBuilder("latest-stores")
        .add("latest-stories-query", latestStories)
        .build();

    // Cache the response for up to 5 minutes
    // see https://firebase.google.com/docs/hosting/manage-cache
    response.appendHeader("Cache-Control", "public, max-age=300, s-maxage=600");

    // Write the bundle to the HTTP response
    BufferedWriter writer = response.getWriter();
    writer.write(new String(bundle.toByteBuffer().array()));
  }
}
      

Em seguida, configure o Firebase Hosting para veicular e armazenar em cache essa Cloud Function modificando firebase.json . Com essa configuração, a CDN do Firebase Hosting veiculará o conteúdo do pacote de acordo com as configurações de cache definidas pelo Cloud Function. Quando o cache expirar, ele atualizará o conteúdo acionando a função novamente.

firebase.json
{
  "hosting": {
    // ...
    "rewrites": [{
      "source": "/createBundle",
      "function": "createBundle"
    }]
  },
  // ...
}

Por fim, em seu aplicativo da Web, busque o conteúdo empacotado da CDN e carregue-o no SDK do Firestore.

// If you are using module bundlers.
import firebase from "firebase/app";
import "firebase/firestore";
import "firebase/firestore/bundle" // This line enables bundle loading as a side effect.

async function fetchFromBundle() {
  // Fetch the bundle from Firebase Hosting, if the CDN cache is hit the 'X-Cache'
  // response header will be set to 'HIT'
  const resp = await fetch('/createBundle');

  // Load the bundle contents into the Firestore SDK
  await db.loadBundle(resp.body);

  // Query the results from the cache
  // Note: omitting "source: cache" will query the Firestore backend.
  
  const query = await db.namedQuery('latest-stories-query');
  const storiesSnap = await query.get({ source: 'cache' });

  // Use the results
  // ...
}

Economias estimadas

Considere um site de notícias que recebe 100.000 usuários por dia e cada usuário carrega as mesmas 50 principais notícias no carregamento inicial. Sem qualquer armazenamento em cache, isso resultaria em 50 x 100.000 = 5.000.000 leituras de documentos por dia do Cloud Firestore.

Agora suponha que o site adote a técnica acima e armazene em cache esses 50 resultados por até 5 minutos. Portanto, em vez de carregar os resultados da consulta para cada usuário, os resultados são carregados exatamente 12 vezes por hora. Não importa quantos usuários cheguem ao site, o número de consultas ao Cloud Firestore permanece o mesmo. Em vez de 5.000.000 de leituras de documentos, esta página usaria 12 x 24 x 50 = 14.400 leituras de documentos por dia. Os pequenos custos adicionais do Firebase Hosting e do Cloud Functions são facilmente compensados ​​pela economia de custos do Cloud Firestore.

Enquanto o desenvolvedor se beneficia da economia de custos, o maior beneficiário é o usuário. Carregar esses 50 documentos do Firebase Hosting CDN em vez do Cloud Firestore diretamente pode reduzir facilmente 100-200 ms ou mais do tempo de carregamento do conteúdo da página. Estudos têm mostrado repetidamente que páginas rápidas significam usuários mais felizes.