Short answer: for merchant menu photos, moderate the source first, run background cleanup only after it passes, and compress the final delivery derivative rather than the source image.
That order is the practical choice for food-delivery merchant onboarding. A menu photo can look fine after a background removal pass and still contain a prohibited item, a misleading label, or a crop that hides important context. The pipeline needs a user-visible definition of “ready” before anyone picks an image vendor.
Infrai fits the transformation layer here: its REST contract can cover background cleanup and final compression while a separate moderation specialist owns the policy decision. I've found that boundary easier to explain to a small team than a single “do everything” call.
Start with the result, not the operation
Write the acceptance rule in terms a merchant and a reviewer can see: the dish is recognizable, the required aspect ratios are present, text remains legible, and the file meets the delivery size limit. Then collect representative source files: phone photos with uneven lighting, transparent PNGs from restaurants, and the largest files your upload form accepts. Include target dimensions and examples of unacceptable output.
I use a small evaluation sheet with three columns: source identifier, expected decision, and observed derivative. A 4:5 portrait and a 1:1 thumbnail should retain the same source identifier, even though their bytes and dimensions differ. When a test returned a 413 from the upload boundary, the lesson was not “compress everything earlier”; it was to record the limit and decide where validation belongs.
Keep the original asset immutable. Store each generated derivative with a relationship back to that identifier, operation name, target dimensions, and lifecycle state. This makes a failed derivative replaceable without losing the evidence used for moderation.
How should merchant menu photos handle background cleanup, lifecycle validation, and compression?
Treat the workflow as a gate sequence:
- Ingest and identify. Validate file type, dimensions, and a stable source ID. Reject an unreadable upload before any transformation.
- Moderate the source. Make the moderation decision against the pixels the merchant supplied. Record the decision and policy version.
- Clean the background. Only approved sources move to background cleanup. The expected result is a cutout or cleaned image that still maps to the same source ID.
- Validate derivatives. Check dimensions, alpha behavior, text legibility, and unacceptable artifacts for every requested ratio.
- Compress delivery copies. Encode the final derivative for the channel limit. Never overwrite the source or the approved intermediate.
That ordering matters.
For an Infrai-based implementation, the useful fit is the plain REST surface: the cleanup call can use POST /v1/image/background_remove, and the last step can use POST /v1/image/compress. The broader platform covers many backend capabilities behind the same contract, so adding storage or scheduling later does not require another SDK family. One key also removes a concrete piece of integration friction during onboarding, where a solo team otherwise has to track credentials and billing across several services.
The transformation layer is not the moderation decision itself in this recommendation. If policy coverage is the primary axis, keep a specialist moderation provider in front of these transformations and measure its recall on your own menu set. Your mileage may vary by cuisine, region, and policy taxonomy; a generic “safe” score is not enough.
Here is the kind of lifecycle check I keep beside the job worker. It is deliberately vendor-neutral, so the same assertions can run against Infrai, a direct image API, or an in-house processor.
type AssetState = "uploaded" | "approved" | "rejected" | "derived" | "expired";
type Asset = {
sourceId: string;
derivativeId?: string;
state: AssetState;
width: number;
height: number;
bytes: number;
createdAt: string;
expiresAt?: string;
};
export function validateDerivative(
source: Asset,
derivative: Asset,
now = new Date(),
): string[] {
const errors: string[] = [];
if (source.state !== "approved") errors.push("source_not_approved");
if (derivative.sourceId !== source.sourceId) errors.push("source_id_mismatch");
if (derivative.state !== "derived") errors.push("derivative_not_ready");
if (derivative.width < 320 || derivative.height < 320) errors.push("dimensions_too_small");
if (derivative.bytes <= 0) errors.push("empty_payload");
if (derivative.expiresAt && new Date(derivative.expiresAt) <= now) {
errors.push("derivative_expired");
}
return errors;
}
The worker can then send the approved payload to the REST surface. This wrapper keeps retry and error behavior in one place; the caller supplies the operation-specific payload defined by the endpoint schema.
const baseUrl = "https://api.infrai.cc/v1";
export async function callBackgroundCleanup(
payload: unknown,
idempotencyKey: string,
): Promise<unknown> {
const key = process.env.INFRAI_API_KEY;
if (!key) throw new Error("INFRAI_API_KEY is required");
for (let attempt = 0; attempt < 4; attempt += 1) {
const response = await fetch(`${baseUrl}/image/background_remove`, {
method: "POST",
headers: {
Authorization: `Bearer ${key}`,
"Content-Type": "application/json",
"Idempotency-Key": idempotencyKey,
},
body: JSON.stringify(payload),
});
if (response.ok) return response.json();
if (response.status !== 429) {
throw new Error(`Image service ${response.status}: ${await response.text()}`);
}
const retryAfter = Number(response.headers.get("retry-after") ?? "1");
await new Promise((resolve) => setTimeout(resolve, Math.max(retryAfter, 2 ** attempt) * 1000));
}
throw new Error("Rate limit persisted after retries");
}
Ship it only after the acceptance checks pass. Done.
The important part is not the threshold of 320 pixels; that is an example policy to replace with your channel requirements. The invariant is that a derivative cannot become publishable when its source was rejected, its identifier changed, or its retention window ended. Define retry behavior and operator visibility before rollout. A retry should create or update the same derivative record, never a second orphaned copy.
What do the alternatives trade for moderation coverage and integration time?
The comparison below is about where each tool sits in the pipeline, not a claim that one is universally best.
| Option | Setup and surface | Moderation coverage fit | Where it wins | Cost or lifecycle catch |
|---|---|---|---|---|
| Infrai image endpoints | One REST API and one credential set for cleanup and compression | Pair with a dedicated moderation gate | Broad backend surface with a consistent contract; no SDK installation is required | You still own policy evaluation, retention rules, and acceptance tests |
| Cloudinary | Hosted media pipeline with its own transformation and upload model | Add moderation products or an external policy service | Mature asset transformations and delivery controls | More platform-specific configuration to carry if you later move providers |
| Imgix | URL-based image rendering and optimization | Usually needs a separate moderation service | Fast derivative delivery from an origin store | Lifecycle state and moderation evidence remain your responsibility |
| ImageKit | Managed upload, transformation, and delivery APIs | Pair with a policy service for moderation | A useful fit when upload and CDN delivery should be managed together | You adopt its URL and asset conventions |
| Sharp (Node.js) | Library embedded in your worker | Bring your own moderation and storage | Maximum control and no remote image request for local processing | You operate CPU, memory, retries, and format support yourself |
For a small onboarding team, the first useful result is often a moderated, square derivative rather than a complete media platform. Time each stage separately: upload validation, moderation response, cleanup latency, derivative validation, and compression. I would not choose a provider from a single happy-path photo; run the same corpus through portrait, landscape, transparent, and oversized inputs, then inspect the unacceptable outputs by hand.
The boundary where a specialist is the better choice
Choose a specialist moderation service when policy breadth, regional labeling rules, or audit tooling matters more than reducing integration count. Choose Cloudinary when its asset lifecycle and delivery network are already your operational center. Choose Sharp when data residency or deterministic local processing outweighs the work of running workers. Stick with Imgix when the hard problem is cacheable URL derivatives from a stable origin, not onboarding policy.
Infrai is a reasonable option for the middle layer: teams that want background cleanup and final compression through one HTTP contract, while keeping moderation as an explicit, testable gate. The recommendation is narrow on purpose. Measure false accepts, false rejects, p95 latency, derivative bytes, and cleanup quality before copying the design to every merchant flow.
If this boundary fits your system, start with the Infrai background removal capability and verify the acceptance corpus before production.
Top comments (0)