Getting started with Workers
Compose Worker-compatible Playstack crypto and feed primitives with Web platform APIs and explicit environment bindings.
This guide demonstrates two edge-safe boundaries: a reusable cryptography service configured from Worker bindings, and a streaming RSS response returned directly from fetch.
1. Install portable packages
Before you start: use an existing Worker project with its generated Env type. These composition examples assume you supply encryption/signing key bindings, a content binding, feedDefinition and streamPublishedEntries. See Crypto for key configuration and Feeds for content shapes. They do not provision infrastructure or secrets. For a complete local first program, use the storage quickstart, then return here for edge deployment.
npm install @playstack/core @playstack/crypto @playstack/feedsThese packages use Web platform primitives. Keep them on the same fixed Playstack version, confirm package access, and run the relevant Worker tests for your selected version. Portable APIs alone do not establish deployment compatibility.
2. Configure Web Crypto explicitly
Convert environment bindings into package configuration in the application entry. Do not let the package read ambient environment variables:
import { systemClock } from '@playstack/core'
import { createCrypto } from '@playstack/crypto'
function cryptoFor(env: Env) {
return createCrypto({
crypto: globalThis.crypto,
clock: systemClock(),
encryptionKeys: JSON.parse(env.PLAYSTACK_ENCRYPTION_KEYS),
signingKeys: JSON.parse(env.PLAYSTACK_SIGNING_KEYS),
})
}Each keyset must contain exactly one active key. Retain inactive keys while existing ciphertext or signed tokens may still reference them. Supply an atomic replay store before verifying single-use tokens.
If configuration is deployment-stable, parse it once at module scope or cache the configured service by binding identity. Request-specific actors and correlation metadata belong in operation context, not global service state.
3. Return a native streaming response
@playstack/feeds can stream directly from a Worker without a Next.js or Node.js adapter:
import { feedResponse } from '@playstack/feeds'
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const url = new URL(request.url)
if (url.pathname !== '/feed.xml') {
return new Response('Not found', { status: 404 })
}
return feedResponse(
'rss',
feedDefinition,
streamPublishedEntries(env.CONTENT),
{ content: 'full', cacheControl: 'public, max-age=60' },
)
},
}The returned value is a standard Response, and the body is a standard ReadableStream. For large generated feeds, use the structural R2 storage adapter and publish a committed generation instead of rebuilding the document on every request.
4. Add edge persistence by contract
- Use
@playstack/rate-limit-dowhen rate-limit state needs Durable Object atomicity. - Use the R2 feed storage implementation from
@playstack/feedsfor generated documents. - Provide application adapters for authentication, accounts, audit, and events rather than importing Node-specific Prisma or NestJS bindings.
- Queue observational events after the domain mutation has reached its own durable boundary.
Verify the bundle
- Run the package's Worker compatibility target before deployment.
- Inspect the bundle for accidental Node built-ins and framework adapters.
- Keep raw subject identifiers out of rate-limit storage keys.
- Verify HMACs and webhooks against the exact request bytes.
- Treat
waitUntilas delivery scheduling, not as a substitute for transactional durability.