Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsCommunityBlog
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

Back to skills

Redis Resource

ASecurity

How to use @owlmeans/redis-resource — Redis-backed Resource implementation for caching/state with TTL, pub/sub, keyspace watching and streams. Auto-invoked when defining a resource backed by Redis.

3 stars
0 votes
0 copies
0 views
Added 9/22/2026
databasestypescriptrustgosqldatabase

Works with

cli

Security Analysis

A100/100

Scanned 9/22/2026

Install to Claude Code

$npx -y skills add owlmeans/common --skill redis-resource --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Redis Resource?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for Redis Resource
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/owlmeans-redis-resource/badge)](https://www.skillsdirectory.com/skills/owlmeans-redis-resource)

More formats (shields.io, HTML) on the badges page.

Download Zip
Files
SKILL.md
---
name: redis-resource
description: How to use @owlmeans/redis-resource — Redis-backed Resource implementation for caching/state with TTL, pub/sub, keyspace watching and streams. Auto-invoked when defining a resource backed by Redis.
user-invocable: false
---

# @owlmeans/redis-resource

**Layer:** Infra
**Install:** `"@owlmeans/redis-resource": "^0.1.18-rc.29"` in `dependencies`

## Key Exports

| Export | Description |
|--------|-------------|
| `makeRedisResource<R>(alias, dbAlias?, serviceAlias?)` | Factory for a Redis-backed `Resource<R>`. Both alias defaults are `DEFAULT_DB_ALIAS` (`'redis'`) |
| `RedisResource<R>` | `Resource<R>` plus the `PubSubResource`, `WatchableResource` and `StreamResource` capabilities, `db`, `name` and `key(id?)` |
| `RedisDb`, `RedisClient`, `RedisDbService`, `RedisConnection` | The db and connection contracts — `@owlmeans/redis` implements them |
| `DEFAULT_DB_ALIAS`, `DEFAULT_PAGE_SIZE`, `SCAN_BATCH`, `READ_BATCH` | Constants for the record half |
| `STREAM_MAX_LENGTH`, `DEFAULT_STREAM_BLOCK`, `RECLAIM_IDLE`, `RECLAIM_COUNT`, `CONSUMER_ID_LENGTH` | Constants for the stream half |

Keys are namespaced by the db config's key prefix plus the resource's name:
`resource.key(id)` → `<prefix>-<name>:<id>`, and `key()` with no argument yields the namespace's
glob. The name is `resource.name` when set and the registration alias otherwise, so two resources
can share one namespace deliberately — and a `name` given by accident quietly merges two id spaces.
Every non-word character in either half is collapsed to `_`.

## Usage

```typescript
import { makeRedisResource } from '@owlmeans/redis-resource'

context.registerResource(makeRedisResource<SessionData>('sessions'))

// Type the RESOURCE once — never a method
const sessions = context.resource<RedisResource<SessionData>>('sessions')
await sessions.create({ id, token }, { ttl: 3600 })   // seconds, or a Date
const live = await sessions.load({ token })           // criteria, not just an id
```

TTL is a **per-call option** on `create` / `update` / `save`, not a property of the factory. The
prefix comes from the `dbs[]` config entry (`schema`), not from the resource.

## Reads: one key is cheap, everything else is a walk

`load(id)`, `get(id)`, `delete(id)`, `take(id)` are single-key commands.

`load(where)`, `get(where)`, `list`, `count` and `purge` walk the resource's **own namespace** with
`SCAN` + `MGET` and evaluate the criteria in memory with `firstMatch` / `filterRecords` /
`applyQuery` from `@owlmeans/resource` — the same engine every other store without a query planner
uses, so a criteria object means here exactly what it means in SQL. That is O(N) over the
namespace, and `{ sort }` orders the records after the walk, not in redis.

- Use it for a small namespaced set — a session cache, an OTP store, an adapter store.
- Never for an unbounded keyspace or a hot path. A resource that needs real queries belongs in
  mongo or postgres.
- `SCAN`, never `KEYS`: `KEYS` blocks the server for the whole sweep.
- The walk reaches one server, so `list` / `count` / `purge` are **not cluster-safe**. In a
  clustered deployment stay on the by-id operations.
- `load({ id })` is recognised and answered by key — the walk only starts for a real criteria.
- A key in the namespace holding something other than a string — this resource's own streams
  included — reads back as `null` from `MGET` and is skipped, so a stream never shows up as a
  record.

## Paging

Redis is **UNPAGED**: `DEFAULT_PAGE_SIZE` is `0`, `list(where)` with no `size` returns every match,
and `ListResult.total` always counts every match. `list(where, { page })` without a `size` throws
`UnsupportedArgumentError('page-without-size')` — there is no implied default to take a window of.

## TTL

| `opts.ttl` | Meaning | Command |
|---|---|---|
| `number` | seconds from now | `EXPIRE` |
| `Date` | the absolute instant to expire at | `PEXPIREAT` (milliseconds — what `Date.getTime()` gives) |
| omitted | persistent (`PTTL` reports `-1`) | — |

A `Date` already in the past drops the record immediately. `update` and `save` rewrite the key, so
a renewal must pass a TTL again — otherwise the record becomes persistent.

## Rules

- `delete(id)` returns the removed record or `null`; `take(id)` is the same read but **throws**
  `UnknownRecordError` when the key is gone. Both use `GETDEL`, so two callers can never be handed
  the same record. Choose by whether absence is an error.
- `create` generates an id when the record carries none, and refuses an id that is already there
  (`RecordExists`) using `SET NX` — no read-then-write race. The id is written into the stored
  record, so what comes back always carries one.
- `update` **replaces** the whole record and throws `UnknownRecordError` for an unknown id (and
  `MisshapedRecord` when the record carries no id at all). It does not merge over what was stored;
  pass every field that must survive.
- `save` creates when the record carries no id and replaces otherwise — that is the upsert.
- `purge(where)` refuses an empty criteria object. Use it instead of a page loop that deletes each
  item.
- `publish(value, channel?)` / `subscribe(handler, { channel?, once?, ttl? })` are channel pub/sub;
  the channel name is namespaced like a key, and an omitted channel listens to the resource's whole
  namespace. `watch(id, handler, { once?, ttl? })` is the keyspace-notification form for ONE
  record — the handler receives the current value on every write, and `null` once the key is gone.
  They are separate methods; a channel is never passed as a bare string.
- `watch` subscribes to `__keyspace@0__:`, while redis publishes keyspace events per database as
  `__keyspace@<db>__:`. So a resource on a connection whose `meta.dbIndex` is anything but 0 gets
  no notifications at all and its `watch` handler silently never fires — deployments sharing one
  instance isolate on the key prefix, not on the db index. The server also needs
  `notify-keyspace-events` enabled; it emits nothing otherwise.
- `stream(key, value)` appends to a namespaced redis stream, trimmed approximately to
  `STREAM_MAX_LENGTH` so trimming stays cheap. `consume(key, { group?, consumer?, block? })` is the
  async generator that reads it — with no `group` it tails from now with `XREAD`, with one it joins
  the consumer group and reclaims stale entries under the **passed** consumer name. Claiming them
  for a fixed name leaves them owned by a consumer that never reads.
- Requires redis 6.2 or newer (`GETDEL`).
- Assert an expiry by reading `PTTL` back from redis, never by trusting that a command was issued.
  An expiry set in the wrong unit is still an expiry — just one tens of millennia out — so only the
  value redis reports back proves anything.

## Tests

Integration specs live in the `@owlmeans/redis` package's own `tests/` — the db package that backs
this contract; putting them here would need a devDependency on this package's own dependent.
Gated on `REDIS_URL` (see `/.env.example`); each suite namespaces its keys and flushes them, and a
spec that walks the namespace boots under its own resource alias so it never sees another test's
records.

## Depends On

- `@owlmeans/resource` · `@owlmeans/context` · `@owlmeans/server-context` · `@owlmeans/basic-ids`
- peer `ioredis` (the client types every method is written against), `ajv`

`@owlmeans/redis` is NOT a dependency — it depends on this package, and that is the direction the
connection contracts flow in.

## Related

- `redis` — the connection service implementing `RedisDbService`, and the `cfg.dbs` entry a
  resource takes its prefix and db index from
- `redis-queue` — BullMQ queues on the same connections
- `resource` — the criteria language, paging and the error classes raised here

Attribution

owlmeansowlmeans
View sourceMore from owlmeans →
SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Related Skills

Mysql Best Practices

MySQL development best practices for schema design, query optimization, and database administration

2481 votes

Jpa Patterns

Spring Boot中的JPA/Hibernate实体设计、关系、查询优化、事务、审计、索引、分页和连接池模式。

2456590 votes

Clickhouse Io

ClickHouse数据库模式、查询优化、分析和数据工程最佳实践,适用于高性能分析工作负载。

2456590 votes

Postgres Patterns

基于Supabase最佳实践的PostgreSQL数据库模式,用于查询优化、架构设计、索引和安全。

2456590 votes

Postgresql

Design a PostgreSQL-specific schema. Covers best-practices, data types, indexing, constraints, performance patterns, and advanced features

458250 votes
View all in databases →