Giter VIP home page Giter VIP logo

itty-durable's Introduction

Itty Durable

npm package Build Status Open Issues

Features

  • Removes nearly all boilerplate from Durable Objects
  • Run instance methods directly on stub (will asynchronously call the same on Durable Object)
  • Optional persistance (on change)
  • Optional created/modified timestamps

Intro

This takes the extreme stateful power of Cloudflare Durable Objects (now in open beta), but drastically cuts down on the boilerplate to use them by pairing it with the flexibility of Itty Router. Currently, the only way to communicate to durable objects (DO) is via fetch, requiring internal routing/handling of requests inside the DO, as well as building/passing the request in from a Worker or other DO in the first place. On top of that, there are a couple steps to even get the instance "stub" to work with in the first place, before you can call fetch on it.

IttyDurable offers a shortcut.

By having your durable objects extend the IttyDurable base class, it creates automatic internal routing/fetch handling via a tiny, embedded Itty Router. This allows you to ignore the initialization (from storage) step, as well as the fetch call itself from inside the DO, instead using the internal router for access/flow.

By adding in the next piece, the withDurables() middleware to the calling router (the outside Worker usually), we make this even more elegant. Now, you can typically ignore the durable object router entirely, and instead call methods (or await properties) directly on the stub itself. Under the hood, this fires a fetch that the built-in router will handle, firing the appropriate method, and passing (json-parsable) arguments in from the request.

DISCLAIMER: This is very much a "working prototype" and will be hardened over the coming weeks with the help of folks on the CF Discord group, and your feedback (in issues). API changes, polls, etc will be broadcast on the #durable-objects channel of that server, as well as on Twitter. Please follow along there (or follow me) for updates and to communicate feedback! Additionally, I'll be putting together a screencast/explanation on YouTube to show how it works - hopefully that can inspire someone else to come along and make it even better!

Installation

npm install itty-durable itty-router itty-router-extras

Example

Counter.js (your durable object class)
import { IttyDurable } from 'itty-durable'

export class Counter extends IttyDurable {
  constructor(state, env) {
    super(state, env)
    this.counter = 0
  }

  increment() {
    this.counter++
  }

  add(a, b) {
    return a + b
  }
}
Worker.js (your standard CF worker)
import { ThrowableRouter, missing, withParams } from 'itty-router-extras'
import { withDurables } from 'itty-durable'

// export the durable class, per spec
export { Counter } from './Counter'

const router = ThrowableRouter({ base: '/counter' })

router
  // add upstream middleware, allowing Durable access off the request
  .all('*', withDurables())

  // get get the durable itself... returns json response, so no need to wrap
  .get('/', ({ Counter }) => Counter.get('test').toJSON())

  // example route with multiple calls to DO
  .get('/increment-a-few-times',
    async ({ Counter }) => {
      const counter = Counter.get('test') // gets DO with id/namespace = 'test'

      // then we fire some methods on the durable... these could all be done separately.
      await Promise.all([
        counter.increment(),
        counter.increment(),
        counter.increment(),
      ])

      // and return the contents (it'll be a json response)
      return counter.toJSON()
    }
  )

  // reset the durable)
  .get('/reset', ({ Counter }) => Counter.get('test').clear())

  // will pass on unknown requests to the durable... (e.g. /counter/add/3/4 => 7)
  .get('/:action/:a?/:b?', withParams,
    ({ Counter, action, a, b }) => Counter.get('test')[action](Number(a), Number(b))
  )

  // 404 for everything else
  .all('*', () => missing('Are you sure about that?'))

// with itty, and using ES6 module syntax (required for DO), this is all you need
export default {
  fetch: router.handle
}

Interacting with it!

GET /counter/increment-a-few-times          => { counter: 3 }
GET /counter/increment-a-few-times          => { counter: 6 }
GET /counter/reset                          => { counter: 0 }
GET /counter/increment                      => { counter: 1 }
GET /counter/increment                      => { counter: 2 }
GET /counter/add/20/3                       => 23
GET /counter                                => { counter: 2 }

(more examples to come shortly, hang tight!)

Exports

IttyDurable: class

Base class to extend, with persistOnChange, but no timestamps.

createIttyDurable(options = {}): class

Factory function for defining another IttyDurable class (different base options).

withDurables(options = {})

This is the Worker middleware to put either on routes individually, up globally as an upstream route. This allows requests for the DO binding directly off the request, and simplifies even the id translation. Any durable stubs retrieved this way automatically talk to the router within IttyDurable (base class) when accessing instance methods on the stub, allowing all fetch boilerplate to be abstracted away.

Special Thanks

Big time thanks to all the fantastic developers on the Cloudflare Workers discord group, for their putting up with my constant questions, code snippets, and guiding me off the dangerous[ly flawed] path of async setters ;)

Contributors

Let's face it, in open source, these are the real heroes... improving the quality of libraries out there for everyone!

  • README tweaks, fixes, improvements: @tomByrer

itty-durable's People

Contributors

kwhitley avatar tombyrer avatar

Watchers

James Cloos avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.