INTEGRITY Dokumentace

Použití třídy RpcTarget pro zpracování metadat Durable Object

Při práci s Durable Objects budete potřebovat přístup k názvu, který byl použit při vytvoření Durable Object, a to pomocí idFromName(). Tento název je obvykle smysluplný identifikátor vyjadřující, za co Durable Object odpovídá (například ID uživatele, název místnosti nebo identifikátor prostředku).

Současná implementace má ale jedno omezení: i když můžete vytvořit Durable Object s .idFromName(name), tento název nemůžete uvnitř Durable Objectu přímo získat pomocí this.ctx.id.name.

RpcTarget vzor uvedený níže nabízí řešení tím, že vytvoří komunikační vrstvu, která název automaticky předává s každým voláním metody. Vaše API tak zůstane přehledné a Durable Object má zároveň přístup ke svému vlastnímu názvu.

Podle svých potřeb můžete metadata dočasně uložit do RpcTarget třídu, nebo pro uchování metadat po celou dobu životnosti objektu použijte úložiště Durable Object.

Tento příklad metadata Durable Object neukládá trvale. Ukazuje, jak:

  1. Vytvořte RpcTarget třída
  2. Nastavte metadata Durable Object (v tomto příkladu identifikátor) v RpcTarget třída
  3. Předejte metadata metodě Durable Object
  4. Vyčistěte RpcTarget třídu po použití
import { DurableObject, RpcTarget } from "cloudflare:workers";

//  * Create an RpcDO class that extends RpcTarget
//  * Use this class to set the Durable Object metadata
//  * Pass the metadata in the Durable Object methods
//  * @param mainDo - The main Durable Object class
//  * @param doIdentifier - The identifier of the Durable Object

export class RpcDO extends RpcTarget {
	constructor(
		private mainDo: MyDurableObject,
		private doIdentifier: string,
	) {
		super();
	}

	//  * Pass the user's name to the Durable Object method
	//  * @param userName - The user's name to pass to the Durable Object method

	async computeMessage(userName: string): Promise<string> {
		// Call the Durable Object method and pass the user's name and the Durable Object identifier
		return this.mainDo.computeMessage(userName, this.doIdentifier);
	}

	//  * Call the Durable Object method without using the Durable Object identifier
	//  * @param userName - The user's name to pass to the Durable Object method

	async simpleGreeting(userName: string) {
		return this.mainDo.simpleGreeting(userName);
	}
}

//  * Create a Durable Object class
//  * You can use the RpcDO class to set the Durable Object metadata

export class MyDurableObject extends DurableObject<Env> {
	constructor(ctx: DurableObjectState, env: Env) {
		super(ctx, env);
	}

	//  * Initialize the RpcDO class
	//  * You can set the Durable Object metadata here
	//  * It returns an instance of the RpcDO class
	//  * @param doIdentifier - The identifier of the Durable Object

	async setMetaData(doIdentifier: string) {
		return new RpcDO(this, doIdentifier);
	}

	//  * Function that computes a greeting message using the user's name and DO identifier
	//  * @param userName - The user's name to include in the greeting
	//  * @param doIdentifier - The identifier of the Durable Object

	async computeMessage(
		userName: string,
		doIdentifier: string,
	): Promise<string> {
		console.log({
			userName: userName,
			durableObjectIdentifier: doIdentifier,
		});
		return `Hello, ${userName}! The identifier of this DO is ${doIdentifier}`;
	}

	//  * Function that is not in the RpcTarget
	//  * Not every function has to be in the RpcTarget

	private async notInRpcTarget() {
		return "This is not in the RpcTarget";
	}

	//  * Function that takes the user's name and does not use the Durable Object identifier
	//  * @param userName - The user's name to include in the greeting

	async simpleGreeting(userName: string) {
		// Call the private function that is not in the RpcTarget
		console.log(this.notInRpcTarget());

		return `Hello, ${userName}! This doesn't use the DO identifier.`;
	}
}

export default {
	async fetch(request, env, ctx): Promise<Response> {
		let id: DurableObjectId = env.MY_DURABLE_OBJECT.idFromName(
			new URL(request.url).pathname,
		);
		let stub = env.MY_DURABLE_OBJECT.get(id);

		//  * Set the Durable Object metadata using the RpcTarget
		//  * Notice that no await is needed here

		const rpcTarget = stub.setMetaData(id.name ?? "default");

		// Call the Durable Object method using the RpcTarget.
		// The DO identifier is passed in the RpcTarget
		const greeting = await rpcTarget.computeMessage("world");

		// Call the Durable Object method that does not use the Durable Object identifier
		const simpleGreeting = await rpcTarget.simpleGreeting("world");

		// Clean up the RpcTarget.
		try {
			(await rpcTarget)[Symbol.dispose]?.();
			console.log("RpcTarget cleaned up.");
		} catch (e) {
			console.error({
				message: "RpcTarget could not be cleaned up.",
				error: String(e),
				errorProperties: e,
			});
		}

		return new Response(greeting, { status: 200 });
	},
} satisfies ExportedHandler<Env>;

