Search code examples
reactjsfirebasegoogle-cloud-functionscorsfirebase-hosting

CORS issue accessing firebase cloud functions from react website hosted on firebase hosting


I am working on a React project, I had made my frontend in react.js and hosted it on firebase. I wanted to use firebase cloud functions for backend. Butt the issue is, I keep getting CORS error. This is the exact Error that I am getting. I can see this in the issues section of the developer tools.

`A 'cache-control' header is missing or empty.`
`Resource should use cache busting but URL does not match configured patterns.`
`Static resources should use a 'cache-control' header with 'max-age=31536000' or more.`
`Static resources should use a 'cache-control' header with the 'immutable' directive.`
`Ensure CORS response header values are valid`
`Response should include 'x-content-type-options' header.`

This is my function (function/index.js)

/**
 * Import function triggers from their respective submodules:
 *
 * const {onCall} = require("firebase-functions/v2/https");
 * const {onDocumentWritten} = require("firebase-functions/v2/firestore");
 *
 * See a full list of supported triggers at https://firebase.google.com/docs/functions
 */

const {onRequest} = require("firebase-functions/v2/https");
const logger = require("firebase-functions/logger");

// Create and deploy your first functions
// https://firebase.google.com/docs/functions/get-started
const functions = require("firebase-functions");
exports.helloWorld = onRequest(
    {cors: true},
    (request, response) => {
      logger.info("Hello logs!", {structuredData: true});
      response.send("Hello from Firebase!");
    });

exports.bigben = functions.https.onRequest(
    {cors: true},
    (req, res) => {
      const hours = (new Date().getHours() % 12) + 1;
      res.status(200).send(`<!doctype html>
    <head>
    <title>Time</title>
    </head>
    <body>
    ${"BONG ".repeat(hours)}
    </body>
    </html>`);
    });

this is my firebase.json

{
  "functions": [
    {
      "source": "functions",
      "codebase": "default",
      "ignore": [
        "node_modules",
        ".git",
        "firebase-debug.log",
        "firebase-debug.*.log"
      ],
      "predeploy": [
        "npm --prefix \"$RESOURCE_DIR\" run lint"
      ]
    }
  ],
  "hosting": {
    "public": "build",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
    "rewrites": [
      {
        "source": "**",
        "destination": "/index.html"
      },
      {
        "source":"/bigben",
        "function":{
          "functionID":"bigben",
          "region":"us-central1",
          "pinTag":true
        }
      },
      {
        "source":"/helloworld",
        "function":{
          "functionID":"helloworld",
          "region":"us-central1",
          "pinTag":true
        }
      }
    ]
  }
}

this is my firebase.js

import { initializeApp } from 'firebase/app';
import { getFunctions, httpsCallable } from 'firebase/functions';

const firebaseConfig = {
// I have put firebaseconfig here
  apiKey: ,
  authDomain:,
  projectId: ,
  storageBucket: ,
  messagingSenderId: ,
  appId: 
};

const app = initializeApp(firebaseConfig);
const functions = getFunctions(app);

// Update the callFunction to use the Firebase Hosting URL
export const callFunction = () => {
  return fetch('/bigben').then(response => response.json());
};

this is where in my frontend code where I am calling the function

import React, { useState, useEffect } from 'react';
import { callFunction } from '../firebase';

import './Home.css';
function Home() {
  const [message, setMessage] = useState('Loading...');

  useEffect(() => {
    callFunction()
      .then((response) => {
        setMessage(response.data);
      })
      .catch((error) => {
        console.error(error);
        setMessage('Error fetching message');
      });
  }, []);
  
  return (
    <div className="Home">
      <p className="hello-message">{message}</p>
      <h1 className="Home-title">Grow Smarter, Grow Happier</h1>

      <div className="About" >
        <h2 className="About-title">Unlocking Nature's Secrets with AI</h2>
        <p className="About-text">
          Whether you're a seasoned farmer or nurturing your first seedling, we're here to help you cultivate success. Our app bridges the gap between cutting-edge technology and everyday gardening, empowering you to make informed decisions and achieve bountiful yields.
        </p>

        <div className="Features">
          <h3 className="Features-title">Your Green Thumb's Best Friend</h3>
        </div>
      </div>
    </div>
  );
}

export default Home;

I have looked at both these https://firebase.google.com/docs/functions/http-events?gen=2nd#node.js and https://firebase.google.com/docs/hosting/functions I have tried including cors in different ways originally I had written the firebase functions in python but I couldn't get cors to work so I shifted to javascript for functions but I still can't get it to work.

I am a beginner so any help is appreciated PREVIOUSLY When I had the website frontend locally I could get it working by using something like this with express as middleware

const express = require('express');
const request = require('request');

const app = express();

app.use((req, res, next) => {
 res.header('Access-Control-Allow-Origin', '*');
 res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
 res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Requested-With');
 if (req.method === 'OPTIONS') {
   return res.sendStatus(200);
 }
 next();
});

app.all('/predict', (req, res) => {
 req.pipe(request.post('https://us-central1-diseasedet.cloudfunctions.net/predict')).pipe(res);
});

app.listen(5000, () => {
 console.log('Proxy server listening on port 5000');
});


Solution

  • I finally managed to fix it by simple adding headers in the cloud function side basically you need to add and code to handle OPTIONS method and for normal POST method you need to return your response along with the headers and code.

    I added the following to my cloud function

    headers = {
        "Access-Control-Allow-Origin": "*",
        "Access-Control-Allow-Methods": "GET, POST, OPTIONS",
        "Access-Control-Allow-Headers": "Content-Type , Authorization, X-Requested-With",
        "Access-Control-Max-Age": "3600",
    }
    
    if request.method == "OPTIONS":
        return ("OK", 200, headers)
    if request.method == 'POST':
        data = request.get_json()
        return json.dumps(response_data), 200, {
        'Content-Type': 'application/json',
        'Access-Control-Allow-Origin': '*',
        'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
        'Access-Control-Allow-Headers': 'Content-Type , Authorization, X-Requested-With',
        'Access-Control-Max-Age': '3600',}
    return ("Method Not Allowed", 405, headers)