Introduction
When building expiring content, the first thing many people wrestle with is "where do I store the data?"
Standing up a relational database is the textbook answer, but an auxiliary feature like temporary pages or event galleries doesn't need that much heavy equipment.
In this article, I'll explain why I managed temporary content using only Vercel KV (a key-value store), along with the concrete data design.
Why does "getting by without a database" turn out to be the right call in this case? From choosing the storage to key design and where TTL fits, I'll share what I learned hands-on.
Why I Chose KV
Avoiding the cost of standing up a database
A relational database is powerful, but it comes with real setup and operational costs.
You design schemas, manage migrations, and mind connection pools and backups. That effort pays off for serious data, but for data that "exists only for a while and then disappears," it's honestly overkill.
With KV, you just put and get a key and a value. No table design, no SQL. The speed of startup and the lightness of operation really pay off for nimble features like this.
The kind of data that suits key-value
The data for a temporary page is structurally very simple.
"Give me a slug (part of the URL) and I'll return that page's body, images, and expiration date" — this is exactly the access pattern key-value excels at. Complex joins and cross-cutting searches almost never come up.
For the admin screen that needs a listing, keeping a separate key as an index of slugs is enough. Because the data shape is straightforward, a simple tool like KV fits it perfectly.
A natural fit with Vercel
Since this system runs on Vercel, using Vercel KV made integration very smooth.
You connect with just an environment variable, and read and write directly from serverless functions. No need to stand up separate infrastructure or carefully manage connection credentials.
Using the tools the platform provides, as-is, keeps the architecture simple and reduces the seeds of trouble. Knowing when to just "use what's available" is part of good design judgment.
Key Points of the Data Design
Key design and data structure
In KV, how you name your keys directly determines the quality of your design.
I store each temporary page's data together under a prefixed key like page:{slug}. The value bundles the body, image URLs, expiration date, and publish state into a single JSON object.
Adding a prefix lets you organize data by type and avoids name collisions when you add another content type later. It's a small thing, but deciding the rules up front makes operation much smoother.
How to hold the expiration date
I hold the expiration date as an explicit field in the data.
For example, I put an ISO-format datetime like "2026-03-31T23:59:59" in expiresAt. That way, both when cron judges expiration and when the public page checks whether access is allowed, they simply look at the same value.
The key point is putting the data in a state where it "knows" when it will disappear. The expiration logic doesn't scatter across the codebase — it consolidates in one place, so changing the spec later is safe.
Where to put the images
The body is light enough to store in KV, but I avoid stuffing images directly into KV.
Event gallery photos and the like go to Cloudinary, which is strong at image delivery, and I store only their URLs in KV. KV handles metadata and body text; heavy binaries are left to dedicated storage. It's a division of labor.
This keeps the KV data lightweight and lets Cloudinary handle image transformation and resizing. It's a straightforward design that uses each tool for what it's best at.
Body, expiration, publish state Image URLs (references only)
The actual gallery images Also handles transforms/resizing
Assembles metadata from KV and images from Cloudinary for display
Choosing Between TTL and an Explicit Flag
Auto-deleting with KV's TTL
KV has a handy feature called TTL (Time To Live). When you save data, you specify a lifespan in seconds, and once that time passes, the data is deleted automatically.
For data that "can vanish without a trace once it expires," this is the easiest option. Without even waiting for cron, KV cleans up after itself. The implementation is simple too — just compute the seconds until expiration and pass it as the TTL.
The strength of TTL is that forgetting to take content down becomes physically impossible.
Use explicit management when you want to keep history
On the other hand, there are cases where you want to "expire it but keep the data."
For instance, when you want to show "this page is no longer available" after expiration. If TTL deleted it, you couldn't even show that notice. In such cases, instead of relying on TTL, you manage explicitly with the expiresAt field and a publish flag.
Whether the deadline has passed is judged by comparing datetimes in cron or at display time, while the data itself remains. Being able to treat expiration as "unpublish" rather than "delete" broadens your operational options.
For data that can be fully deleted. Simplest to implement, and forgetting to take it down is physically impossible
For data where you want a notice or history. The data is kept; only the publish state is toggled
Rule of thumb
Use TTL for "disposable data you won't miss," and an explicit flag for "data you'll want to reference or notice about later." Deciding this line up front keeps your implementation from wavering.
Conclusion
Managing temporary content doesn't always require a heavyweight database. When the data shape is simple and the lifespan is limited, a lightweight tool like KV makes both startup and operation easier.
Three points stand out: organize data with prefixed keys, hold the expiration date in the data itself, and use TTL and explicit flags for different jobs based on requirements. This design forms the foundation that automates the cleanup.
The process that actually "expires it when the deadline arrives" is handled by cron. That mechanism is covered in "Cron-Based Expiration and Access Control." If you'd like to revisit the big picture, the hub article "Auto-Expiring Content" is a good stop too.