Tento příklad metadata Durable Object ukládá trvale. Ukazuje podobné kroky jako předchozí příklad, ale k uložení identifikátoru využívá úložiště Durable Object, takže ho není potřeba předávat přes RpcTarget.

import { DurableObject, RpcTarget } from "cloudflare:workers";

//  * Create an RpcDO class that extends RpcTarget
//  * Use this class to set the Durable Object metadata
//  * Pass the metadata in the Durable Object methods
//  * @param mainDo - The main Durable Object class
//  * @param doIdentifier - The identifier of the Durable Object

export class RpcDO extends RpcTarget {
	constructor(
		private mainDo: MyDurableObject,
		private doIdentifier: string,
	) {
		super();
	}

	//  * Pass the user's name to the Durable Object method
	//  * @param userName - The user's name to pass to the Durable Object method

	async computeMessage(userName: string): Promise<string> {
		// Call the Durable Object method and pass the user's name and the Durable Object identifier
		return this.mainDo.computeMessage(userName, this.doIdentifier);
	}

	//  * Call the Durable Object method without using the Durable Object identifier
	//  * @param userName - The user's name to pass to the Durable Object method

	async simpleGreeting(userName: string) {
		return this.mainDo.simpleGreeting(userName);
	}
}

//  * Create a Durable Object class
//  * You can use the RpcDO class to set the Durable Object metadata

export class MyDurableObject extends DurableObject<Env> {
	constructor(ctx: DurableObjectState, env: Env) {
		super(ctx, env);
	}

	//  * Initialize the RpcDO class
	//  * You can set the Durable Object metadata here
	//  * It returns an instance of the RpcDO class
	//  * @param doIdentifier - The identifier of the Durable Object

	async setMetaData(doIdentifier: string) {
		// Use DO storage to store the Durable Object identifier
		await this.ctx.storage.put("doIdentifier", doIdentifier);
		return new RpcDO(this, doIdentifier);
	}

	//  * Function that computes a greeting message using the user's name and DO identifier
	//  * @param userName - The user's name to include in the greeting

	async computeMessage(userName: string): Promise<string> {
		// Get the DO identifier from storage
		const doIdentifier = await this.ctx.storage.get("doIdentifier");
		console.log({
			userName: userName,
			durableObjectIdentifier: doIdentifier,
		});
		return `Hello, ${userName}! The identifier of this DO is ${doIdentifier}`;
	}

	//  * Function that is not in the RpcTarget
	//  * Not every function has to be in the RpcTarget

	private async notInRpcTarget() {
		return "This is not in the RpcTarget";
	}

	//  * Function that takes the user's name and does not use the Durable Object identifier
	//  * @param userName - The user's name to include in the greeting

	async simpleGreeting(userName: string) {
		// Call the private function that is not in the RpcTarget
		console.log(this.notInRpcTarget());

		return `Hello, ${userName}! This doesn't use the DO identifier.`;
	}
}

export default {
	async fetch(request, env, ctx): Promise<Response> {
		let id: DurableObjectId = env.MY_DURABLE_OBJECT.idFromName(
			new URL(request.url).pathname,
		);
		let stub = env.MY_DURABLE_OBJECT.get(id);

		//  * Set the Durable Object metadata using the RpcTarget
		//  * Notice that no await is needed here

		const rpcTarget = stub.setMetaData(id.name ?? "default");

		// Call the Durable Object method using the RpcTarget.
		// The DO identifier is stored in the Durable Object's storage
		const greeting = await rpcTarget.computeMessage("world");

		// Call the Durable Object method that does not use the Durable Object identifier
		const simpleGreeting = await rpcTarget.simpleGreeting("world");

		// Clean up the RpcTarget.
		try {
			(await rpcTarget)[Symbol.dispose]?.();
			console.log("RpcTarget cleaned up.");
		} catch (e) {
			console.error({
				message: "RpcTarget could not be cleaned up.",
				error: String(e),
				errorProperties: e,
			});
		}

		return new Response(greeting, { status: 200 });
	},
} satisfies ExportedHandler<Env>;