Adding AI Magic To Your Firestore API

Welcome to the final day of Firebase Week! No tutorial written in 2024 is complete without a touch of AI magic, so that's precisely what we will do in this last part. By the end of this tutorial, we will have an AI-infused API that leverages Google Gemini to summarize our to-do list.

Before we get started, you’ll need to make sure you have the following prerequisites in place:

With those checked off, we can wrap up the last part of this series by creating a new Get Summary endpoint.

Create a Get Summary Endpoint#

The first thing on our agenda is creating the Get summary endpoint. The AI logic will be accessible through the /v1/summary endpoint that will generate a summary by feeding the todos saved in Firestore into Gemini. To set up our endpoint, Under the Code section in Zuplo, open up the routes.oas.json file, and on the Route Designer, we will:

  1. Click Add Route.
  2. Set the Summary as “Get summary”
  3. Set the Path as “/v1/summary”
  4. Set the Method as “GET”
  5. Set CORS as “Anything Goes”
  6. In the Request Handler section, we will:
    1. Set the Handler as “Function”.
    2. Click the Function dropdown and select New Module.
    3. Set the new module's name as “summary”.
    4. Click Create.

The summary.ts file will now be added underneath the modules directory in the side menu. Open the file and paste in the following code:

import { environment, ZuploContext, ZuploRequest } from "@zuplo/runtime";
 
export default async function (request: ZuploRequest, context: ZuploContext) {
  const firestoreUrl = `https://firestore.googleapis.com/v1/projects/${environment.PROJECT_ID}/databases/(default)/documents:runQuery`;
 
  const query = {
    structuredQuery: {
      from: [
        {
          collectionId: "todos",
        },
      ],
      where: {
        fieldFilter: {
          field: {
            fieldPath: "userId",
          },
          op: "EQUAL",
          value: {
            stringValue: request.user.sub,
          },
        },
      },
    },
  };
 
  context.log.info(query, firestoreUrl);
 
  // Query Firestore
  const todosResponse = await fetch(firestoreUrl, {
    method: "POST",
    headers: request.headers,
    body: JSON.stringify(query),
  });
 
  const todosJson = await todosResponse.json();
 
  context.log.info(todosJson);
 
  // Collate results
  const todos = todosJson.map(
    (doc) => doc.document.fields.description.stringValue,
  );
 
  const prompt = {
    contents: [
      {
        parts: [
          {
            text: `Here are some tasks: '${todos.join(", ")}'. Write a funny one-sentence summary. Make me laugh`,
          },
        ],
      },
    ],
  };
 
  // Send request to Gemini
  const geminiResponse = await fetch(
    `https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key=${environment.GEMINI_API_KEY}`,
    {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify(prompt),
    },
  );
 
  if (!geminiResponse.ok) {
    throw new Error(`Gemini request failed: ${geminiResponse.status} - ${geminiResponse.statusText}
 
 
${await geminiResponse.text()}`);
  }
  const geminiData = await geminiResponse.json();
 
  // Extract summary from Gemini's response
  const summary = geminiData.candidates[0].content.parts[0].text.trim();
 
  // Send response
  return { response: summary };
}

The code above does the following:

Fetch User's Todos

  • Retrieves the user's unique ID from the request.
  • Constructs a query to fetch the user's "todos" from Firestore.
  • Executes the query against the Firestore API.

Prepare for Gemini

  • Extracts the todo descriptions from the Firestore results.
  • Creates a prompt for the Gemini AI model, asking for a funny one-sentence summary of the todos.

Generate Summary

  • Sends the prompt to the Gemini API.
  • Handles potential errors in the Gemini response.

Return Summary

  • Extracts the generated summary from Gemini's response.
  • Sends the summary back as the final response.

With the code plugged into the summary.ts file, click Save in the bottom left corner to save the updated function and endpoint and deploy it to the gateway and developer portal.

Before wrapping up the endpoint, we must add the necessary policies to the request pipeline. For this, we will:

  1. Go back to the routes.oas.json file and open up the Route Designer.
  2. Open the Get summary endpoint.
  3. Expand the Policies section, and under Requests, click Add Policy.
  4. In the modal that appears, under Existing Policies, select:
    • api-key-inbound
    • upstream-firebase-admin-auth-inbound
    • monetize-requests
  5. Click Save in the bottom-left of the screen to save the updated endpoint.

Adding these policies will help us secure the API by API key, connect the Firebase API, and monetize the endpoint by incrementing the Requests meter we created in part 4 of this series. After everything is plugged in as specified above, the endpoint will look like this in the endpoint designer.

Zuplo route designer

Since our summary.ts file depends on a Gemini API key, we will need to plug that into our Zuplo instance next.

Add Your Gemini API Key Environment Variable#

Make sure to grab a Gemini API key before proceeding.

Similar to how we’ve added other environment variables in Zuplo, we will add the Gemini API key. To add the key to an environment variable, we will do the following:

  1. While in Zuplo, click on Settings in the header menu.
  2. On the Settings screen, select Environment Variables from the left-side menu.
  3. Click Add Variable.
  4. In the modal that appears, set the Name field as “GEMINI_API_KEY” and the Value field with your Gemini API key.
  5. Check the Secret checkbox off.
  6. Click Save.

Here’s an example of what your environment variable configuration will look like before submitting it.

setting the api key environment variable

With the endpoint created and our API key plugged in for Gemini, let’s give this new endpoint a go!

Test The New Endpoint Via The Developer Portal#

Similar to other tests we’ve run, let's log back into the Zuplo Developer Portal and test things out from there.

To test this endpoint, log in to the Developer Portal, go to the Get summary endpoint in the developer docs, and click the Test button on the example.

testing the gemini API in Zuplo docs

In the API Playground modal, click the Test button to send off a request. The returned response should contain a somewhat ridiculous summary of your tasks for your user, powered by Gemini.

Zuplo docs API playground

True to 2024, you now have a Firestore API infused with AI via Google’s Gemini API.

Conclusion#

Just like that, we’ve finished our five-day, five-tutorial series on using Zuplo and Firestore to create production-ready APIs in minutes. In this series, we have covered how to build APIs on top of Firebase, secure and monetize them with Zuplo, and use AI to create dynamic responses based on data within our Firestore instance.

Ready to try this out for yourself? Sign up for Zuplo today to build and secure APIs with developer-first API management.

Check out the day 5 video walk-through.

Designed for Developers, Made for the Edge