Simple interface for accessing Cloudflare Durable Objects' storage and class methods.
This library handles request building, fetching and responding behind the scenes via lightweight proxy object which provides interface for accessing DO instance's storage and class methods.
npm install do-proxy
- Todo app utilizing
storage
andbatch
methods - Todo app extending
DOProxy
class and utilizingclass
methods
Make your Durable Object class methods accessible by extending the DOProxy
.
import { DOProxy } from 'do-proxy';
class MyDOClass extends DOProxy {
// Arguments & return values have to be JSON serialiazable
myClassMethod(param: string) {
// Do what ever you would do inside DO
}
}
Inside your Worker's fetch
method:
// Get `DurableObjectNamespace` wrapped inside our proxy
const MY_DO_BINDING = MyDOClass.wrap(env.MY_DO_BINDING);
// You can use the default namespace methods or shorthand methods `getByName` & `getById`
const stub = MY_DO_BINDING.getByName('name');
// You can access instance's storage methods
const res1 = await stub.storage.get('my-store');
// You can also access your class's methods.
const res2 = await stub.class.myClassMethod('foo');
// Or handle both with a single fetch behind the scenes using `batch` method
const [res3, res4] = await stub.batch(() => [
stub.storage.get('my-store'),
stub.class.myClassMethod('foo'),
]);
You can use DOProxy
as is for Durable Object bindings. This enables you to use storage
methods.
Here we expect you to have DO class Todo
bound to TODO
inside wrangler.toml
:
import { DOProxy } from 'do-proxy';
export { DOProxy as Todo };
export default {
async fetch(req: Request, env: any) {
const TODO = DOProxy.wrap(env.TODO);
const stub = TODO.getByName('name');
await todo.storage.put('todo:1', 'has to be done');
const list = Object.fromEntries(await todo.storage.list());
return Response.json(list);
},
};
Or you can extend it, which enables you to call class methods via class
property:
import { DOProxy } from 'do-proxy';
class Todo extends DOProxy {
state: DurableObjectState;
constructor(state: DurableObjectState) {
super(state);
this.state = state;
}
async add(todo: string) {
const id = Math.ceil(Math.random() * 100);
this.state.storage.put(`todo:${id}`, todo);
return id;
}
async get(id: number) {
return this.state.storage.get(`todo:${id}`);
}
}
export default {
async fetch(req: Request, env: any) {
const stub = Todo.wrap(env.TODO).getByName('my-todos');
const id = await stub.class.add('has to be done');
const todo = await stub.class.get(id);
return Response.json({
id,
todo,
});
},
};
export { Todo };
You can also utilize the batch
method which allows you to run multiple methods with one fetch request to DO instance:
// See previous example for `Todo` details
const [, , list] = await stub.batch(() => [
stub.class.add('my todo'),
stub.class.add('my other todo'),
stub.storage.list(),
]);
return Response.json(Object.fromEntries(list as Map<string, string>));
DOProxy
can be used as Durable Object class as is. It gives you access to Durable Object instance's Transactional storage API methods (excluding transaction
which can't be proxied because of JSON serialization. See batch method).
Available methods: DurableObjectStubProxy.storage.get|put|delete|deleteAll|list|getAlarm|setAlarm|deleteAlarm|sync
If you need to invoke Durable Object instance's multiple times, DurableObjectStubProxy
has a batch
method which allows you to run multiple method calls inside one fetch request.
Method calls passed to batch
will be run in sequence.
const COUNTER = Counter.wrap(env.Counter);
const stub = COUNTER.get(COUNTER.newUniqueId());
await stub.batch(() => [
stub.class.increment(),
stub.class.increment(),
stub.storage.deleteAll(),
stub.class.increment(),
]); // => [1, 2, null, 1]
This method return DurableObjectNamespace
wrapped inside proxy.
It has all the same methods that DurableObjectNamespace
:
newUniqueId(options?: DurableObjectNamespaceNewUniqueIdOptions | undefined): DurableObjectId
;idFromName(name: string): DurableObjectId
idFromString(id: string): DurableObjectId
get(id: DurableObjectId): DurableObjectStubProxy
It also has some custom shorthand methods:
getByName(name: string): DurableObjectStubProxy
: Shorthand forDO.get(DO.idFromName('foo'))
getByString(id: string): DurableObjectStubProxy
: Shorthand forDO.get(DO.idFromString(hexId))
get
Method returns DurableObjectStubProxy
instead of DurableObjectStub
.
id: DurableObjectId
stub: DurableObjectStub
: The actual stub if you need access to itstorage: Object
: Storage methodsbatch: (callback: () => Promise<unknown>[]) => unknown[]
class: Object|undefined
: All the class methods ifwrap
was called on an extended class
Remember that we are still doing fetch requests even if it is done in the background, so everything sent to class
and storage
methods must be JSON serializable.
Not the Durable Object proxy you were looking for?
do-proxy
for Rust by @fisherdarling