javascriptnode.jsgoogle-chromegoogle-chrome-extensionnetlify

CORS problem while sending request to netlify dev server


I'm working on a chrome devtools panels extension. In it I have:

const toDbBtn = document.getElementById("toDbBtn");

toDbBtn.addEventListener("click", async () => {
  const dbName = document.getElementById("textBox").value;

  // const result = await chrome.storage.local.get(["yourKey"]);

  const storedJSON = await chrome.storage.local.get(["yourKey"]);
  console.log("Result from storage:", storedJSON);

  if (storedJSON.yourKey) {

    const parsedArray = JSON.parse(storedJSON.yourKey);
    console.log("Parsed Array:", parsedArray);
    const partArray = parsedArray.slice(0, 30);
    console.log("partArray", partArray);
    const data = {
      dbName: dbName,
      savedArray: partArray,
    };

    const postable = JSON.stringify(data);
    console.log("postable", postable);
    // http://localhost:8888/.netlify/functions//updateCollection.js

    const response = await fetch(
      "http://localhost:8888/.netlify/functions/updateCollection",
      {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          // "Access-Control-Allow-Origin": "*",
          // "Access-Control-Allow-Methods": "POST",
          // "Access-Control-Allow-Headers": "Content-Type",
        },
        body: postable,
      }
    );

    const result = await response.json();
    console.log("Response from server:", result);
  } else {
    console.log("Value not found in storage for key: yourKey");
  }
});

I'm planning to send some data to a netlify function, and so decided to test locally using "NTL DEV" command.

my target ntl function is:

exports.handler = async function (event, context) {
  const obj = JSON.parse(event.body);
  console.log("obj: ", obj);   \
  const collectionName = obj.dbName;
  const objArray = obj.savedArray;
  console.log("objArray: ", objArray);
  console.log("collectionName: ", collectionName);

  return {
    statusCode: 200,
    body: JSON.stringify({
      message: data,
    }),
  };
};

When I push the devtools button, I get:

Request from ::ffff:127.0.0.1: OPTIONS /.netlify/functions/updateCollection

based on CORS error while sending request from Browser to play server even after sending CORS header , I assume that the browser starts of by sending a OPTIONS request. But I'm not sure based on https://cli.netlify.com/commands/dev/ how to set a cors header like "Access-Control-Allow-Origin": "*" . How can I get this working?

EDIT:

I have come up with the code below, which appears to handle the original problem, but still has a CORS problem. I'll ask a follow up question.

exports.handler = async function (event, context) {

  console.log("context: ", context);
  console.log("event: ", event);

  if (event.httpMethod !== "OPTIONS" && event.httpMethod !== "POST") {
    console.log("event.httpMethod: ", event.httpMethod);
    return {
      statusCode: 405,
      body: JSON.stringify({ message: "Method Not Allowed" }),
    };
  } else if (event.httpMethod === "OPTIONS") {
    return {
      statusCode: 204,
      headers: headers,
      body: null,
    };
    
  } 
  if (event.httpMethod === "POST") {
    console.log("event.httpMethod: ", event.httpMethod);
    console.log("event.body: ", event.body);
    const data = JSON.parse(event.body);
    console.log("data: ", data);
  }

Solution

  • You'll have to handle the OPTIONS request and send Access-Control-Allow-Origin:<origin> header in the OPTIONS preflight request. See Why does Fetch API Send the first PUT request as OPTIONS.

    Something like this using a Node.js HTTP/2 server https://gist.github.com/guest271314/c56d769bca04d92dc941c04d9ac40ba5

    const headers = {
      "Cache-Control": "no-cache",
      "Content-Type": "text/plain; charset=UTF-8",
      "Cross-Origin-Opener-Policy": "unsafe-none",
      "Cross-Origin-Embedder-Policy": "unsafe-none",
      "Access-Control-Allow-Origin": "*",
      "Access-Control-Allow-Private-Network": "true",
      "Access-Control-Allow-Headers": "Access-Control-Request-Private-Network",
      "Access-Control-Allow-Methods": "OPTIONS,POST,GET,HEAD,QUERY,query",
    };
    // ...
    (async () => {
      for await (const { request, response } of requestStream) {
        if (request.method === "OPTIONS") {
          response.writeHead(204, headers);
          continue;
        }
        if (request.method === "POST" || /query/i.test(request.method)) {
          response.writeHead(200, headers);
        }
        // ...
    

    Something like this using Deno Deploy https://gist.github.com/guest271314/c56d769bca04d92dc941c04d9ac40ba5

    const responseInit = {
      headers: {
        'Cache-Control': 'no-cache',
        'Content-Type': 'text/plain; charset=UTF-8',
        'Cross-Origin-Opener-Policy': 'unsafe-none',
        'Cross-Origin-Embedder-Policy': 'unsafe-none',
        'Access-Control-Allow-Origin': '*',
        'Access-Control-Allow-Private-Network': 'true',
        'Access-Control-Allow-Headers': 'Access-Control-Request-Private-Network',
        'Access-Control-Allow-Methods': 'OPTIONS,POST,GET,HEAD,QUERY',
      },
    };
    
    for await (
      const conn of Deno.listen({
        alpnProtocols: ["h2", "http/1.1"],
      })
    ) {
      for await (const {
          request,
          respondWith
        }
        of Deno.serveHttp(conn)) {
        if (request.method === 'OPTIONS' || request.method === 'HEAD') {
          respondWith(new Response(null, responseInit));
        }
        // ...
    

    Since you're on Chrome I would suggest to take a look at this https://github.com/explainers-by-googlers/local-network-access and https://github.com/explainers-by-googlers/local-network-access/issues/2 re Access-Control-Allow-Private-Network: true header see https://developer.chrome.com/blog/private-network-access-preflight. Some folks in Google world are proposing adding a property to WHATWG Fetch implementation just for local usage. I don't think it's necessary. Chime in if you are an interested Chromium and or WHATWG Fetch and Streams developer/hacker.