Proxy fal.ai FLUX Image Generation via CloudBase Cloud Function
In one sentence: Use
@fal-ai/clientinside a CloudBase Cloud Function to call the FLUX schnell model, pull the generated image binary back viafetch, write it to Cloud Storage with@cloudbase/node-sdk, and return a temporary URL viagetTempFileURLfor the frontend to display.Estimated time: 25 minutes | Difficulty: Beginner
Applicable Scenarios
- A Mini Program or web app that wants AI-generated images (avatars, product shots, marketing assets, placeholder illustrations) without running a model locally.
- The fal.ai key cannot be exposed to the frontend, but you don't want to stand up a separate server between the frontend and fal.
- You want the generated images stored in your own Cloud Storage rather than relying on a fal CDN link (which can expire or be purged).
- In the same Cloud Function environment, you plan to post-process the image (crop, watermark, write to a database).
Not applicable:
- Scenarios requiring very low latency where users are sensitive to generation time (this recipe uses synchronous
subscribe; one image typically takes 3–15 seconds while the Cloud Function waits). - High-volume batch generation (tens of images at a time); consider fal's queue mode + a database state machine instead — not covered here.
- Running a model directly in the browser (WebGPU) — that is a different approach and does not require a Cloud Function.
Prerequisites
| Dependency | Version |
|---|---|
| Node.js (Cloud Function runtime) | ≥ 18 (built-in fetch; older versions need node-fetch) |
@fal-ai/client | ^1.10.1 (note: @fal-ai/serverless-client is deprecated — do not use it) |
@cloudbase/node-sdk | ^3.18.1 |
@cloudbase/cli | latest |
| Cloud Function type | HTTP trigger (--httpFn) |
| Public network egress | Calling fal.ai requires public internet access; confirm "Network → Public Access" is enabled in the Console |
You will need:
- A fal.ai account with an API key from fal.ai dashboard.
- A CloudBase environment ID with Cloud Functions and Cloud Storage enabled.
Step 1: Get a fal.ai Key and Add It as a Cloud Function Environment Variable
- Open https://fal.ai/dashboard/keys, click Add Key, and copy the generated string (it is shown only once).
- This key will be injected into the Cloud Function as the environment variable
FAL_KEY. Do not put it in your code or commit it to git. - You can add it in the CloudBase Console under "Cloud Functions → Environment Variables" now or after deployment — either order works.
The fal.ai SDK automatically reads credentials from
process.env.FAL_KEY, so the variable name must be exactlyFAL_KEY.
Step 2: Write the Cloud Function
Create a directory fal-image-gen and initialize it:
mkdir fal-image-gen && cd fal-image-gen
npm init -y
npm install --save @fal-ai/client @cloudbase/node-sdk
The main field in package.json defaults to index.js, which is correct.
index.js:
const { fal } = require('@fal-ai/client');
const tcb = require('@cloudbase/node-sdk');
// Read FAL_KEY from environment variable — no key in source code
fal.config({ credentials: process.env.FAL_KEY });
const app = tcb.init({
env: process.env.TCB_ENV || tcb.SYMBOL_CURRENT_ENV,
});
exports.main = async (event) => {
// event.body is the HTTP request body string — parse it as JSON
let body = {};
try {
body = typeof event.body === 'string' ? JSON.parse(event.body) : (event.body || {});
} catch (err) {
return httpJson(400, { error: 'invalid_json', message: err.message });
}
const prompt = body.prompt;
const imageSize = body.image_size || 'landscape_16_9';
const model = body.model || 'fal-ai/flux/schnell';
if (!prompt || typeof prompt !== 'string') {
return httpJson(400, { error: 'missing_prompt' });
}
// 1. Call fal.ai to generate the image (synchronous — waits for the result)
let falResult;
try {
falResult = await fal.subscribe(model, {
input: { prompt, image_size: imageSize },
});
} catch (err) {
console.error('fal.subscribe failed', err);
return httpJson(502, { error: 'fal_failed', message: err.message });
}
const images = falResult?.data?.images || [];
if (images.length === 0) {
return httpJson(502, { error: 'no_image_returned' });
}
const imageUrl = images[0].url;
// 2. Pull the image binary back via fetch
let buffer;
try {
const resp = await fetch(imageUrl);
if (!resp.ok) {
return httpJson(502, { error: 'fetch_image_failed', status: resp.status });
}
buffer = Buffer.from(await resp.arrayBuffer());
} catch (err) {
console.error('fetch image failed', err);
return httpJson(502, { error: 'fetch_image_failed', message: err.message });
}
// 3. Write to Cloud Storage — use date + random suffix to avoid collisions
const ext = guessExt(imageUrl) || 'png';
const cloudPath = `ai-images/${dateStr()}/${Date.now()}-${rand6()}.${ext}`;
let uploadResult;
try {
uploadResult = await app.uploadFile({
cloudPath,
fileContent: buffer,
});
} catch (err) {
console.error('uploadFile failed', err);
return httpJson(500, { error: 'upload_failed', message: err.message });
}
// 4. Get a temporary URL (1 hour) for the frontend to use directly
const tmp = await app.getTempFileURL({
fileList: [{ fileID: uploadResult.fileID, maxAge: 3600 }],
});
const tempFileURL = tmp.fileList?.[0]?.tempFileURL;
return httpJson(200, {
fileID: uploadResult.fileID,
tempFileURL,
model,
prompt,
});
};
function httpJson(statusCode, data) {
return {
statusCode,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
};
}
function dateStr() {
const d = new Date();
const m = String(d.getUTCMonth() + 1).padStart(2, '0');
const day = String(d.getUTCDate()).padStart(2, '0');
return `${d.getUTCFullYear()}-${m}-${day}`;
}
function rand6() {
return Math.random().toString(36).slice(2, 8);
}
function guessExt(url) {
const m = url.match(/\.([a-zA-Z0-9]{2,5})(\?|$)/);
return m ? m[1].toLowerCase() : null;
}
A few implementation notes:
fal.subscribe(...)waits synchronously for the result — suitable for schnell, which typically returns in a few seconds. For slower models likefal-ai/flux-proorfal-ai/flux-kontext/dev, increase the Cloud Function timeout accordingly (see below).falResult.data.imagesis an array; by default only one image is generated, but the model supports anum_imagesparameter.- Do not concatenate user input directly into
cloudPath, and never include..—uploadFilewill reject it. fetchis built into Node.js 18. Set the Cloud Function runtime to 18 or later andnode-fetchis not needed.
Step 3: Deploy the Cloud Function
Log in to the CLI (interactive login locally, or use an API key in CI):
# Local
tcb login
# CI environment
tcb login --apiKeyId <YOUR_API_KEY_ID> --apiKey <YOUR_API_KEY_SECRET>
Deploy as an HTTP function:
tcb fn deploy fal-image-gen --httpFn -e your-env-id
After a successful deployment, go to the Console under "Cloud Functions → fal-image-gen" and do two things:
- Environment Variables: add
FAL_KEYwith the value from Step 1. - Timeout: fal schnell usually takes 3–8 seconds but can occasionally exceed 15 seconds. The default 3-second timeout will cause failures — set it to at least 60 seconds.
Note the access URL, which looks like https://your-env.service.tcloudbase.com/fal-image-gen.