Context
The Context type provides type-safe access to resource state, lifecycle phase, and scope utilities within resource lifecycle handlers. It is automatically provided as the this binding in Resource handlers.
Type Definition
type Context<Output, Props = ResourceProps> =
| CreateContext<Output>
| UpdateContext<Output, Props>
| DeleteContext<Output, Props>
The Context type is a discriminated union based on the phase property:
- CreateContext: Used when creating a new resource (
phase: "create")
- UpdateContext: Used when updating an existing resource (
phase: "update")
- DeleteContext: Used when deleting a resource (
phase: "delete")
Phase-Specific Contexts
CreateContext
Used during resource creation. The output and props are undefined.
interface CreateContext<Output> extends BaseContext<Output> {
phase: "create";
output?: undefined;
props?: undefined;
}
UpdateContext
Used during resource updates. Provides access to previous output and props.
interface UpdateContext<Output, Props> extends BaseContext<Output> {
phase: "update";
output: Output; // Previous resource output
props: Props; // Previous resource props
}
DeleteContext
Used during resource deletion. Provides access to the resource being deleted.
interface DeleteContext<Output, Props> extends BaseContext<Output> {
phase: "delete";
output: Output; // Resource to delete
props: Props; // Props used to create the resource
}
Properties
phase
'create' | 'update' | 'delete'
The current lifecycle phase of the resource.
The previous output of the resource. Only available in update and delete phases.
The previous props of the resource. Only available in update and delete phases.
The resource identifier provided when creating the resource.
The fully qualified name of the resource (e.g., "my-app/dev/my-resource").
The scope containing this resource. Provides access to scope properties and methods.
The current stage name (e.g., "dev", "prod").
Whether logging is suppressed.
Whether this resource is being created as a replacement for another resource.
Methods
destroy()
Terminates the resource lifecycle handler and marks the resource for deletion.
destroy(retainChildren?: boolean): never
Whether to retain child resources when destroying this resource.
Returns: Never returns (throws DestroyedSignal)
Example:
if (this.phase === "delete") {
if (this.output?.resourceId) {
await api.delete(`/resources/${this.output.resourceId}`);
}
return this.destroy();
}
replace()
Signals that the resource should be replaced (deleted then recreated). Used when an immutable property changes.
replace(force?: boolean): never
Force replacement even if not necessary.
Returns: Never returns (throws ReplacedSignal)
Example:
// Check if immutable property changed
if (this.phase === "update" && this.output.region !== props.region) {
return this.replace(); // Triggers delete → create
}
get()
Retrieves a value from the resource’s internal state storage.
get<T>(key: string): Promise<T | undefined>
Returns: The stored value, or undefined if not found.
Example:
const metadata = await this.get<{ version: string }>("metadata");
set()
Stores a value in the resource’s internal state storage.
set<T>(key: string, value: T): Promise<void>
Example:
await this.set("metadata", { version: "1.0.0", deployedAt: Date.now() });
delete()
Deletes a value from the resource’s internal state storage.
delete<T>(key: string): Promise<T | undefined>
Returns: The deleted value, or undefined if not found.
Example:
const oldValue = await this.delete("temporary-data");
onCleanup()
Registers a cleanup function to run when the process exits.
onCleanup(fn: () => void | Promise<void>): void
The cleanup function to register.
Example:
const proc = spawn('my-command', ['arg1', 'arg2']);
this.onCleanup(async () => {
proc.kill();
await waitForExit(proc);
});
create()
Creates the resource output with Alchemy-managed properties.
create(props: Omit<Output, keyof Resource>): Output
props
Omit<Output, keyof Resource>
required
The resource-specific properties (excludes internal Alchemy properties).
Returns: The complete resource output with internal properties added.
Example:
return this.create({
name: result.name,
url: result.url,
resourceId: result.id
});
Examples
Phase-Based Logic
import { Resource, Context } from "alchemy";
export const MyResource = Resource(
"provider::MyResource",
async function (
this: Context<MyResource>,
id: string,
props: MyResourceProps
): Promise<MyResource> {
switch (this.phase) {
case "create":
// this.output is undefined
// this.props is undefined
const result = await api.create(props);
return { id, ...result };
case "update":
// this.output contains previous output
// this.props contains previous props
const updated = await api.update(this.output.resourceId, props);
return { id, ...updated };
case "delete":
// this.output contains the resource being deleted
await api.delete(this.output.resourceId);
return this.destroy();
}
}
);
Using Context Methods
export const MyResource = Resource(
"provider::MyResource",
async function (
this: Context<MyResource>,
id: string,
props: MyResourceProps
): Promise<MyResource> {
if (this.phase === "delete") {
await api.delete(this.output.resourceId);
return this.destroy();
}
// Store metadata in resource state
await this.set("lastModified", Date.now());
await this.set("version", props.version);
// Retrieve metadata
const lastModified = await this.get<number>("lastModified");
// Register cleanup
const watcher = startWatcher();
this.onCleanup(async () => {
await watcher.stop();
});
const result = await api.createOrUpdate(props);
return { id, ...result };
}
);
Handling Immutable Properties
export const ImmutableResource = Resource(
"provider::ImmutableResource",
async function (
this: Context<ImmutableResource>,
id: string,
props: ImmutableResourceProps
): Promise<ImmutableResource> {
if (this.phase === "delete") {
await api.delete(this.output.resourceId);
return this.destroy();
}
// Check if immutable property changed during update
if (this.phase === "update") {
if (this.output.region !== props.region) {
// Signal that resource should be replaced
return this.replace();
}
}
const result = this.output?.resourceId
? await api.update(this.output.resourceId, props)
: await api.create(props);
return { id, region: props.region, ...result };
}
);
Accessing Scope Properties
export const ScopedResource = Resource(
"provider::ScopedResource",
async function (
this: Context<ScopedResource>,
id: string,
props: ScopedResourceProps
): Promise<ScopedResource> {
// Access scope properties via this.scope
const name = props.name ?? this.scope.createPhysicalName(id);
const isLocal = this.scope.local;
const stage = this.stage;
if (isLocal) {
// Return mock data for local development
return {
id,
name,
url: `http://localhost:3000/${id}`,
stage
};
}
if (this.phase === "delete") {
await api.delete(this.output.name);
return this.destroy();
}
const result = await api.deploy({ name, stage });
return { id, ...result };
}
);
Type-Safe Phase Handling
export const TypeSafeResource = Resource(
"provider::TypeSafeResource",
async function (
this: Context<TypeSafeResource>,
id: string,
props: TypeSafeResourceProps
): Promise<TypeSafeResource> {
if (this.phase === "delete") {
// TypeScript knows this.output is defined
console.log(`Deleting ${this.output.name}`);
await api.delete(this.output.resourceId);
return this.destroy();
}
if (this.phase === "update") {
// TypeScript knows both this.output and this.props are defined
console.log(`Updating ${this.output.name}`);
console.log(`Previous version: ${this.props.version}`);
console.log(`New version: ${props.version}`);
}
if (this.phase === "create") {
// TypeScript knows this.output and this.props are undefined
console.log(`Creating new resource`);
}
const result = await api.createOrUpdate(props);
return { id, ...result };
}
);