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.
Body, expiration, publish state Image locations (references only)
The actual gallery images Also handles resizing and conversion
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.
For data that can go entirely. Simplest to build, and a forgotten takedown becomes physically impossible
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.
Rule of thumb
Disposable data you won't miss: set a lifespan and let it delete. Data you'll want to reference or write a notice about: use the flag and unpublish. Draw this line before you build and the implementation won't drift halfway through.
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.
Cron-Based Expiration and Access Control
How scheduled jobs find expired content and unpublish it, and how access after expiry is handled.
Use Cases for Disposable URLs
Where this mechanism earns its keep, from campaign pages to private event photo sharing.
Auto-Expiring Content — Designing Away "Forgot to Take It Down"
The hub article covering the big picture and the thinking behind the design.