javascriptoracle-databaseplsqloracle-apex

How to call an external API in Oracle Apex?


I'm having the following problem:

I have an application in Oracle APEX and I need to make a call to an external API when I click the button.

The solution I thought of was to create a dynamic action on the button that executes JavaScript code when clicked (code below).

But when the code is executed, I receive the following error in my console:

Access to fetch at 'https://api.rd.services/platform/events?event_type=sale' from origin 'http://minha-url.com.br' 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. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

My code:

let options = {
  method: 'POST',
  headers: {
    accept: 'application/json',
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${accessToken}`,
  },
  body: JSON.stringify({
    event_type: 'SALE',
    event_family: 'CDP',
    payload: {
      email: 'conversao@exemplo.com',
      funnel_name: 'default',
      value: 999
    }
  })
};

fetch('https://api.rd.services/platform/events?event_type=sale', options)
  .then(response => response.json())
  .then(response => console.log(response))
  .catch(err => console.error(err));

I consulted the API documentation, and it contains the following information:

The RD Station Marketing APIs that use the access_token to authorize requests only allow requests coming from the backend of your application. This is because the token used in this API cannot be exposed in the browser, as it allows access to private information about the contacts in your account.

How could I resolve this error with CORS?

I need to make a POST request to the external API when clicking the button.


Solution

  • Their API documentation explains the CORS issue pretty well: you can't call their API using javascript from the client's browser; you have to call their API from your server.

    You don't have an easy way to work around CORS - the API is telling the browser not to allow it from your site, and the browser agrees. None of that is in your control.

    What you can do is call their API from PL/SQL. The API request will come from your database server, not from the client's browser, so CORS doesn't apply. And like their documentation points out, the sensitive access token will only be passing from your server to the API, so your clients can't steal it.

    Your easiest option is probably APEX_WEB_SERVICE.make_rest_request.

    First you need to create an ACL to allow the database to access the remote web server. Run this as SYSTEM or an admin account:

    declare
      l_principal VARCHAR2(20) := 'APEX_190200'; -- this should match your APEX schema name
      l_host varchar2(100) := 'api.rd.services'; -- remote server name
    begin
      dbms_network_acl_admin.create_acl (
        acl          => 'my_acl.xml',
        description  => 'my acl',
        principal    => l_principal,
        is_grant     => TRUE,
        privilege    => 'connect',
        start_date   => systimestamp,  
        end_date     => null);
      dbms_network_acl_admin.assign_acl (
        acl         => 'my_acl.xml',
        host        => l_host, 
        lower_port  => 443,
        upper_port  => 443); 
      commit;
    end;
    /
    

    This example just grants access to the APEX schema. You could run it again with l_principal set to your username if you want to test the API call from SQL Developer.

    Then you can use a block like this in APEX:

    declare
      l_clob clob;
      l_access_token varchar2(100) := 'io7u34907iafh394897';
    begin
      apex_web_service.set_request_headers(
          p_name_01        => 'Content-Type',
          p_value_01       => 'application/json',
          p_name_02        => 'Authorization',
          p_value_02       => 'Bearer ' || l_access_token,
          p_reset          => false,
          p_skip_if_exists => true );
      l_clob := apex_web_service.make_rest_request(
        p_url         => 'https://api.rd.services/platform/events?event_type=sale',
        p_http_method => 'POST',
        p_body => '{
      event_type: ''SALE'',
      event_family: ''CDP'',
      payload: {
        email: ''conversao@exemplo.com'',
        funnel_name: ''default'',
        value: 999
      }
    }'
      ) ;
      :P101_RESULT := l_clob;
    end;
    

    In this example, the access token is hardcoded in the PL/SQL block, but you could also store it in an application item variable. You probably don't want to store it in a page item that could be visible in the HTML to end users.

    The API result in this example is being stored in page item P101_RESULT. You may want to store it somewhere else.

    There's also examples of this on the Oracle-Base site