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
storageandbatchmethods - Todo app extending
DOProxyclass and utilizingclassmethods
Make your Durable Object class methods accessible by extending the DOProxy.
import{DOProxy}from'do-proxy';classMyDOClassextendsDOProxy{// Arguments & return values have to be JSON serialiazablemyClassMethod(param: string){// Do what ever you would do inside DO}}Inside your Worker's fetch method:
// Get `DurableObjectNamespace` wrapped inside our proxyconstMY_DO_BINDING=MyDOClass.wrap(env.MY_DO_BINDING);// You can use the default namespace methods or shorthand methods `getByName` & `getById`conststub=MY_DO_BINDING.getByName('name');// You can access instance's storage methodsconstres1=awaitstub.storage.get('my-store');// You can also access your class's methods.constres2=awaitstub.class.myClassMethod('foo');// Or handle both with a single fetch behind the scenes using `batch` methodconst[res3,res4]=awaitstub.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{DOProxyasTodo};exportdefault{asyncfetch(req: Request,env: any){constTODO=DOProxy.wrap(env.TODO);conststub=TODO.getByName('name');awaittodo.storage.put('todo:1','has to be done');constlist=Object.fromEntries(awaittodo.storage.list());returnResponse.json(list);},};Or you can extend it, which enables you to call class methods via class property:
import{DOProxy}from'do-proxy';classTodoextendsDOProxy{state: DurableObjectState;constructor(state: DurableObjectState){super(state);this.state=state;}asyncadd(todo: string){constid=Math.ceil(Math.random()*100);this.state.storage.put(`todo:${id}`,todo);returnid;}asyncget(id: number){returnthis.state.storage.get(`todo:${id}`);}}exportdefault{asyncfetch(req: Request,env: any){conststub=Todo.wrap(env.TODO).getByName('my-todos');constid=awaitstub.class.add('has to be done');consttodo=awaitstub.class.get(id);returnResponse.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` detailsconst[,,list]=awaitstub.batch(()=>[stub.class.add('my todo'),stub.class.add('my other todo'),stub.storage.list(),]);returnResponse.json(Object.fromEntries(listasMap<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.
constCOUNTER=Counter.wrap(env.Counter);conststub=COUNTER.get(COUNTER.newUniqueId());awaitstub.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): DurableObjectIdidFromString(id: string): DurableObjectIdget(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: DurableObjectIdstub: DurableObjectStub: The actual stub if you need access to itstorage: Object: Storage methodsbatch: (callback: () => Promise<unknown>[]) => unknown[]class: Object|undefined: All the class methods ifwrapwas 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-proxyfor Rust by @fisherdarling