Ploy
Ploy
Features

State

Add durable key-value storage to your workers with simple get/set/delete operations.

State

Ploy State provides a durable key-value store backed by SQLite. Unlike cache, state has no expiry -- data persists until you explicitly delete it. This makes it ideal for user preferences, feature flags, counters, and any data that should survive restarts.

Configuration

Add a state binding in your ploy.yaml:

ploy.yaml
kind: dynamic
build: pnpm build
out: dist
state:
  STATE: default

The key (STATE) is the binding name available in your worker's env. The value (default) is the state store identifier.

Run ploy types to generate TypeScript types:

env.d.ts
import type { StateBinding } from "@meetploy/types";

export interface Env {
	STATE: StateBinding;
}

Basic Example

src/index.ts
export default {
	async fetch(request, env) {
		const url = new URL(request.url);

		if (url.pathname === "/set") {
			await env.STATE.set("greeting", "hello world");
			return Response.json({ success: true });
		}

		if (url.pathname === "/get") {
			const value = await env.STATE.get("greeting");
			return Response.json({ value });
		}

		if (url.pathname === "/delete") {
			await env.STATE.delete("greeting");
			return Response.json({ success: true });
		}

		return new Response("State Worker");
	},
} satisfies Ploy;

API Methods

get(key) - Get a Value

Returns the stored value or null if the key doesn't exist.

const value = await env.STATE.get("my-key");
// value is string | null

set(key, value) - Set a Value

Stores a string value. Overwrites any existing value for the same key. Data persists until explicitly deleted.

await env.STATE.set("username", "alice");

delete(key) - Delete a Value

Removes a key from the state store.

await env.STATE.delete("username");

All state values are strings. Use JSON.stringify() and JSON.parse() to store structured data.

Common Patterns

Storing JSON Data

// Set
const user = { name: "Alice", role: "admin" };
await env.STATE.set("user:123", JSON.stringify(user));

// Get
const raw = await env.STATE.get("user:123");
const user = raw ? JSON.parse(raw) : null;

Feature Flags

async function isFeatureEnabled(
	env: PloyEnv,
	feature: string,
): Promise<boolean> {
	const value = await env.STATE.get(`feature:${feature}`);
	return value === "true";
}

// Enable a feature
await env.STATE.set("feature:dark-mode", "true");

// Check the feature
const enabled = await isFeatureEnabled(env, "dark-mode");

Persistent Counters

async function incrementCounter(env: PloyEnv, name: string): Promise<number> {
	const current = await env.STATE.get(`counter:${name}`);
	const newCount = current ? parseInt(current) + 1 : 1;
	await env.STATE.set(`counter:${name}`, String(newCount));
	return newCount;
}

User Preferences

async function getUserPreferences(env: PloyEnv, userId: string) {
	const raw = await env.STATE.get(`prefs:${userId}`);
	return raw ? JSON.parse(raw) : { theme: "light", language: "en" };
}

async function setUserPreferences(
	env: PloyEnv,
	userId: string,
	prefs: Record<string, string>,
) {
	await env.STATE.set(`prefs:${userId}`, JSON.stringify(prefs));
}

Multiple State Bindings

You can configure multiple state stores for different use cases:

ploy.yaml
state:
  STATE: default
  USER_STATE: users
// General state
await env.STATE.set("config", value);

// User-specific state
await env.USER_STATE.set("user:abc", userData);

Next Steps

  • Cache - Add temporary key-value caching with TTL
  • Databases - Add persistent SQLite databases
  • Workers - Learn about Ploy workers

How is this guide?

Last updated on