Resource()
Defines a custom resource provider that handles the complete lifecycle (create, update, delete) of a cloud resource. Resources are the building blocks of Alchemy applications.
Signature
function Resource<Type extends string, Handler extends ResourceLifecycleHandler>(
type: Type,
handler: Handler
): ResourceProvider<Type, Handler>
function Resource<Type extends string, Handler extends ResourceLifecycleHandler>(
type: Type,
options: Partial<ProviderOptions>,
handler: Handler
): ResourceProvider<Type, Handler>
Parameters
The unique type identifier for this resource, conventionally in the format "provider::ResourceName" (e.g., "cloudflare::Worker", "aws::S3Bucket").
Optional configuration for the resource provider.If true, the resource will be updated even if the inputs have not changed.
options.destroyStrategy
'sequential' | 'parallel'
default:"'sequential'"
The strategy to use when destroying this resource and its dependencies.
handler
ResourceLifecycleHandler
required
The async function that manages the resource lifecycle. Receives a Context object as this, along with id and props parameters.type ResourceLifecycleHandler = (
this: Context<Output, Props>,
id: string,
props: Props
) => Promise<Output>
Returns
A callable function that creates instances of the resource. When called with an id and props, it returns a Promise that resolves to the resource output.const MyResource = Resource("provider::MyResource", handler);
const instance = await MyResource("my-id", { prop: "value" });
Context API
The resource handler function receives a Context object as this with the following properties and methods:
this.phase
'create' | 'update' | 'delete'
The current lifecycle phase of the resource.
The previous output of the resource (undefined during creation).
The previous props of the resource (undefined during creation).
The current scope containing this resource.
The fully qualified name of the resource.
Terminates the resource lifecycle and marks it for deletion. Used in the delete phase.if (this.phase === "delete") {
await api.deleteResource(this.output.id);
return this.destroy();
}
Signals that the resource should be replaced (deleted and recreated). Used when an immutable property changes.if (this.output.name !== props.name) {
return this.replace(); // Triggers delete → create
}
Retrieves a value from the resource’s internal state storage.
Stores a value in the resource’s internal state storage.
Registers a cleanup function to run when the process exits.const proc = spawn('my-command');
this.onCleanup(async () => {
proc.kill();
await waitForExit(proc);
});
Examples
Basic Resource Provider
import { Resource, Context } from "alchemy";
export interface MyResourceProps {
name?: string;
region: string;
}
export interface MyResource {
id: string;
name: string;
region: string;
resourceId: string;
}
export const MyResource = Resource(
"provider::MyResource",
async function (
this: Context<MyResource>,
id: string,
props: MyResourceProps
): Promise<MyResource> {
const name = props.name ?? this.scope.createPhysicalName(id);
if (this.phase === "delete") {
if (this.output?.resourceId) {
await api.delete(`/resources/${this.output.resourceId}`);
}
return this.destroy();
}
// Create or update the resource
const result = this.output?.resourceId
? await api.put(`/resources/${this.output.resourceId}`, { name, region: props.region })
: await api.post("/resources", { name, region: props.region });
return {
id,
name: result.name,
region: props.region,
resourceId: result.id
};
}
);
Resource with Secret Handling
import { Resource, Context, Secret } from "alchemy";
export interface DatabaseProps {
name?: string;
password: string | Secret;
}
export interface Database {
id: string;
name: string;
connectionString: string;
password: Secret;
}
export const Database = Resource(
"provider::Database",
async function (
this: Context<Database>,
id: string,
props: DatabaseProps
): Promise<Database> {
const name = props.name ?? this.scope.createPhysicalName(id);
if (this.phase === "delete") {
if (this.output?.name) {
await api.deleteDatabase(this.output.name);
}
return this.destroy();
}
// Unwrap secret for API call
const result = await api.createOrUpdateDatabase({
name,
password: Secret.unwrap(props.password)
});
// Wrap secret in output
return {
id,
name: result.name,
connectionString: result.connectionString,
password: Secret.wrap(props.password)
};
}
);
// Usage
const db = await Database("my-db", {
password: alchemy.secret(process.env.DB_PASSWORD)
});
Resource with Adoption
export const MyResource = Resource(
"provider::MyResource",
async function (
this: Context<MyResource>,
id: string,
props: MyResourceProps & { adopt?: boolean }
): Promise<MyResource> {
const name = props.name ?? this.scope.createPhysicalName(id);
const adopt = props.adopt ?? this.scope.adopt;
if (this.phase === "delete") {
if (this.output?.resourceId) {
await api.delete(`/resources/${this.output.resourceId}`);
}
return this.destroy();
}
let result;
if (this.output?.resourceId) {
// Update existing resource
result = await api.put(`/resources/${this.output.resourceId}`, { name });
} else {
try {
// Try to create new resource
result = await api.post("/resources", { name });
} catch (error) {
if (error.code === "ALREADY_EXISTS" && adopt) {
// Adopt existing resource
const existing = await api.findByName(name);
result = await api.put(`/resources/${existing.id}`, { name });
} else {
throw error;
}
}
}
return {
id,
name: result.name,
resourceId: result.id
};
}
);
Resource with Immutable Properties
export const ImmutableResource = Resource(
"provider::ImmutableResource",
async function (
this: Context<ImmutableResource>,
id: string,
props: ImmutableResourceProps
): Promise<ImmutableResource> {
const name = props.name ?? this.scope.createPhysicalName(id);
if (this.phase === "delete") {
if (this.output?.resourceId) {
await api.delete(`/resources/${this.output.resourceId}`);
}
return this.destroy();
}
// Check if immutable property changed
if (this.phase === "update" && this.output.region !== props.region) {
return this.replace(); // Triggers delete → create
}
const result = this.output?.resourceId
? await api.put(`/resources/${this.output.resourceId}`, { name })
: await api.post("/resources", { name, region: props.region });
return {
id,
name: result.name,
region: props.region,
resourceId: result.id
};
}
);