-1

I have a flutter web app that I am running on localhost to debug, my web app posts data to my Firebase Cloud functions API which then sends data to Google Bigquery to create a table, although I have installed CORS already I keep getting this error in my browser below

Access to XMLHttpRequest at 'https://us-central1-denance-cbf3f.cloudfunctions.net/api/create_table' from origin 'http://localhost:55073' 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.
    us-central1-denance-cbf3f.cloudfunctions.net/api/create_table:1 Failed to load resource: net::ERR_FAILED
    errors.dart:202 Uncaught (in promise) Error: XMLHttpRequest error.
        C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/_internal/js_dev_runtime/patch/core_patch.dart 897:28                get current
    packages/http/src/browser_client.dart 71:22                                                                                    <fn>
    C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/async/zone.dart 1687:54                                              runUnary
    C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/async/future_impl.dart 155:18                                        handleValue
    C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/async/future_impl.dart 707:44                                        handleValueCallback
    C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/async/future_impl.dart 736:13                                        _propagateToListeners
    C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/async/future_impl.dart 533:7                                         [_complete]
    C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/async/stream_pipe.dart 61:11                                         _cancelAndValue
    C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/async/stream.dart 1219:7                                             <fn>
    C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 324:14  _checkAndCall
    C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 329:39  dcall
    C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/html/dart2js/html_dart2js.dart 37324:58                              <fn>
    
    
        at Object.createErrorWithStack (:55073/dart_sdk.js:5070)
        at Object._rethrow (:55073/dart_sdk.js:37715)
        at async._AsyncCallbackEntry.new.callback (:55073/dart_sdk.js:37711)
        at Object._microtaskLoop (:55073/dart_sdk.js:37568)
        at _startMicrotaskLoop (:55073/dart_sdk.js:37574)
        at :55073/dart_sdk.js:33324

this is my code below

const functions = require("firebase-functions");

const express = require('express');
var cors = require('cors');
const { BigQuery } = require('@google-cloud/bigquery');

const bigquery = new BigQuery();
var app = express();
app.use(cors);
app.use(express.json());

exports.api = functions.https.onRequest(app);


app.post('/create_table', cors(dynamicCorsOptions), function (req, res,) {


  'use strict';


  async function createTable() {


    let schema = [];


    async function groupUp() {

      for (let key in req.body) {


        schema.push({ name: req.body[key]['name'], type: req.body[key]['type'] });


      }


    }

    await groupUp();


    await doingIt();


    async function doingIt() {


      var datasetId = 'denanse'; // Existing dataset
      var tableId = 'hrhrhhh'; // Table to be created

      const options = {
        schema: schema,
        location: 'US',
      };

      const [table] = await bigquery
        .dataset(datasetId)
        .createTable(tableId, options);

      console.log(`Table ${table.id} created.`);
      res.send(`Table ${table.id} created.`);

    }

  }

  createTable();


});

how can I resolve this?

Jim G.
  • 15,141
  • 22
  • 103
  • 166
Jojomobs
  • 67
  • 1
  • 1
  • 3
  • https://stackoverflow.com/q/35693758/109941 – Jim G. Jul 22 '21 at 17:21
  • Does this answer your question? [Google Cloud Functions enable CORS?](https://stackoverflow.com/questions/35693758/google-cloud-functions-enable-cors) – Ian Kemp Jul 23 '21 at 05:43

1 Answers1

-1

Currently your usage of the cors package is bugged in two ways. You only need to implement either of the below fixes.

Enable CORS on all routes

Because you've imported and used the cors package like so:

var cors = require('cors');
// ...
app.use(cors);

You've attached the cors constructor to the Express application rather than an instance of the cors middleware (You are calling cors(options) with cors(request)). The options object and its properties are covered in the documentation.

In its most basic form, you can use no configuration to allow CORS on all requests:

app.use(cors())

However, this is effectively the same as just manually setting the CORS headers yourself to * along with some other headers.

This is unsecure and goes against the point of CORS. You should instead configure it so that you only accept a handful of origins related to your project.

/** The current project's ID */
export const PROJECT_ID = JSON.parse(process.env.FIREBASE_CONFIG).projectId;

/** Array of permitted origins for CORS */
export const CORS_ALLOWED_ORIGINS = [
  "https://my.custom.domain",                // 0+ custom domains related to your project
  `https://${PROJECT_ID}.firebaseapp.com`,   // legacy Firebase Hosting domain
  `https://${PROJECT_ID}.web.app`,           // modern Firebase Hosting domain
  ...(process.env.NODE_ENV === "production"
    ? []
    : ["localhost", "undefined"]             // permit CORS on localhost, only while emulated
  ),
];

app.use(cors({ origin: CORS_ALLOWED_ORIGINS }))

Enable CORS only on /create_table

Similar to above, you've attached the cors() middleware to the /create_table route using:

app.post('/create_table', cors(dynamicCorsOptions), function (req, res) { /* ... */ });

Here, dynamicCorsOptions is undefined (at least in the code you've shared) which will use the CORS middleware on requests to POST /create_table. However, you need to handle OPTIONS /create_table too.

// handle preflight requests
app.options('/create_table', cors(dynamicCorsOptions));

// handle actual requests
app.post('/create_table', cors(dynamicCorsOptions), function (req, res) { /* ... */ });

You can even use cors({ origin: CORS_ALLOWED_ORIGINS }) from the above section for this.

Other points

  • The Functions Framework (that runs your function), as documented here, parses the body of the request for you if it is set with the appropriate headers. This means you don't need app.use(express.json()).
  • Your code calls createTable() but doesn't handle if it fails which will throw an unhandled Promise exception and terminate your function. You can either handle this case using catch() or the try/catch block in the next snippet:
createTable()
  .catch((err) => {
    console.error("Failed to create table: " + err.code || err.message, err);
    res.status(500)
      .send("Failed to create table with unknown error");
  });
  • Currently the execution of your /create_table handler jumps around quite a bit, I propose rearranging it for readability:
app.post('/create_table', cors(dynamicCorsOptions), function (req, res) {
  'use strict';

  // this could be moved outside of the handler
  async function groupUp(requestBody) {
    const groups = [];
 
    for (let key in requestBody) {
      const { name, type } = requestBody[key];
      groups.push({ name, type });
    }

    return groups;
  }

  // this could be moved outside of the handler
  async function createTableInDataset(datasetId, schema) {
    const tableId = /* generate table ID */;

    const [table] = await bigquery
      .dataset(datasetId)
      .createTable(tableId, {
        schema,
        location: 'US',
      });

    return table.id;
  }

  async function createTable() {
    try {
      const schema = await groupUp(req.body);

      const createdTableId = createTableInDataset('denanse', schema);

      console.log(`Table ${createdTableId} created.`);
      res.status(201)
        .send(`Table ${createdTableId} created.`);
    } catch (err) {
      console.error("Failed to create table: " + err.code || err.message, err);
      res.status(500)
        .send("Failed to create table with unknown error");
    }
  }

  createTable();
});
samthecodingman
  • 23,122
  • 4
  • 30
  • 54
  • @JimG. Spamming that comment on every question/answer is not helpful. Use the "close -> mark as duplicate" for that. While your linked question applies for general GCF functions (and until I edited the [accepted answer](https://stackoverflow.com/a/43418322/3068190) had a significant bug), the Firebase fork of CGF allows for finer controls over CORS behaviour as covered here. Understanding the problem is better than just pointing at an answer. – samthecodingman Jul 23 '21 at 05:44