Search code examples
javascriptfirebasegoogle-cloud-functionscorsopenai-api

Accessing ChatGPT API through Firebase Cloud Function


Below is some code for a simple Firebase Cloud Function that hits the ChatGPT API. Deploying this code and accessing it from my app results in a CORS error.

import * as functions from "firebase-functions";
import {defineString} from "firebase-functions/v2/params";
import {Configuration, OpenAIApi} from "openai";

const openAIKey = defineString("OPEN_API_KEY");

export const getSummary = functions.https.onCall(async (data) => {
  const configuration = new Configuration({
    apiKey: openAIKey.value(),
  });
  const openai = new OpenAIApi(configuration);
  const completion = await openai.createChatCompletion({
    model: "gpt-3.5-turbo",
    messages: [
      {
        role: "user",
        content: data.prompt,
      },
    ],
  });
  const [choice] = completion.data.choices;
  return {
    response: choice.message ?? "no response",
  };
});

This cloud function works perfectly when I access it from my app using the functions emulator. I only get the CORS error when I deploy it to the cloud and try to use it.

Also, I have a helloWorld function deployed alongside this one so that I can check that there's nothing wrong with my whole functions setup, and it works fine also. Furthermore, when I go into my Cloud Functions Console and test the function directly, it also works. So the issue clearly has to do with accessing the API specifically via the cloud function production environment and specifically from the app.

Update: Here's the client code and the exact error:

const getSummary = httpsCallable(functions, "getSummary");
async function askGPT() {
    const result = await getSummary({
      prompt: "Please summarize the question in the following text. Phrase your response in the form of a question, and use Markdown for any formatting you might need.\n\n" + question.text
    });
    question.question_summary = (
      (question.question_summary ?? "") // @ts-ignore
      + (result?.data?.response?.content || "").trim()
    );
  }

error:

Access to fetch at 'https://us-central1-my-documentation.cloudfunctions.net/getSummary' from origin 'http://localhost:5173' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.


Solution

  • The best solution to this problem is to avoid using onCall when you have CORS issues. Instead:

    1. Come up with a name for a new Firestore collection.
    2. Set up a cloud function that is triggered by the creation of a new document in that collection.
    3. Put the logic that talks to the OpenAI API in the cloud function. When you have the response, write it to a collection in Firestore (wherever you want).
    4. From the client:
      1. Write to the aforementioned collection when you want to make a request.
      2. Subscribe to the collection that the cloud function writes to.

    All together, a new request from the client causes this cascade:

    1. A write to the Firestore collection for requests.
    2. A cloud function read of that document, plus an API call and any other necessary logic.
    3. A write to a Firestore collection for responses.
    4. An update on the client in response to that document update.

    The reason this alternative approach is required is that an onCall cloud function has different CORS behavior than a cloud function triggered by a Firestore event.