Managing Temporary Content with KV Storage — Skipping the Database

How the body text and expiration date of a limited-time page can live in a key-value store alone, and why that call makes sense

Vercel KVdata designTTLcontent managementstoragetemporary pages
8 min read

Introduction

The first thing you have to settle when building a limited-time page is where the data goes. Body text, images, expiration date — none of the page renders unless something is holding them. Standing up a database is the textbook answer, but whether a supporting feature like temporary pages or event galleries really needs that weight is worth a second look.

This article explains why I managed temporary content with nothing but a key-value store, and how the data is actually shaped. A key-value store is simply a place where you give one name and get back exactly what you stored under it. By the end you should have a clear line between the cases where skipping the database is fine and the cases where it isn't.

Why I Chose KV Over a Database

The amount of setup before you can start is very different

A relational database (a system that organizes data into tables) is powerful, but there is a lot to do before you write your first record. You design which columns each table holds, you manage the migration steps for adding columns later, and you mind connection settings and backups. For core business data like orders and inventory, that effort is well spent.

Temporary page data, though, exists for a while and then goes away. Starting from table design for that kind of data means most of the preparation never pays for itself. With KV you pick a name, hand over the contents, and later fetch it back by that name. Less time on setup means more time actually trying the feature out.

The data for a temporary page is simple to begin with

List out what needs storing and it's surprisingly short. Give it the slug (the string that forms part of the URL) and it returns that page's body text, where the images live, and the expiration date. That's the whole requirement — no searches spanning multiple tables, no filtering by combined conditions.

"One name in, the whole record out" happens to be exactly what a key-value store is best at. The one place that needs more is the admin listing, and that's handled by keeping an index of created slugs under a separate name. When the shape of the data is straightforward, a straightforward tool is enough.

Matching the storage to where the site runs cuts down the wiring

The system runs on Vercel (the place where the built pages are hosted), so I used the KV store Vercel provides. Register one setting and you're connected, and the code that builds the pages can read and write directly. No separate service to sign up for, no second set of connection credentials to look after.

Fewer connection points also means fewer places to check when something stops working. Using what the platform already offers doesn't make the feature more impressive, but it's the kind of decision you appreciate months later.

The table's message: when the data is simple and short-lived, there isn't much reason to reach for the heavier tool.

How You Shape the Data Decides the Work Later

Prefix the names so types stay separated

In KV, the name you save under is the design. Temporary pages are stored under names starting with a type prefix — "page" followed by the slug, for instance. The record itself bundles the body text, where the images are, the expiration date, and whether it's currently published.

Prefixing means names won't collide when you add another kind of content later. It's an unglamorous rule, but deciding it up front changes how clear things look six months on.

Keep the expiration date inside the data itself

The expiration lives as an explicit field in the record — a datetime in a fixed format, meaning something like "through 23:59:59 on March 31, 2026."

With that in place, both the scheduled job that watches deadlines and the public page deciding whether to render look at the same single value. Another way to put it: the data knows when it's supposed to disappear. Because the rule isn't scattered around, a later request like "change the display three days before the end" only means editing one place.

Put images where images are handled well

Body text is light enough for KV, but the images themselves don't belong there. Event gallery photos go to Cloudinary (hand it an image and it optimizes size and format for delivery automatically), and KV holds only the URL pointing to it.

Splitting the roles keeps the KV records small, and resizing and format conversion are handled by the service built for it. Not stuffing everything into one box makes both boxes easier to work with.

Where temporary content lives
KV storage

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

Image delivery service

The actual gallery images Also handles resizing and conversion

Fetched by slug
Public page

Pulls the text from KV and the images from the delivery service, then assembles them

The layout in one line: light information in KV, heavy images with a service built for images, and the public page fetching both each time it renders.

Delete It, or Keep It and Hide It?

Set a lifespan at save time and let it disappear on its own

KV offers TTL (Time To Live — set a lifespan when you save and the contents are removed automatically once that time passes). You calculate how long until the deadline, pass it along, and nothing else is required.

For data that can vanish without a trace once it expires, this is the simplest option. There's not even a scheduled job to wait for. The appeal is that you've made a forgotten takedown impossible at the moment you saved.

If you want to show a notice, keep the data and stop the publishing

Sometimes you want to say "this page is no longer available" after expiry. Delete the record via TTL and you can't even show that. Given how often a URL shared on social media gets clicked after the campaign ends, that's not a trivial difference.

In those cases you skip TTL and manage with the expiration field plus a flag for whether it's published. Whether the deadline has passed is decided by comparing datetimes, and the data stays put. Treating expiry as "unpublish" rather than "delete" is what lets you choose how the page ends.

Two ways to handle expired data
Set a lifespan and auto-delete

For data that can go entirely. Simplest to build, and a forgotten takedown becomes physically impossible

Flip a flag and unpublish

For data where you want a notice or a record. The data stays; only the publish state changes

So the deciding question is whether you want to show anything after expiry.

Summary

Managing temporary content turned out not to need a heavyweight database at all. When the data is simple in shape and limited in lifespan, a light tool like KV makes both the launch and the ongoing operation easier.

Three things to get right. Organize records with prefixed names, keep the expiration inside the data itself, and choose between auto-deletion and unpublishing based on the use. Those three form the foundation that automates the cleanup. None of them is a hard call, so as long as you can describe what you want in plain words, this is a good area to work through alongside an AI agent.

The process that actually retires the data when the deadline arrives is handled by the scheduled job.