Managing Temporary Content with KV Storage

A lightweight design that manages content and expiration in KV without standing up a database

Vercel KVデータ設計TTLコンテンツ管理ストレージ
7 min read

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.

Where temporary content lives
Vercel KV

Body, expiration, publish state Image URLs (references only)

Cloudinary

The actual gallery images Also handles transforms/resizing

Fetch by slug
Public page /p/[slug]

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.

Two ways to handle expired data
Auto-delete with TTL

For data that can be fully deleted. Simplest to implement, and forgetting to take it down is physically impossible

Unpublish with an explicit flag

For data where you want a notice or history. The data is kept; only the publish state is toggled

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.