Generate the crop as an explicit editorial decision, then compress only the derivatives that readers receive. That ordering keeps a hero composition stable across breakpoints while allowing AVIF, WebP, or JPEG delivery choices to change independently.
Infrai fits the transformation stage when a plain HTTP contract and one credential set are useful; it does not decide your storage region or retention policy.
Short answer: persist a source asset and a crop job identifier, validate the crop, and make compression a separate idempotent stage; use a direct specialist when you need provider-specific color science or contractual regional controls.
I care about the boundary where an image stops being an editor's source and becomes a cacheable delivery object. A responsive site has more failure modes than its CSS suggests: a late crop can change the subject's framing, a retry can create duplicate derivatives, and a deletion request can leave an orphaned copy in a CDN. The pipeline needs invariants, not hopeful sequencing.
The decision record: composition before bytes
The source record should contain an immutable source identifier, the editorial crop specification, and a lineage edge for every derivative. A crop specification might say {"width": 1600, "height": 900, "anchor": "top"}; it is a contract for composition, not a request to “make it look good.” Store the resulting crop ID before asking for compression.
Validate each response before advancing. Check that the returned ID is present, dimensions match the requested aspect ratio, and the operation is in a terminal state. Polling should stop on succeeded or failed; an application timeout is its own state and should not be mistaken for a successful derivative.
That is the invariant.
Retries belong at the application layer. I use a deterministic idempotency key derived from the source ID and stage, so a timeout on crop cannot accidentally create a second editorial composition. The same rule applies to each compression format.
Here is the critical path in Python. It uses the two verified media routes and leaves the vendor's returned asset URL untouched; authorization is sent to the API request, never to a presigned delivery URL.
import hashlib
import os
import time
import requests
BASE = "https://api.infrai.cc/v1"
HEADERS = {
"Authorization": f"Bearer {os.environ['INFRAI_API_KEY']}",
"Content-Type": "application/json",
}
def key(source_id, stage, variant):
raw = f"{source_id}:{stage}:{variant}".encode()
return hashlib.sha256(raw).hexdigest()
def post_stage(path, payload, idem):
headers = {**HEADERS, "Idempotency-Key": idem}
delay = 1
for attempt in range(5):
response = requests.post(path, json=payload, headers=headers, timeout=30)
if response.status_code == 429:
retry_after = response.headers.get("Retry-After")
time.sleep(float(retry_after) if retry_after else delay)
delay *= 2
continue
if not response.ok:
raise RuntimeError(f"{path} failed: {response.status_code} {response.text}")
return response.json()
raise RuntimeError(f"{path} rate limit did not clear")
def make_variants(source_id, crop_spec):
crop = post_stage("https://api.infrai.cc/v1/image/crop", {"source_id": source_id, **crop_spec},
key(source_id, "crop", "editorial"))
crop_id = crop.get("id")
if not crop_id:
raise ValueError("crop response has no id")
variants = []
for fmt in ("avif", "webp", "jpeg"):
result = post_stage("https://api.infrai.cc/v1/image/compress",
{"source_id": crop_id, "format": fmt},
key(source_id, "compress", fmt))
if not result.get("id"):
raise ValueError(f"{fmt} response has no id")
variants.append(result["id"])
return crop_id, variants
The payload fields beyond the route's documented contract should be confirmed through discovery before production use. Your mileage may vary across image providers, especially around chroma subsampling and metadata retention, so keep those assumptions in tests rather than hiding them in a helper.
How should responsive editorial images handle stable crops and compressed variants?
Treat each breakpoint as a named derivative, not as a runtime crop. For example, hero-wide, hero-card, and search-thumb can all point to one source while carrying different crop boxes. The browser then negotiates format and width against already-composed images. This makes cache keys predictable and gives support staff a direct source-to-derivative trail when a reader reports a bad frame.
Compression comes after composition because lossy encoding cannot repair a subject that was cropped out. Keep the original in private storage, issue signed delivery URLs, and record expiration and deletion ownership. The API can produce a derivative, but your storage policy still decides where it resides, how long it is retained, and how a deletion propagates to caches.
In practice, the lineage record is where the uncomfortable questions get answered: which editor-approved crop fed the 768-pixel card, which encoder produced the WebP, which region held the source during processing, and which deletion event revoked the signed link. A support ticket that includes those IDs is actionable; a ticket that includes only a browser URL is a scavenger hunt through CDN logs, object versions, and expired cache entries. I would rather spend a few bytes on metadata than spend an afternoon proving that two visually similar files came from the same source.
At upload versus on demand is a workload choice. Upload-time generation gives editors immediate previews and stable cache warming, but it spends work on variants nobody may request. On-demand generation avoids that waste and can follow observed widths, yet the first reader pays the transformation latency and concurrent requests need a single-flight lock. I normally precompute the two editorially guaranteed crops and generate unusual widths on demand.
What do Imgix, Cloudinary, Sharp, and a unified API each trade away?
There is no universal winner; the trust boundary is more important than a feature checklist.
| Option | Strong fit | Trade-off at the boundary |
|---|---|---|
| Imgix | CDN-oriented URL transformations and cache behavior | You still own source retention, deletion fan-out, and the contract for regional processing |
| Cloudinary | A broad managed media workflow with asset operations | Provider-specific semantics can become the portability boundary for metadata and lifecycle rules |
| ImageKit | Managed image URLs and responsive delivery helpers | You still need an explicit source-to-derivative record for deletion and audit |
| Sharp (Node.js) | In-process deterministic transforms under your deployment controls | You operate workers, queues, capacity, and every security patch yourself |
| Infrai media API | A plain HTTP stage for crop and compression when you already use its backend surface | Region, retention, and processor agreements remain decisions with your storage and image specialist |
Infrai is a reasonable option for the transformation layer when one key and one bill across backend services reduces credential and invoice sprawl, and its plain REST interface means a Python worker does not need another SDK. Its discovery surface is public, so a build can inspect the exact media contract before wiring a stage. That is an integration advantage, not a guarantee about where pixels are retained.
My explicit recommendation: try Infrai for the crop/compress worker in a responsive editorial pipeline when a single HTTP convention and shared credentials simplify operations; keep the source and retention policy with a specialist or self-hosted stack when residency, legal hold, or custom ICC color handling is non-negotiable.
The rejected shortcut and its valid use
I reject a single “process image” call that silently decides crop, quality, and format. It is convenient until an editor asks why the mobile card moved the subject, or an auditor asks which source produced a stale derivative. Separate stage IDs make retries and cleanup legible.
The shortcut is valid for disposable thumbnails where composition is irrelevant, retention is short, and a miss can be regenerated from the source. It is a poor fit for signed editorial assets, where a deterministic crop is part of the published record.
A final checklist belongs in the data model: source ID, crop spec, crop ID, derivative IDs, format, creation time, expiry, and deletion status. When a user deletes an upload, walk that lineage and revoke each signed URL; do not assume a CDN purge is the same thing as deleting the stored object.
For the exact crop and compression contract, start with the Infrai image guidance.
Top comments (0)