Managing access to information can be a delicate task. You may need to expose certain data to a client but not grant full access. Cloud functions on AWS and Google Cloud Platform (GCP) can be a great way to achieve this controlled access.

I’ll use GCP for my current example.

Below, I will walk through a function that retrieves and filters data from a third-party service. The deployment method has changed from the original zip-upload example, but the function remains the same small middle layer and uses the current Functions Framework form.

Function

index.js:

const functions = require("@google-cloud/functions-framework");

const API_BASE_URL = process.env.API_BASE_URL;
const SERVICE_TOKEN = process.env.SERVICE_TOKEN;
const CLIENT_ID = process.env.CLIENT_ID;

functions.http("getMyData", async (req, res) => {
  if (req.method !== "GET") {
    res.set("Allow", "GET").status(405).json({ error: "method not allowed" });
    return;
  }
  if (!API_BASE_URL || !SERVICE_TOKEN || !CLIENT_ID) {
    console.error("Required configuration is missing");
    res.status(500).json({ error: "service is not configured" });
    return;
  }

  const headers = { Authorization: `Bearer ${SERVICE_TOKEN}` };
  try {
    const sitesResponse = await fetch(`${API_BASE_URL}/sites`, {
      headers,
      signal: AbortSignal.timeout(10_000),
    });
    if (!sitesResponse.ok) throw new Error(`sites returned ${sitesResponse.status}`);
    const payload = await sitesResponse.json();
    if (!Array.isArray(payload.our_sites)) throw new Error("invalid sites response");

    const sites = payload.our_sites.filter(
      (site) => site.client_id === CLIENT_ID && site.type === "report-on-site"
    );
    const details = await Promise.all(sites.map(async (site) => {
      if (typeof site.id !== "string") throw new Error("invalid site id");
      const url = `${API_BASE_URL}/check-site/${encodeURIComponent(site.id)}`;
      const response = await fetch(url, { headers, signal: AbortSignal.timeout(10_000) });
      if (!response.ok) throw new Error(`check returned ${response.status}`);
      return (await response.json()).check;
    }));

    res.status(200).json(details);
  } catch (error) {
    console.error("Upstream request failed", error);
    res.status(502).json({ error: "upstream service unavailable" });
  }
});

I use exact field comparisons rather than treating configuration as a regular expression, validate the upstream shape, set timeouts, and return a generic error instead of serializing tokens or upstream details.

package.json:

{
  "name": "filtered-client-data",
  "version": "1.0.0",
  "private": true,
  "engines": { "node": "22" },
  "dependencies": {
    "@google-cloud/functions-framework": "^3.4.0"
  },
  "scripts": {
    "start": "functions-framework --target=getMyData"
  }
}

Modern Node runtimes provide fetch, so axios is no longer needed. The platform installs declared dependencies from package.json; I do not upload node_modules. Cloud Run functions Node dependencies

Configuration and Deployment

I store the bearer token in Secret Manager and keep non-secret selection values as environment variables:

printf '%s' 'replace-me' | gcloud secrets create special-service-token --data-file=-

gcloud run deploy get-my-data \
  --source . \
  --function getMyData \
  --base-image nodejs22 \
  --region europe-west1 \
  --set-env-vars API_BASE_URL=https://api.the-special-service.example,CLIENT_ID=my-client-id \
  --set-secrets SERVICE_TOKEN=special-service-token:latest

Source deployment builds and installs the package dependencies. I leave the service authenticated and grant roles/run.invoker only to the caller that needs it; adding --allow-unauthenticated would expose the endpoint publicly. Deploy Cloud Run functions and service authentication

This is still boilerplate, but it now runs as written and preserves the purpose of the original function: expose only the filtered data the client needs.



Buy Me a Coffee