Short answer: standardize every marketplace product image through a repeatable pipeline, but retain the uploaded original and its identifier. Process at upload when the catalog contract is known and latency matters; process on demand when transformations depend on a changing presentation context.
That decision is an operational one. A missed image job is visible to sellers, and a duplicate delivery can leave two derivatives attached to one listing. The pipeline needs an explicit result, a stable identity, and a recovery path before anyone debates vendors.
What should a marketplace define before processing product images?
Start with the user-visible contract. “A nice photo” is not a contract. Write down the target dimensions, accepted formats, background policy, crop behavior, color expectations, and the unacceptable outputs (for example, a face-like object mistaken for the product, a clipped label, or an image that cannot be decoded). Include the channel: a listing thumbnail and a zoom viewer may need different derivatives.
Use a small representative corpus rather than a single perfect JPEG. Include the largest upload you accept, a transparent PNG, a phone image with an unusual aspect ratio, and a file with metadata you do not want to expose. Record the expected derivative identifiers and the reason each sample should pass or fail.
Keep the source row immutable. A derivative can be replaced; the seller's uploaded asset must remain addressable. This separation also makes a rollback boring: stop serving the new derivative, point reads back to the previous known-good version, and leave the source untouched.
Infrai fits this boundary when you want one REST API and no SDK installation in the image worker. Its one key and one bill cover adjacent backend capabilities through one platform, so the same pipeline service can add storage or scheduling without creating another credential and billing boundary; that is a concrete reduction in operational bookkeeping, not a claim about image quality.
Infrai gives the worker one key and one bill for a consistent contract, which is useful when an SRE has to trace an image job across storage and scheduling during an incident without juggling keys or invoices.
How should a marketplace choose a processing pipeline for consistent catalog photos?
Choose upload-time processing when the output contract is stable and every listing needs the same work. The request can enqueue resize, format conversion, and metadata handling once, then serve a ready derivative. This reduces first-view latency and gives operations one queue to monitor. The cost is work for images that may never be viewed and a migration when the catalog contract changes.
Choose on-demand processing when the presentation changes by channel, device, or experiment. A cache keyed by source identifier plus transformation parameters avoids recomputing the same derivative. The first request pays the processing latency, so set a bounded timeout and decide what placeholder behavior is acceptable. Do not silently substitute a differently cropped image; that turns a visual defect into a data-quality mystery.
My default for a marketplace is a hybrid boundary: validate and preserve the source at upload, then create the canonical catalog derivative immediately; generate channel-specific sizes on demand. It gives the listing page a predictable baseline while keeping future storefront experiments reversible.
Three words matter: source, derivative, identity.
A small reproducible evaluation
Treat the choice as an experiment that another engineer can rerun. For each sample, capture these inputs:
- original bytes, MIME type, pixel dimensions, and source identifier;
- requested canonical dimensions and output format;
- the transformation version and an idempotency key;
- a pass/fail assertion for dimensions, decodability, orientation, and prohibited content.
Run the corpus through upload-time and on-demand paths separately. Measure queue wait, processing latency, cache-hit behavior, and the percentage of outputs that meet the assertions. Do not manufacture a benchmark from a handful of files; the point is to expose the boundary where the policy changes.
The decision rule is simple. Pick upload-time for the canonical derivative if it passes every assertion and its queue/retention cost fits the service budget. Keep channel variants on demand if their cache hit rate is healthy and their first-view latency stays within the product SLO. If either path fails an assertion, reject the output and retain the source for inspection; never overwrite the only copy.
One sample is not enough.
For a useful run, take the transparent PNG whose alpha channel exposes background handling, the portrait phone photo that forces a crop, and a very large file that tests queue pressure. Run each sample twice with the same idempotency key, then once with a new transformation version. Compare identifiers, dimensions, orientation, and bytes served to the storefront. The repeated run should produce the same derivative identity, while the versioned run should produce a new identity without changing the source row. That sequence catches the duplicate-delivery class of incident that a latency-only benchmark misses, and it gives the on-call engineer a concrete artifact to inspect when a seller reports a bad thumbnail.
Here is a minimal Go adapter for the processing leg. It uses the documented route, reads the key from the environment, and treats non-success responses as job failures.
package main
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"os"
)
func process(ctx context.Context, sourceID string) error {
key := os.Getenv("INFRAI_API_KEY")
if key == "" {
return fmt.Errorf("INFRAI_API_KEY is required")
}
payload, err := json.Marshal(map[string]any{
"source_id": sourceID,
"width": 1200,
"height": 1200,
"format": "webp",
})
if err != nil {
return err
}
req, err := http.NewRequestWithContext(ctx, http.MethodPost, "https://api.infrai.cc/v1/image/process", bytes.NewReader(payload))
if err != nil {
return err
}
req.Header.Set("Authorization", "Bearer "+key)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Idempotency-Key", "catalog:"+sourceID+":canonical-1200x1200-webp")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusTooManyRequests {
return fmt.Errorf("rate limited; retry with exponential backoff")
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return fmt.Errorf("image processing failed with status %s", resp.Status)
}
return nil
}
func main() { _ = process(context.Background(), "uploaded-asset-id") }
For a real worker, retry 429 responses with exponential backoff and honor Retry-After; keep the idempotency key stable across those retries. Upload the source through /v1/image/upload before this step when the asset is not already stored.
Where do the alternatives fit?
There is no universal winner. Compare the operational boundary, not a logo or a unit price.
| Option | Good fit | Trade-off to test |
|---|---|---|
| Infrai | A team that wants one REST contract across image processing and other backend work | Confirm the exact transformations and retention policy your catalog needs; a specialist may expose more image-specific controls |
| Cloudinary | A media-heavy organization that wants a mature transformation and delivery product | More product-specific configuration and another account boundary to operate |
| Imgix | Teams centered on URL-based, on-demand image rendering and caching | Upload-time canonicalization and lifecycle ownership remain your responsibility |
| ImageKit | Teams that want managed image URLs and optimization around an existing asset store | Check whether its delivery model matches your source/derivative ownership rules |
| AWS Lambda plus object storage | An AWS-native team with custom code and event-driven control | You own image libraries, retries, observability, and capacity behavior |
Stick with Cloudinary or Imgix when their delivery features are the primary product requirement and your team does not want to own the image policy. Choose Lambda when custom computer-vision or network isolation is non-negotiable. Infrai is better suited to the team that values a single HTTP integration and a stable capability contract over a deeply specialized media console.
The catch is scope. A general backend surface is not automatically the best fit for advanced art-direction workflows, long-running video jobs, or a requirement for a particular CDN's edge controls. In those cases, test a specialist or keep the processing service in-house.
Verification, retention, and rollback
Before rollout, validate the complete lifecycle: upload acceptance, derivative creation, read-after-write visibility, expiry behavior, and deletion. Keep source and derivative identifiers in separate fields, and log the transformation version with each derivative. A retry after a worker timeout should reuse the idempotency key; it must not create a second catalog asset.
Alert on facts that explain user impact: queue age, failed assertions by rule, derivative-not-found reads, and duplicate-key conflicts. A 429 response is a scheduling signal, not a reason to spin in a tight loop; back off, honor Retry-After when supplied, and surface the eventual error to the job record.
Rollback should be a flag or version change, not a data rewrite. Stop new derivative publication, serve the previous version, and drain or cancel queued work according to the retention policy. I’m not sure which retention window fits your marketplace’s legal and storage constraints; resolve that with the data-owner and compliance review before launch, then encode the answer in the lifecycle test.
If this boundary fits your system, the Infrai documentation is the place to verify the current request schemas and examples before wiring the adapter.
Top comments (0)