2

I'm developing a serverless react app that makes calls to the Notion API from the browser but I can't seem to avoid getting CORS errors no matter what I do. What headers do I need to set to make this work?

T3db0t
  • 3,491
  • 4
  • 28
  • 45
  • Does this answer your question? [XMLHttpRequest cannot load XXX No 'Access-Control-Allow-Origin' header](https://stackoverflow.com/questions/35553500/xmlhttprequest-cannot-load-xxx-no-access-control-allow-origin-header) – Quentin Jul 20 '21 at 16:35

2 Answers2

3

You can register a domain name on freenom. Use cloudfare for the domain name. In the cloudfare, you can add a service (worker), the code below is for the worker.

 // Set CORS headers  
 response.headers.set("Access-Control-Allow-Origin", "*")

This will solve the cors error.

The worker basically just modifies your request and response.

Here is the link https://developers.cloudflare.com/workers/examples/cors-header-proxy

Worker (service) code

// We support the GET, POST, HEAD, and OPTIONS methods from any origin,
// and allow any header on requests. These headers must be present
// on all responses to all CORS preflight requests. In practice, this means
// all responses to OPTIONS requests.
const corsHeaders = {
  "Access-Control-Allow-Origin": "*",
  "Access-Control-Allow-Methods": "GET,HEAD,POST,OPTIONS",
  "Access-Control-Max-Age": "86400",
}

// The URL for the remote third party API you want to fetch from
// but does not implement CORS

// Your notion URL
const API_URL = "https://api.notion.com/v1/databases/123123/query"

// The endpoint you want the CORS reverse proxy to be on
const PROXY_ENDPOINT = "/getAllBook"

// Your notion api token
const token = "";

// The rest of this snippet for the demo page
function rawHtmlResponse(html) {
  return new Response(html, {
    headers: {
      "content-type": "text/html;charset=UTF-8",
    },
  })
}

const DEMO_PAGE = `
  <!DOCTYPE html>
  <html>
  <body>
    <h1>API GET without CORS Proxy</h1>
    <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#Checking_that_the_fetch_was_successful">Shows TypeError: Failed to fetch since CORS is misconfigured</a>
    <p id="noproxy-status"/>
    <code id="noproxy">Waiting</code>
    <h1>API GET with CORS Proxy</h1>
    <p id="proxy-status"/>
    <code id="proxy">Waiting</code>
    <h1>API POST with CORS Proxy + Preflight</h1>
    <p id="proxypreflight-status"/>
    <code id="proxypreflight">Waiting</code>
    <script>
    let reqs = {};
    reqs.noproxy = () => {
      return fetch("${API_URL}").then(r => r.json())
    }
    reqs.proxy = async () => {
      let href = "${PROXY_ENDPOINT}?apiurl=${API_URL}"
      return fetch(window.location.origin + href).then(r => r.json())
    }
    reqs.proxypreflight = async () => {
      let href = "${PROXY_ENDPOINT}?apiurl=${API_URL}"
      let response = await fetch(window.location.origin + href, {
        method: "POST",
        headers: {
          "Content-Type": "application/json"
        },
        body: JSON.stringify({
          msg: "Hello world!"
        })
      })
      return response.json()
    }
    (async () => {
      for (const [reqName, req] of Object.entries(reqs)) {
        try {
          let data = await req()
          document.getElementById(reqName).innerHTML = JSON.stringify(data)
        } catch (e) {
          document.getElementById(reqName).innerHTML = e
        }
      }
    })()
    </script>
  </body>
  </html>`

async function handleRequest(request) {
  const url = new URL(request.url)
  let apiUrl = url.searchParams.get("apiurl")

  if (apiUrl == null) {
    apiUrl = API_URL
  }

  // Rewrite request to point to API url. This also makes the request mutable
  // so we can add the correct Origin header to make the API server think
  // that this request isn't cross-site.
  request = new Request(apiUrl, request)
  request.headers.set("Origin", new URL(apiUrl).origin)
  request.headers.set("Authorization", "Bearer "+token)
  let response = await fetch(request)

  // Recreate the response so we can modify the headers
  response = new Response(response.body, response)

  // Set CORS headers
  response.headers.set("Access-Control-Allow-Origin", "*")

  // Append to/Add Vary header so browser will cache response correctly
  response.headers.append("Vary", "Origin")

  return response
}

function handleOptions(request) {
  // Make sure the necessary headers are present
  // for this to be a valid pre-flight request
  let headers = request.headers;
  if (
    headers.get("Origin") !== null &&
    headers.get("Access-Control-Request-Method") !== null &&
    headers.get("Access-Control-Request-Headers") !== null
  ){
    // Handle CORS pre-flight request.
    // If you want to check or reject the requested method + headers
    // you can do that here.
    let respHeaders = {
      ...corsHeaders,
    // Allow all future content Request headers to go back to browser
    // such as Authorization (Bearer) or X-Client-Name-Version
      "Access-Control-Allow-Headers": request.headers.get("Access-Control-Request-Headers"),
    }

    return new Response(null, {
      headers: respHeaders,
    })
  }
  else {
    // Handle standard OPTIONS request.
    // If you want to allow other HTTP Methods, you can do that here.
    return new Response(null, {
      headers: {
        Allow: "GET, HEAD, POST, OPTIONS",
      },
    })
  }
}

addEventListener("fetch", event => {
  const request = event.request
  const url = new URL(request.url)
  if(url.pathname.startsWith(PROXY_ENDPOINT)){
    if (request.method === "OPTIONS") {
      // Handle CORS preflight requests
      event.respondWith(handleOptions(request))
    }
    else if(
      request.method === "GET" ||
      request.method === "HEAD" ||
      request.method === "POST"
    ){
      // Handle requests to the API server
      event.respondWith(handleRequest(request))
    }
    else {
      event.respondWith(
        new Response(null, {
          status: 405,
          statusText: "Method Not Allowed",
        }),
      )
    }
  }
  else {
    // Serve demo page
    event.respondWith(rawHtmlResponse(DEMO_PAGE + url.pathname))
  }
})

Ajax example

$.ajax({
        type: "POST",
        url: "https://yourdomainname.com/getAllBook",
        headers: {
            "Notion-Version": "2021-08-16"
        },
        data : JSON.stringify(data),
        dataType: "json",
        contentType: "application/json; charset=utf-8",
        success: function(data){
         
        },
        error: function(data){
            console.log(data.statusText);
        },
        cache:"false"
    })
ouflak
  • 2,458
  • 10
  • 44
  • 49
  • 1
    This solution also works with the official notion js sdk. Just use the 'fetch' option when setting up a Client to provide a custom fetch wrapper that will redirect the request to your cloudflare service worker. – Austin Jun 07 '22 at 05:49
  • For the SDK you can set baseUrl when initialising the client and use there the url of your service worker. – Jobeso Jan 17 '23 at 13:04
-3

If you have CORS problems, you can use this Google Chrome extension: https://chrome.google.com/webstore/detail/allow-cors-access-control/lhobafahddgcelffkeicbaginigeejlf?hl=en

  • Asking every user of your web application to install a browser extension which punches a hole through security features of their browser isn't a practical solution. That extension doesn't work with preflighted requests anyway, which makes it pretty useless for most APIs. – Quentin Jul 20 '21 at 16:53