7

I have a cloud function that validates inputs from a form submission on my client. I'm using Cloud Functions for Firebase https triggers with cors express middleware.

Firebase Function

const functions = require('firebase-functions');
const express = require('express');
const cors = require('cors')({origin: true});
const validateImageForm = require('./library/validate-image-form');

exports.apiValidateImageForm = functions.https.onRequest((req, res) => {

  cors(req, res, () => {

    validateImageForm(req.body.formInputs, req.body.imageStatus).then((data) => {
      res.status(200).send(data);
    });

  });

});

Client Call To Function

const validateImageFormFetchOptions = {
   headers: {
     'Accept': 'application/json',
     'Content-Type': 'application/json'
   },
   method: 'POST',
   body: JSON.stringify({
     formInputs: formInputs
   })
}

fetch('https://project-url.cloudfunctions.net/apiValidateImageForm', validateImageFormFetchOptions)
 .then(response => response.json())
 .then(serverErrors => {console.log(serverErrors)});

Issue

When I call this function from my client using a fetch request I see two apiValidateImageForm triggers in the functions logs. The first is a status 204 and I assume this comes from the cors preflight request that checks the origin. The final request is status 200. I just want one function trigger when I fetch the apiValidateImageForm function. I'm concerned that over time the preflight requests that output the status 204 will add unnecessary function calls to my project quota.

enter image description here

Question

Is it possible to prevent firebase from triggering a function call on the preflight request? If not then is there a way to prevent the preflight request and successfully pass data to the function.

Frank van Puffelen
  • 418,229
  • 62
  • 649
  • 645
Celso
  • 465
  • 1
  • 5
  • 14
  • 2
    As long as you’re adding a `Content-Type': 'application/json'` header to the request, the browser is going to automatically on its own do a CORS preflight OPTIONS request before trying the request from your code. So the only way to avoid is that is to not send it encoded as application/json but to instead send the JSON as the value of a parameter in a request body with a application/x-www-form-urlencoded content type; that is, as name+value pair like `json=[your JSON data here]`. See the *How to avoid the CORS preflight* section of the answer at https://stackoverflow.com/a/43881141/441757 – sideshowbarker Sep 22 '17 at 17:54

1 Answers1

10

To fix the duplicate requests 200 and 204 then change how you're client side fetch request. @sideshowbarker is right. The browser automatically does the CORS preflight OPTIONS request so this is not an issue in Cloud Functions for Firebase. This answer was helpful.

To fix the preflight I changed my code to the following:

Client Call To Function

Removed the headers from the fetch options completely rather than setting the content type as application/json. By default the fetch request content type is application/x-www-form-urlencoded; charset=UTF-8.

const validateImageFormFetchOptions = {
  method: 'POST',
  body: JSON.stringify({
   formInputs: formInputs
  })
}

fetch('https://project-url.cloudfunctions.net/apiValidateImageForm', validateImageFormFetchOptions)
  .then(response => response.json())
  .then(serverErrors => {console.log(serverErrors)});

Firebase Function

Explicitly parsed the request body because it is now received as a text string.

const functions = require('firebase-functions');
const express = require('express');
const cors = require('cors')({origin: true});
const validateImageForm = require('./library/validate-image-form');

exports.apiValidateImageForm = functions.https.onRequest((req, res) => {
  cors(req, res, () => {
    const body = JSON.parse(req.body);
    validateImageForm(body.formInputs, body.imageStatus).then((data) => {
      res.status(200).send(data);
    });
  });
});
Celso
  • 465
  • 1
  • 5
  • 14