Service Bindings
Cloudflare Workers provide additional runtime APIs on their platform, such as Durable Objects & KV. You can access these bindings via the env
property during a fetch
event.
Table of contents:
- KV - low-latency, key-value data store.
- Durable Objects - low-latency coordination and consistent storage.
KV
Workers KV is a global, low-latency, key-value data store. It stores data in a small number of centralized data centers, then caches that data in Cloudflare's data centers after access. KV supports exceptionally high read volumes with low latency, making it possible to build highly dynamic APIs and websites that respond as quickly as a cached static file would. While reads are periodically revalidated in the background, requests which are not in cache and need to hit the centralized back end can see high latencies. Learn more.
To use KV, you must first create a KV namespace on your account. You can use the wranger
CLI to create a new namespace.
Once created, ensure it is referenced in your wrangler.toml
file, for example:
kv_namespaces = [
{ binding = "TODOS", id = "06779da6940b431db6e566b4846d64db" }
]
Next, call the getKVNamespace
method on the Environment
class, referencing the KV namespace binding name (in this case "TODOS"). An error will be thrown if the namespace does not exist.
import 'package:cloudflare_workers/cloudflare_workers.dart';
void main() {
CloudflareWorkers(
fetch: (request, env, ctx) async {
final todos = env.getKVNamespace('TODOS');
// ...
},
);
}
You can then interact with your KV namespace. For example, to put a new value into KV:
await todos.put('write-docs', 'in-progress');
You can access values too:
final status = await todos.get('write-docs');
KV supports additional features, such as reading and writing metadata, JSON values, listing keys, writing buffers and much more.
Durable Objects
Durable Objects provide low-latency coordination and consistent storage for the Workers platform. A given namespace can support essentially unlimited Durable Objects, with each Object having access to a transactionally consistent key-value storage API. Learn more.
To use Durable Objects, first define the Durable Object name
and class_name
in your wrangler.toml
file:
durable_objects.bindings = [
{name = "VIEWS", class_name = "ViewsDurableObject"},
]
Next, create a new Dart class which extends the DurableObject
class. You must implement the fetch
class by default, however alarms are also supported.
import 'package:cloudflare_workers/cloudflare_workers.dart';
class ViewsDurableObject extends DurableObject {
@override
Future<Response> fetch(Request request, DurableObjectEnv env) async {
// ...
}
}
Define the class instance in the CloudflareWorkers
instance, provinding a Map
whose key is the class_name
and a function returning a new instance of your class.
import 'package:cloudflare_workers/cloudflare_workers.dart';
class ViewsDurableObject extends DurableObject { ... }
void main() {
CloudflareWorkers(
durableObjects: {
'ViewsDurableObject': () => ViewsDurableObject(),
},
fetch: (request, env, ctx) async {
// ...
},
);
}
You can then access the Durable Object namespace via the Environment
during a fetch
request, referencing the name
of the namespace. If the namespace does not exist, an error will be thrown.
void main() {
CloudflareWorkers(
durableObjects: {
'ViewsDurableObject': () => ViewsDurableObject(),
},
fetch: (request, env, ctx) async {
final views = namespace.getDurableObjectNamespace('VIEWS');
final id = views.idFromName('...');
return views.get(id).fetch(request);
},
);
}
State, Storage & Alarms
Each Durable Object instance has access to it's own state (and the environment). You can access Durable Object State via the state
getter. The state instance provides access to the Durable Object's unique ID, as well as a transaction storage API.
To access the storage API, call the storage
property on the state
instance. Here you can manage persisted storage for this specific Durable Object:
class ViewsDurableObject extends DurableObject {
@override
Future<Response> fetch(Request request, DurableObjectEnv env) async {
final views = (await state.storage.get<int>('views') ?? 0) + 1;
await state.storage.put('views', views);
return Response.ok('This Durable Object has been requested $views times!');
}
}
Additionally you can manage alarms, enabling you to schedule events to occur at a specific time:
class ViewsDurableObject extends DurableObject {
@override
Future<Response> fetch(Request request, DurableObjectEnv env) async {
// Add some data to the DO storage.
await state.storage.put(
DateTime.now().toIso8601String(),
{
'job': '1234',
'data': {
'foo': 'bar',
}
}
);
// If not already set, set an alarm for 60 seconds from now.
if (await state.storage.getAlarm() == null) {
await state.storage.setAlarm(DateTime.now().add(Duration(seconds: 60)));
}
return Response.ok('Job queued!');
}
@override
FutureOr<void> alarm() {
print('Alarm triggered, processing jobs...');
final jobs = await state.storage.list();
// Process jobs...
}
}