Free tools Windows power users keep installed
One-click scans. No signup required.
Build the API around a clear division of responsibility: PostgreSQL is the durable source of truth, Prisma handles database access, NestJS validates requests, and Redis temporarily caches active messages with a matching time-to-live. This example uses reusable links that expire at a fixed time; one-time retrieval is a separate product rule, not an automatic property of temporary messages.
Choose the expiration and storage rules first
“Temporary” needs a precise meaning before it becomes code. This design uses an absolute expiration time: a message remains retrievable through its link until that time, and reading it does not extend its lifetime or consume it. PostgreSQL is canonical; Redis is a cache, not the only copy of a message.
As an Amazon Associate I earn from qualifying purchases.
| Decision | Choice in this design | What changes if you choose differently |
|---|---|---|
| Expiration | Fixed time stored as expiresAt |
Sliding expiration requires extending the timestamp and cache TTL on access. |
| Read behavior | Reusable link until expiration | One-time retrieval needs atomic claim-and-delete behavior; a normal read is not sufficient. |
| Storage authority | PostgreSQL | Redis loss or eviction can cause cache misses, but PostgreSQL can repopulate the cache. |
| Expired data | Not served after its expiration time; physical deletion is a separate cleanup concern | A deletion guarantee across databases, logs, and backups requires additional controls and cannot be inferred from a Redis TTL. |
Choose the maximum message size and allowed lifetime as product limits. The NestJS, Prisma, and Redis documentation does not prescribe suitable values for this API.
Recommended Free Tools
Define the API contract
A small API can expose one creation route and one retrieval route. The status codes below are proposed contract choices; keep them consistent with the client rather than treating them as framework defaults.
#1 Best Overall
| Operation | Example route | Request or result |
|---|---|---|
| Create | POST /messages |
Accept message content and an allowed lifetime. Return a generated opaque link identifier and the resulting expiration time. |
| Retrieve | GET /messages/:token |
Return content only while the link is active. Return the same not-found response for unknown and expired links if you do not want to disclose which tokens were once valid. |
Do not accept a client-supplied expiration timestamp without checking it against the product’s permitted duration. Validate both the body and the route token before persistence or cache access.
Validate at the NestJS boundary
NestJS recommends validating every piece of data an application receives before acting on it. Use class-based DTOs with class-validator rules and a ValidationPipe, or use a schema compatible with Standard Schema and NestJS’s StandardSchemaValidationPipe. For path values with a defined format, use a suitable parameter pipe such as ParseUUIDPipe only if the identifier is actually a UUID.
- Define DTOs. Require content to be a non-empty string and validate the requested lifetime against the API’s configured minimum and maximum. Set a content-size ceiling deliberately; no supported ceiling is established by the framework documentation.
- Register validation. Apply the pipe at the controller or register it globally with
app.useGlobalPipes(...). Keep runtime DTOs as classes when using class-based validation: TypeScript interfaces and generics do not retain the metadata thatValidationPipeneeds. - Reject invalid input early. Do not call Prisma or Redis for a body or token that fails validation. Validation does not provide rate limiting, abuse prevention, or authorization.
Model canonical data in PostgreSQL
Store the content, opaque link token, creation time, and absolute expiration time in PostgreSQL. A Prisma model can follow this shape; confirm syntax and generated-client behavior against the Prisma ORM major version pinned for your project.
The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Rank #2
model Message {
id String @id @default(cuid())
token String @unique
content String @db.Text
createdAt DateTime @default(now())
expiresAt DateTime
@@index([expiresAt])
}
The unique token constraint prevents two records from sharing a link identifier, while the expiration index can support cleanup queries. A token must be generated by the server, not chosen by the message author. Select and review a cryptographically strong token-generation approach for your deployment; the cited framework and database documentation does not establish an entropy value or complete token-security policy.
Configure Prisma for PostgreSQL using the postgresql provider and a connection string appropriate to the environment. In a serverless PostgreSQL deployment, Prisma documents using a pooled runtime URL and a direct URL for CLI operations. Do not copy connection or client setup from a different Prisma major version without checking its matching NestJS integration and connector documentation.
Create a message as one database operation
- Validate content and the requested lifetime.
- Generate the opaque token on the server and calculate
expiresAtfrom the chosen policy. - Insert the message through Prisma and return the link information only after the database write succeeds.
- Populate Redis after the PostgreSQL commit, with a TTL no longer than the remaining time to expiration.
If creation also writes related PostgreSQL records that must succeed or fail together, use the transaction API supported by your pinned Prisma version. A database transaction can make those database writes atomic; it does not make a PostgreSQL write and a Redis write one atomic operation. If Redis is unavailable after the database insert, retain the valid PostgreSQL record and treat cache population as a recoverable failure rather than pretending the two stores committed together.
Rank #3
Retrieve only unexpired messages
Enforce expiration in the application as well as in Redis. The authoritative check is that the stored expiration time is later than the current time when the request is served. Redis TTL helps remove cached values, but it is not a substitute for the product’s retrieval rule.
- Validate the route token.
- Look up the message in Redis. If a cache entry is present, check its embedded expiration time before returning it; do not serve it if it has expired.
- On a cache miss, query PostgreSQL for the token and require
expiresAtto be later than the current time. - If there is no active PostgreSQL record, return the API’s not-found response. Otherwise, cache the value for no longer than its remaining lifetime and return the message.
For stricter consistency or immediate revocation, verify the database on every read or define a reliable invalidation mechanism. A cache hit can otherwise outlive a change made only in PostgreSQL. Do not claim immediate cross-store deletion unless the implementation enforces and tests it.
Set Redis TTL without accidentally making data permanent
Redis supports EXPIRE key seconds, expiration options on SET, and TTL key to inspect remaining lifetime in seconds. For a cached message, compute a positive TTL from the remaining expiration interval and write the key with its expiry in the same operation where possible.
TTLreturning-1means the key exists without an expiry; treat this as a cache correctness defect for a temporary message.TTLreturning-2means the key does not exist; load it from PostgreSQL if it remains active.- A plain
SETthat overwrites a key clears its prior expiry. Include an expiration option when rewriting, or useKEEPTTLwhen preserving an existing TTL is the intended behavior.
Use a predictable key namespace, such as a message-specific prefix plus the token, and avoid placing message content in operational logs. Redis expiration automatically destroys an expired key, but it says nothing by itself about the PostgreSQL row, database backups, logs, or other copies.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Plan cleanup and failure behavior
Expiration at read time is what prevents late retrieval; a background cleanup job is for reclaiming expired PostgreSQL rows, not for making the expiration promise timely. Define a retention and cleanup policy for expired rows, and document what happens if the cleanup job is delayed. Redis may remove its key independently, so an absent cache entry is normal and must not be mistaken for message deletion from the database.
- Redis unavailable: If PostgreSQL is healthy, bypass or defer cache operations according to your availability policy. Do not make Redis a hidden single point of failure if PostgreSQL is the declared source of truth.
- PostgreSQL unavailable: A Redis cache may still contain a value, but serving it means accepting potentially stale state. Decide explicitly whether the API fails closed or permits bounded cache-only reads.
- Cache and database disagree: Define whether PostgreSQL overrides the cache and how stale keys are invalidated. A transaction in Prisma does not coordinate Redis.
- Expiration has passed: Do not serve the content even if an entry is still present due to timing or configuration error; return the documented not-found result.
Address security and privacy separately
An expiring link is not automatically confidential or secure. Before launch, decide how links resist guessing, whether requests need throttling, which content and identifiers may appear in logs, how abuse is reported, and whether encryption or access controls are required. The cited NestJS, Prisma, and Redis material establishes neither specific token entropy nor these broader security requirements.
Best Value
Make the user-facing deletion statement match the implementation. A Redis TTL proves only that Redis is configured to expire a key; it does not prove removal from PostgreSQL, backups, monitoring systems, or logs. If the product promises deletion across those systems, implement and verify each path before making that promise.
Pin versions and deploy deliberately
Record the NestJS, Prisma ORM, PostgreSQL, and Redis versions used by the project. Prisma commands, generated APIs, and transaction syntax can vary by major version, so follow the matching Prisma NestJS and PostgreSQL connector documentation instead of combining examples from different releases. For hosted PostgreSQL, choose connection pooling and backup behavior as deployment settings; use a direct connection for CLI work where the selected serverless setup calls for one. Treat Redis as an independently operated service with its own availability and persistence characteristics, rather than assuming its TTL configures PostgreSQL retention.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Fix the driver behind crashes, sound loss and screen glitches3Repair Windows errors before they cause bigger problems




