Temporary Message API: Build Reliable Expiration
Build a temporary message API by assigning every message an explicit expiration time, checking that time on every read, and removing expired records with a background cleanup job. Treat cleanup as storage maintenance, not the mechanism that makes a message unavailable: a delayed job must never make an expired message readable.
Define What “Expires” Means
Before choosing a database or writing endpoints, decide what expiration promises to clients. A message may become inaccessible at a fixed deadline, after a time-to-live (TTL), after one successful read, or when its owner deletes it. These behaviors are different and should be represented explicitly rather than hidden in a generic “temporary” flag.
- Fixed deadline: The creator supplies an expiration timestamp, such as 2026-11-05T18:30:00Z.
- Relative TTL: The creator supplies a duration, such as 3,600 seconds; the server calculates the deadline from its current time.
- Read-once: The message is consumed on retrieval. This requires atomic coordination so simultaneous requests cannot both obtain it.
- Manual deletion: The creator or an authorized user can delete a message before its deadline.
Document the exact boundary. A practical rule is “expired when current server time is greater than or equal to expires_at.” Use UTC timestamps and server-side time, not a client’s clock, which may be inaccurate or deliberately changed.
Design the Message Record and API
A minimal record might contain an opaque identifier, the message payload, creation time, expiration time, and any access-control data needed to retrieve it. Add a consumed or deleted state only if the product needs those behaviors. Avoid collecting unrelated personal information: temporary storage does not automatically make stored content private.
Keep the API contract simple. For example, a create endpoint can accept a payload and a TTL, then return an identifier and expiration timestamp. A read endpoint returns the payload only if the caller is authorized and the record has not expired. A delete endpoint can make early deletion explicit. Validate payload size, TTL limits, timestamp format, and required fields on the server.
- Use unpredictable identifiers rather than sequential numbers that expose how many messages exist.
- Return a clear not-found or expired response without revealing whether an inaccessible identifier belonged to another user.
- Do not accept an arbitrary expiration far in the future unless that is an intentional feature.
- Make create and delete behavior safe to retry where possible, since clients may resend requests after network failures.
Whether to use 404 for both missing and expired messages, or a distinct response for expiration, is an API design choice. A uniform response can reduce information disclosure; a distinct response may be easier for clients. Choose deliberately and document it.
Enforce Expiration on Every Read
The read path is the reliability boundary. At the moment a request accesses a record, compare the database’s current time with its expiration value. If the deadline has passed, refuse to return the payload and arrange deletion or mark the record as expired. Never rely on a periodic worker having already run.
For a database query, the condition can be expressed conceptually as “find this identifier where expires_at is later than the database’s current time.” A database-side time comparison avoids disagreements between application servers with slightly different clocks. If the database cannot do that conveniently, synchronize server clocks and use a consistent time source, while recognizing that clock synchronization does not make clocks identical.
For read-once messages, checking and consuming must be one atomic operation. Two requests can otherwise both check “unused” before either marks the record consumed. Use a transaction, conditional update, or atomic delete-and-return operation supported by the chosen datastore. Decide what happens if the response fails after consumption: guaranteeing that a client receives content exactly once across network failures is not generally possible without more protocol design.
Clean Up Without Depending on It
Expired records still consume storage until removed, so run a cleanup process. Options include a database’s native TTL feature, a scheduled worker that deletes batches, or a queue that schedules deletion. Native TTL mechanisms are convenient, but many perform deletion asynchronously; they should not be assumed to remove a record at the exact deadline.
A batch worker can select records whose expiration is at or before the current time, delete a bounded number, and repeat. Index the expiration field if the database supports the access pattern, and avoid one enormous delete that creates excessive load. Make the job idempotent: rerunning it should be harmless. Multiple workers should coordinate through safe claims, partitions, or database operations rather than assuming only one process will ever run.
Set a cleanup interval based on storage needs and expected load, then monitor the oldest expired record still present, deletion failures, backlog size, and job duration. These measurements reveal whether cleanup is falling behind. The service should still reject expired messages during a cleanup outage.
Security, Privacy, and Failure Cases
A short retention period is not the same as end-to-end secrecy or guaranteed erasure. Message contents may also appear in application logs, traces, backups, database replicas, browser storage, or error reports. Keep payloads out of routine logs, restrict staff and service access, encrypt traffic, and review how backups and replicas handle deleted data. If the product promises deletion from backups, verify that the storage and backup design can actually meet that promise.
Protect retrieval with authorization appropriate to the use case. An unguessable link can reduce casual discovery, but it is a bearer secret: anyone who obtains it may be able to read the message. Avoid putting sensitive tokens in analytics or referrer data, and consider rate limits and abuse controls. For highly sensitive content, decide whether storing plaintext is acceptable; application-level encryption changes key management and recovery requirements and is not a substitute for controlling access.
Test the awkward cases, not only a successful create-and-read flow: a request arriving exactly at the deadline, cleanup running late, duplicate requests, two concurrent read-once requests, a worker restart, and a database timeout. Define whether a failed delete is retried and how clients learn that a message is unavailable. Clear behavior is more dependable than promising that a background task will always run on schedule.
Choose a Storage and Expiration Strategy
The right design depends on volume, consistency needs, and retention expectations. A relational database is useful when messages need transactions, access rules, or relationships to users. A key-value store can suit simple identifier-to-payload retrieval and may offer native TTL support. In either case, verify the actual expiration semantics, limits, backup behavior, and atomic operations in the product’s documentation rather than assuming all TTL features behave alike.
- Small service: Start with a durable database, an expiration field, read-time enforcement, and a modest scheduled cleanup job.
- High-volume ephemeral traffic: Consider a datastore designed for short-lived keys, but retain read-time checks if exact availability at expiry matters.
- Strict access requirements: Add authentication or scoped access tokens, audit access without logging payloads, and review retention in backups and replicas.
Do not introduce a message queue just to delete a modest number of records. Scheduled scans are often simpler. A queue becomes useful when per-message scheduling or large-scale deletion justifies its additional delivery, retry, and duplicate-processing concerns.
Frequently Asked Questions
Can a database TTL guarantee deletion at the exact expiration second?
Usually, TTL features are asynchronous cleanup mechanisms. Check the selected database’s documented behavior, and enforce expiry in the read query if an expired message must never be served.
Should the client or server set the expiration time?
The client can request a TTL or deadline, but the server should validate it and establish the authoritative expiration value. Use server-side or database time when evaluating expiry.
What happens if the cleanup job is down?
Expired messages should remain inaccessible because reads check the deadline independently. The job’s recovery task is to reclaim storage after it resumes.
Is a one-time link the same as a read-once message?
No. A link may be opened more than once unless the server atomically consumes the message. Also decide whether failed requests consume it and how concurrent requests are handled.
Does expiration erase a message from backups?
Not necessarily. Database deletion, replica cleanup, and backup retention are separate processes. Make accurate retention promises only after checking each storage layer.
Related
Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.