Create an API Proxy Server for No-Code

When working with no-code tools like Bubble, dealing with API data can often be a frustrating experience. You may find yourself in situations where you need to manipulate or transform the data before you can use it effectively in your application. Unfortunately, the limitations of no-code tools can make this task feel like an uphill battle. You might find yourself resorting to complicated workarounds or even giving up altogether.

But what if there was a way to overcome these limitations? Enter the concept of a proxy server. By setting up a proxy server, you can intercept the API calls made by your no-code application and perform data transformations on the fly. This means you can take the raw API response, modify it to fit your needs, and then pass it along to your no-code tool for seamless integration.

Now, the idea of setting up a proxy server might sound intimidating, especially if you’re not familiar with coding. But here’s the good news: it’s not as scary as it seems. With the right approach and tools, you can create a serverless API proxy server that handles the data transformations for you, without the need for complex infrastructure or server management.

In this article, we’ll guide you step-by-step in creating a serverless API proxy server tailored for your no-code application. This proxy server will serve as a bridge between your no-code platform and the API, enabling you to customize API responses to suit your application’s needs. By the end of this guide, you’ll have a robust solution that enhances your no-code development capabilities and unlocks new ways to work with API data.

So, let’s dive in and see how you can take control of your API data transformations and streamline your no-code development workflow.

Cloudflare Workers

Cloudflare workers are a serverless edge cloud computing instances…in other words, you can run some JavaScript in the cloud and not have to deal with setting up your own server. And the best part is Cloudflare has a generous free plan.

Sign Up for Cloudflare

First register an account with Cloudflare if you don’t already have one. You can skip the register a website and click “Explore other products”.

Create a Hello World Worker

On the left hand nav, click “Workers and Pages”. In the center of the screen click “Create Worker”.

Next, you’re create a simple Hello World worker. Click “Deploy” at the bottom of the screen and finally, click the deployed URL, which looks something like: https://hello-world-calm-bird-9b18.mad-be1.workers.dev/

Congrats! Your Hello World worker is up and running.

The Proxy and Transformer

Set Up the Proxy

Back on the screen with the worker URL, click “Edit Code”.

On the left side will be the Hello World code. Delete this code and replace with:

addEventListener('fetch', event => {
  event.respondWith(handleRequest(event.request));
});

