Redis Client
Factory that wraps ioredis with lifecycle management. All Redis extensions accept this client.
// src/lib/server/redis.js
import { createRedisClient } from 'svelte-adapter-uws-extensions/redis';
export const redis = createRedisClient({
url: 'redis://localhost:6379',
keyPrefix: 'myapp:' // optional, prefixes all keys
}); Options
| Option | Default | Description |
|---|---|---|
url | 'redis://localhost:6379' | Redis connection URL |
keyPrefix | '' | Prefix for all keys |
autoShutdown | true | Disconnect on sveltekit:shutdown |
options | {} | Extra ioredis options |
API
| Method | Description |
|---|---|
redis.redis | The underlying ioredis instance |
redis.key(k) | Returns keyPrefix + k |
redis.duplicate(overrides?) | New connection with same config. Pass ioredis options to override defaults. |
redis.quit() | Gracefully disconnect all connections |
Pub/Sub Bus
Distributes platform.publish() calls across multiple server instances via Redis pub/sub. Each instance publishes locally AND to Redis. Incoming Redis messages are forwarded to the local platform with echo suppression (keyed by a per-process instance ID, so messages originating from the same instance are dropped on receive).
Multiple publish() calls within the same event-loop tick are coalesced into a single Redis pipeline via microtask batching. This means a form action that publishes to three topics results in one pipelined round trip, not three independent commands.
When to use over the built-in plugin: The core adapter’s platform.publish() only reaches clients connected to the current process. If you run two or more instances behind a load balancer, clients on instance A will not see messages published on instance B. The pub/sub bus fixes that.
Setup
// src/lib/server/bus.js
import { redis } from './redis.js';
import { createPubSubBus } from 'svelte-adapter-uws-extensions/redis/pubsub';
export const bus = createPubSubBus(redis); Usage
The recommended wiring is bus.hooks.open - a ready-made hook that does both the subscriber activation and the per-connection systemChannel subscribe in one step:
// src/hooks.ws.js
import { bus } from '$lib/server/bus';
import { createMessage } from 'svelte-realtime/server';
export const { open } = bus.hooks;
export const message = createMessage({
platform: (platform) => bus.wrap(platform)
}); bus.hooks.open puts every connection in the systemChannel subscriber set via platform.subscribe(ws, '__realtime') (the platform-trust path, which intentionally bypasses the wire-level __-prefix gate) AND calls bus.activate(platform) on first invocation. This is the one-line wiring; the previous two-line pattern (bus.activate(platform) + manual subscribe) still works but is no longer recommended.
The auto-systemChannel subscribe matters because, since svelte-adapter-uws@0.5, wire-level subscribes to __-prefixed topics are denied. Pre-rename, the client-side store’s wire subscribe to __realtime was the side-effect that populated the subscriber set server-side; after the wire gate, nothing else was putting connections into the systemChannel subscriber set. Without bus.hooks.open, bus-emitted degraded / recovered frames publish into an empty set and $health === 'degraded' is silently dead.
The lower-level pattern, if you need to compose with other open-time work:
// src/hooks.ws.js
import { bus } from '$lib/server/bus';
let distributed;
export function open(ws, { platform }) {
bus.activate(platform); // start subscriber (idempotent)
bus.hooks.open(ws, { platform }); // subscribe ws to systemChannel
distributed = bus.wrap(platform);
}
export function message(ws, { data, platform }) {
const msg = JSON.parse(Buffer.from(data).toString());
distributed.publish('chat', 'message', msg);
} Mirrors the bus.hooks surface already exposed by createShardedBus.
Options
| Option | Default | Description |
|---|---|---|
channel | 'uws:pubsub' | Redis channel name |
maxEnvelopeBytes | 1_048_576 (1 MB) | Inbound envelope size cap. Larger envelopes are rejected before JSON.parse. |
allowSystemTopics | false | Whether to republish __-prefixed topics. The configured systemChannel is always allowlisted. |
systemChannel | '__realtime' | Bus’s own degraded/recovered system topic. Set null or false to disable auto-emission. |
onDegraded / onRecovered | unset | Optional local callbacks for circuit-breaker state changes. |
Upgrading from 0.4.x:
allowSystemTopicsdefaults tofalsein 0.5. If your app legitimately bus-relays user-defined__-prefixed topics, setallowSystemTopics: trueexplicitly. Pre-fix, the bus relayed any topic the wire layer accepted.
API
| Method | Description |
|---|---|
bus.wrap(platform) | Returns a wrapped Platform whose publish / publishBatched route through Redis + local. Also passes through subscribe, unsubscribe, checkSubscribe, sendCoalesced, request, requestId, pressure, onPressure, onPublishRate, maxPayloadLength, bufferedAmount, replay (live getter, so platform.replay = ... set after the wrap is visible to framework auto-routing). |
bus.hooks.open | Ready-made open hook. Calls bus.activate(platform) (idempotent) and subscribes the connection to systemChannel via platform.subscribe. Drop in as export const { open } = bus.hooks. |
bus.activate(platform) | Start the Redis subscriber (idempotent). |
bus.deactivate() | Stop the subscriber. |
bus.publishBatched(messages) | One Redis envelope per call (one PUBLISH for createPubSubBus, one SPUBLISH per shard for createShardedBus). Receivers fan out via platform.publishBatched. |
degraded / recovered events
When createPubSubBus shares a circuit breaker with the rest of the extensions, the bus auto-emits degraded / recovered events on the configured systemChannel. Counters: pubsub_degraded_total, pubsub_recovered_total. Replaces the manually wired breaker.onStateChange to __system topic pattern from 0.4.x docs.
Parse-error counter
Malformed Redis envelopes increment pubsub_parse_errors_total (sharded_pubsub_parse_errors_total for createShardedBus). Pre-0.5 they were silently swallowed.
ConnectionError redaction
When createRedisClient throws on DNS / TLS / malformed-URL, the ConnectionError message redacts the password segment to ***. Error-tracker hooks searching for the raw connection string must update queries; the redacted form is redis://:***@host:port. The helper is exported standalone via svelte-adapter-uws-extensions/sensitive.
See also
- Sharded Pub/Sub Bus - Redis 7+ sharded counterpart with
SPUBLISH/SSUBSCRIBE. - Multi-tenant deployments - the two-namespace problem (keys vs channels) and per-tenant override patterns.
- Authorization model - the identity-blind contract behind every primitive.
Was this page helpful?