On this pageOverview
Resources
Commands are self-contained by default. Each execution starts fresh with no shared state. But some browser APIs like AudioContext, RTCPeerConnection, or CanvasRenderingContext2D need a single long-lived instance shared across commands. That’s what resources is for.
Think of it like a restaurant kitchen
Resources are kitchen equipment: the oven, the stand mixer, the deep fryer. They’re turned on when the kitchen opens and run all night. Every dish (Command) can use them. You don’t buy a new oven per order. AudioContext and CanvasRenderingContext2D are the same: expensive singletons that live for the entire app lifecycle. Need multiple pieces of equipment? Combine them with Layer.mergeAll.
Define a service using Effect.Service, then pass its default layer to makeProgram via the resources config field. The runtime creates the layer once and makes it available to every Command. Commands access it by yielding the service tag.
import { Effect } from 'effect'
import { Command, Runtime } from 'foldkit'
class AudioContextService extends Effect.Service<AudioContextService>()(
'AudioContextService',
{ sync: () => new AudioContext() },
) {}
const PlayNote = Command.define('PlayNote', CompletedPlayNote)
const playNote = (frequency: number, duration: number) =>
PlayNote(
Effect.gen(function* () {
const audioContext = yield* AudioContextService
const oscillator = audioContext.createOscillator()
oscillator.frequency.setValueAtTime(frequency, audioContext.currentTime)
oscillator.connect(audioContext.destination)
oscillator.start()
oscillator.stop(audioContext.currentTime + duration)
return CompletedPlayNote()
}),
)
// 3. Pass the service's default layer to makeProgram
const program = Runtime.makeProgram({
Model,
init,
update,
view,
container: document.getElementById('root')!,
resources: AudioContextService.Default,
})Commands declare their resource requirements in the type signature via the third type parameter of Command. This makes dependencies explicit and type-checked. If a command requires a service that isn’t provided via resources, you’ll get a compile error.
When not to use resources
Resources are for mutable browser singletons with lifecycle: things that must be created once and reused. Stateless services like HttpClient or BrowserKeyValueStore should be provided per-command with Effect.provide instead.
The resources field takes a single Layer, but Effect layers compose. Use Layer.mergeAll to combine multiple service layers into one.
import { Layer } from 'effect'
import { Runtime } from 'foldkit'
const program = Runtime.makeProgram({
Model,
init,
update,
view,
container: document.getElementById('root')!,
resources: Layer.mergeAll(
AudioContextService.Default,
WebRTCService.Default,
CanvasService.Default,
),
})Resources live for the entire application. But what if a resource should only exist while the model is in a certain state, like a camera stream during a video call, or a WebSocket while on a chat page? That’s what Managed Resources are for.