async function handleRequest(request) {
  const apiKey = request.headers.get('Authorization');

  if (!apiKey) {
    return new Response('Missing Authorization header', { status: 401 });
  }

  const profileKey = request.headers.get('Profile-Key');

  let apiUrl;
  let requestOptions;

  if (request.method === 'GET') {
    // Get the API endpoint from the query parameter
    apiUrl = new URL(request.url).searchParams.get('endpoint');

    if (!apiUrl) {
      return new Response('Missing endpoint parameter', { status: 400 });
    }

    // Decode the URL-encoded endpoint
    apiUrl = decodeURIComponent(apiUrl);

    requestOptions = {
      method: 'GET',
      headers: {
        'Authorization': apiKey,
        'Content-Type': 'application/json',
      },
    };

    if (profileKey) {
      requestOptions.headers['Profile-Key'] = profileKey;
    }
  } else if (request.method === 'POST' || request.method === 'PUT') {
    // Parse the request body as JSON
    const requestBody = await request.json();

    apiUrl = requestBody.endpoint ?? new URL(request.url).searchParams.get('endpoint');

    if (!apiUrl) {
      return new Response('Missing endpoint parameter', { status: 400 });
    }

    requestOptions = {
      method: request.method,
      headers: {
        'Authorization': apiKey,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(requestBody),
    };

    if (profileKey) {
      requestOptions.headers['Profile-Key'] = profileKey;
    }
  } else {
    return new Response('Method not allowed', { status: 405 });
  }

  try {
    // Make the API request with the appropriate options
    const response = await fetch(apiUrl, requestOptions);

    // Parse the response JSON
    const data = await response.json();

    // Transform the data structure based on the endpoint
    const transformedData = transformData(data, apiUrl);

    // Return the transformed data as the response with the returned response.status
    return new Response(JSON.stringify(transformedData), {
      status: response.status,
      headers: { 'Content-Type': 'application/json' },
    });
  } catch (error) {
    // Handle any errors
    return new Response('Error occurred', { status: 500 });
  }
}

function transformData(data, apiUrl) {
  const endpoint = apiUrl.split('/').pop();

  switch (endpoint) {
    case 'endpoint':
      // Check if the data is an object with a "data" property
      if (data && typeof data === 'object' && 'data' in data) {
        // Transform the data structure
        const transformedData = {
          data: [data.data],
        };
        return transformedData;
      }
      break;
    case 'user':
      // Check if the user endpoint is an object
      if (data && typeof data === 'object') {
        // Add in the timestamp
        const transformedData = {...data};
        data.myAddedDate = (new Date()).toISOString();
        return transformedData;
      }
      break;
    // Add more cases for other endpoint checks if needed
    default:
      break;
  }

  // Return the original data if no transformation is needed
  return data;
}

Click “Save and Deploy” in the upper right corner.

There are two primary functions in this code: handleRequest and transformData.

The handleRequest function does the proxying of the API calls. You will not need to modify this function.

The transformData is the function that does the data transformation and you will need to update this to handle your data needs. More on this later.

You may also find this code on github.

For now, let’s try running this code.

Run the Proxy – GET

A few prerequisite items to test out the proxy server with Ayrshare’s social API, but you’re welcome to use any API:

Let’s start with a GET call to the /user endpoint. In the center panel at the the end of your URL

?endpoint=https%3A%2F%2Fapp.ayrshare.com%2Fapi%2Fuser

This is a URL encoded value, which translates to https://app.ayrshare.com/api/user.

For example:

https://hello-world-calm-bird-9b18.mad-be1.workers.dev?endpoint=https%3A%2F%2Fapp.ayrshare.com%2Fapi%2Fuser

Right below the URL entry click “+ Add header”. Enter in the key “Authorization” and the value “Bearer API_KEY” where API_KEY is your API Key found in the Ayrshare Dashboard.

Now click the “Send” button.

You should see on the right panel 🟢 200 if everything was successful. Any GET URL may be used (be sure to URL encode) with the proxy server.

Run the Proxy – POST

You may also do POST requests.

Change the URL to call the analytics endpoint, which retrieves analytics on a post. Modify the query parameter of your worker URL to:

?endpoint=https%3A%2F%2Fapp.ayrshare.com%2Fapi%2Fanalytics%2Fpost

Also, check the HTTP method from GET to POST (the dropdown).

In the “Body” text box, enter:

{
    "id": "bBbtup4YDUm6agvSkQA4",
    "platforms": [
        "facebook"
    ]
}

Where the id is the ID of a published post, which you can easily do in the “Post” section of the Ayrshare dashboard.

After entering all the information, click “Send”. The response should again be 🟢 200.

Data Transformation

The final part is transforming the data to your needs.

In the code section above:

function transformData(data, apiUrl) {
  const endpoint = apiUrl.split('/').pop();

  switch (endpoint) {
    case 'endpoint':
      // Check if the data is an object with a "data" property
      if (data && typeof data === 'object' && 'data' in data) {
        // Transform the data structure
        const transformedData = {
          data: [data.data],
        };
        return transformedData;
      }
      break;
     case 'user':
      // Check if the user endpoint is an object
      if (data && typeof data === 'object') {
        // Add in the timestamp
        const transformedData = {...data};
        data.myAddedDate = (new Date()).toISOString();
        return transformedData;
      }
      break;
    // Add more cases for other endpoint checks if needed
    default:
      break;
  }

  // Return the original data if no transformation is needed
  return data;
}

Within the switch statement, specify the different endpoint endings such as “post” or “analytics”. Then transform the data based on what that particular endpoint returns. In this example, if the endpoint ends in “endpoint” and if there is a field called “data” in the data object, change the object into an array and return the transformed data. In the second example, if the endpoint ends in “user” and the “data” is an object, add the current date timestamp myAddedDate to the object.

You may add other endpoints to the switch statement and actions on those endpoints, such an changing all the values in an object from uppercase to lowercase.

Conclusion

If you’re comfortable with no-code tools but want to take your projects to the next level, learning a bit of JavaScript can be a game-changer. By incorporating JavaScript into your no-code workflows, you can unlock a whole new world of possibilities and customize your tools to better suit your needs.

But what about the hassle of setting up and maintaining servers? That’s where serverless functions come in. With serverless computing, you can run your JavaScript code without worrying about the underlying infrastructure. This means you can focus on writing and deploying your code, while the serverless platform takes care of the rest.

Now, if you’ve never written a line of JavaScript before, the thought of diving in might seem intimidating. But fear not! With a little experimentation and the help of online resources like ChatGPT, you can quickly get up to speed. Start small, try out some basic concepts, and gradually build your skills.

By combining the power of JavaScript and serverless functions with your existing no-code tools, you can streamline your workflows, automate repetitive tasks, and create more sophisticated solutions. So don’t be afraid to step out of your comfort zone and embrace the world of code. With a bit of effort and guidance, you’ll be amazed at how much easier your life in no-code land can become.