StashFyle
Stack-agnostic file storage API—POST a file, get a URL.
- DevTool
- SaaS
- Full Stack
Removing the file-upload tax
StashFyle started from a recurring frustration: file storage is rarely the interesting part of a product, but implementing it can consume an unreasonable amount of time. A simple upload often turns into bucket policies, IAM credentials, CORS rules, presigned URLs, an SDK, metadata tables, and a billing calculator before the first file reaches a user.
I wanted the smallest useful abstraction over that machinery. StashFyle is a REST API that works with anything capable of making an HTTP request. There is no required SDK, framework adapter, or upload widget: send a multipart file with an API key and receive a stable URL.
curl -X POST https://api.stashfyle.com/v1/upload \
-H "Authorization: Bearer sk_live_xxx" \
-F "file=@photo.jpg"{
"id": "f_abc123xyz",
"url": "https://cdn.stashfyle.com/.../photo.jpg",
"size": 248000,
"type": "image/jpeg"
}That constraint shaped the whole product. The API should feel equally natural from curl, JavaScript, Python, Go, Ruby, PHP, or a shell script. Choosing plain HTTP as the primary interface made StashFyle less magical, but much harder to outgrow.
Designing the one-request upload
The basic endpoint stays intentionally boring: one required file field and a few optional controls. A caller can organize files with folder, make one private with private=true, or give temporary output an expiration such as 1h, 7d, or 30d. Public uploads return a CDN URL immediately; private uploads return an ID that can be exchanged for time-limited access.
I kept the response format stable across endpoints and gave every failure a machine- readable code alongside a useful message. A caller can distinguish a file-size problem from an exhausted storage quota or a rate limit without parsing prose. Every response also carries rate-limit headers, which makes backoff and monitoring possible without another API call.
Listing files uses cursor pagination rather than page numbers, so adding new uploads does not reshuffle an integration’s place in the collection. The same API supports folders, metadata lookup, deletion, usage reporting, and health checks without changing the core mental model.
What sits behind the abstraction
The public surface is small because the service absorbs the coordination work. A Next.js route authenticates the request, applies account and plan rules, and sends the object to Cloudflare R2. StashFyle then records the file’s ownership, storage key, type, size, visibility, folder, and expiration in Postgres before returning an API-safe response.
I used R2 because it exposes the familiar S3 protocol while removing egress fees from the product’s cost model. Public files are served through a CDN URL. Private objects never receive a public URL; the service signs an R2 request for a caller-selected duration instead.
The response can be stored and rendered immediately. Delivery happens from Cloudflare’s edge network.
Access is granted on demand for one minute to seven days, without exposing the secret key to a browser.
Separating object storage from metadata also keeps the API flexible. The object key is deterministic and namespaced by user, folder, file ID, and original filename, while the database remains the source of truth for ownership and state. Deletion first marks the record unavailable to the product, removes the underlying object, and reconciles the user’s storage usage.
Making “works anywhere” safe
Stack-agnostic should not mean one credential with unlimited power. StashFyle has secret and public API keys with separate capabilities. Secret keys are intended for servers and can manage files or create signed URLs. Public keys can be embedded in a browser, but they are restricted to uploads and checked against an explicit allowlist of origins.
Keys are only shown once and stored as hashes. Each request resolves the key to its owner and plan, then passes through a sliding-window rate limiter keyed to that credential. The upload path also enforces file-size, storage, and subscription limits before writing data. Private-file operations verify both the caller and file ownership, so knowing a file ID is not enough to access it.
pk_Browser-safe upload access, constrained by allowed origins. It cannot list files, create signed links, or retrieve private metadata.
sk_Server-side access to the complete API. It can manage files and issue temporary links, so it never belongs in client code.
Operating the service, not just the endpoint
Building the upload route was only part of making StashFyle usable as a service. Every request is logged with its endpoint, status, latency, file size, error code, API key, and originating IP. The dashboard turns those records into a practical view of files, keys, usage, billing, and failures rather than exposing the underlying infrastructure.
Auto-expiring files are handled by a scheduled cleanup job that removes the R2 object, soft-deletes its metadata, and returns the consumed storage to the account. Stripe controls plan state and limits. Downgrades use a grace period instead of immediately breaking downloads, which gives someone time to reduce usage before cleanup is needed.
The documentation was part of the product from the beginning. The quickstart gets from account creation to a real upload in a few minutes, and the Mintlify reference documents authentication, endpoint schemas, errors, rate-limit behavior, private files, and examples across multiple languages. A simple API only feels simple when its edge cases are easy to discover.
What I took away
StashFyle is an exercise in moving complexity to the right side of an interface. The developer integrating it should see one endpoint and one predictable response. The service still has to care about object storage, identity, abuse, quotas, billing, lifecycle management, and observability—it just should not make every customer rebuild those pieces.
I also chose to release the project under the MIT license. For infrastructure products, being able to inspect how credentials, storage, and limits are handled is useful in itself. The repository is both the implementation and a concrete explanation of what StashFyle does on someone’s behalf.