Initial commit
This commit is contained in:
483
references/workers-api.md
Normal file
483
references/workers-api.md
Normal file
@@ -0,0 +1,483 @@
|
||||
# Cloudflare Workers KV - Complete API Reference
|
||||
|
||||
This document provides the complete Workers KV API reference based on official Cloudflare documentation.
|
||||
|
||||
---
|
||||
|
||||
## KVNamespace Interface
|
||||
|
||||
```typescript
|
||||
interface KVNamespace {
|
||||
get(key: string, options?: Partial<KVGetOptions<undefined>>): Promise<string | null>;
|
||||
get(key: string, type: "text"): Promise<string | null>;
|
||||
get<ExpectedValue = unknown>(key: string, type: "json"): Promise<ExpectedValue | null>;
|
||||
get(key: string, type: "arrayBuffer"): Promise<ArrayBuffer | null>;
|
||||
get(key: string, type: "stream"): Promise<ReadableStream | null>;
|
||||
get(keys: string[]): Promise<Map<string, string | null>>;
|
||||
get<ExpectedValue = unknown>(keys: string[], type: "json"): Promise<Map<string, ExpectedValue | null>>;
|
||||
|
||||
getWithMetadata<Metadata = unknown>(key: string, options?: Partial<KVGetOptions<undefined>>): Promise<KVGetWithMetadataResult<string, Metadata>>;
|
||||
getWithMetadata<ExpectedValue = unknown, Metadata = unknown>(key: string, type: "json"): Promise<KVGetWithMetadataResult<ExpectedValue, Metadata>>;
|
||||
getWithMetadata<Metadata = unknown>(keys: string[]): Promise<Map<string, KVGetWithMetadataResult<string, Metadata>>>;
|
||||
|
||||
put(key: string, value: string | ArrayBuffer | ArrayBufferView | ReadableStream, options?: KVPutOptions): Promise<void>;
|
||||
|
||||
delete(key: string): Promise<void>;
|
||||
|
||||
list<Metadata = unknown>(options?: KVListOptions): Promise<KVListResult<Metadata>>;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Read Operations
|
||||
|
||||
### `get()` - Single Key
|
||||
|
||||
Read a single key-value pair.
|
||||
|
||||
**Signature:**
|
||||
```typescript
|
||||
get(key: string, options?: KVGetOptions): Promise<T | null>
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `key` (string, required) - The key to read
|
||||
- `options` (object, optional):
|
||||
- `type` - Return type: `"text"` (default), `"json"`, `"arrayBuffer"`, `"stream"`
|
||||
- `cacheTtl` (number) - Edge cache duration in seconds (minimum: 60)
|
||||
|
||||
**Returns:**
|
||||
- `Promise<T | null>` - Value or `null` if key doesn't exist
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
// Text (default)
|
||||
const value = await env.MY_KV.get('my-key');
|
||||
|
||||
// JSON
|
||||
const data = await env.MY_KV.get<MyType>('my-key', { type: 'json' });
|
||||
|
||||
// With cache optimization
|
||||
const value = await env.MY_KV.get('my-key', {
|
||||
type: 'text',
|
||||
cacheTtl: 300, // Cache for 5 minutes
|
||||
});
|
||||
|
||||
// ArrayBuffer
|
||||
const buffer = await env.MY_KV.get('binary-key', { type: 'arrayBuffer' });
|
||||
|
||||
// Stream (for large values)
|
||||
const stream = await env.MY_KV.get('large-file', { type: 'stream' });
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `get()` - Multiple Keys (Bulk)
|
||||
|
||||
Read multiple keys in a single operation.
|
||||
|
||||
**Signature:**
|
||||
```typescript
|
||||
get(keys: string[], type?: 'text' | 'json'): Promise<Map<string, T | null>>
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `keys` (string[], required) - Array of keys to read
|
||||
- `type` (optional) - Return type: `"text"` (default) or `"json"`
|
||||
|
||||
**Returns:**
|
||||
- `Promise<Map<string, T | null>>` - Map of key-value pairs
|
||||
|
||||
**Important:**
|
||||
- Counts as **1 operation** regardless of number of keys
|
||||
- Only supports `text` and `json` types (not `arrayBuffer` or `stream`)
|
||||
- For binary/stream types, use individual `get()` calls with `Promise.all()`
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
// Read multiple keys
|
||||
const keys = ['key1', 'key2', 'key3'];
|
||||
const values = await env.MY_KV.get(keys);
|
||||
|
||||
// Access values
|
||||
const value1 = values.get('key1');
|
||||
const value2 = values.get('key2');
|
||||
|
||||
// Convert to object
|
||||
const obj = Object.fromEntries(values);
|
||||
|
||||
// Read as JSON
|
||||
const values = await env.MY_KV.get<MyType>(keys, 'json');
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `getWithMetadata()` - Single Key
|
||||
|
||||
Read key-value pair with metadata.
|
||||
|
||||
**Signature:**
|
||||
```typescript
|
||||
getWithMetadata<Value, Metadata>(
|
||||
key: string,
|
||||
options?: KVGetOptions
|
||||
): Promise<KVGetWithMetadataResult<Value, Metadata>>
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- Same as `get()`
|
||||
|
||||
**Returns:**
|
||||
```typescript
|
||||
{
|
||||
value: Value | null,
|
||||
metadata: Metadata | null
|
||||
}
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
// Get with metadata
|
||||
const { value, metadata } = await env.MY_KV.getWithMetadata('my-key');
|
||||
|
||||
// Get as JSON with metadata
|
||||
const { value, metadata } = await env.MY_KV.getWithMetadata<MyType>('my-key', {
|
||||
type: 'json',
|
||||
cacheTtl: 300,
|
||||
});
|
||||
|
||||
if (value !== null) {
|
||||
console.log('Value:', value);
|
||||
console.log('Metadata:', metadata);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `getWithMetadata()` - Multiple Keys (Bulk)
|
||||
|
||||
Read multiple keys with metadata.
|
||||
|
||||
**Signature:**
|
||||
```typescript
|
||||
getWithMetadata<Metadata>(
|
||||
keys: string[],
|
||||
type?: 'text' | 'json'
|
||||
): Promise<Map<string, KVGetWithMetadataResult<T, Metadata>>>
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
const keys = ['key1', 'key2'];
|
||||
const results = await env.MY_KV.getWithMetadata(keys);
|
||||
|
||||
for (const [key, data] of results) {
|
||||
console.log(key, data.value, data.metadata);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Write Operations
|
||||
|
||||
### `put()` - Write Key-Value Pair
|
||||
|
||||
Write or update a key-value pair.
|
||||
|
||||
**Signature:**
|
||||
```typescript
|
||||
put(
|
||||
key: string,
|
||||
value: string | ArrayBuffer | ArrayBufferView | ReadableStream,
|
||||
options?: KVPutOptions
|
||||
): Promise<void>
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `key` (string, required) - Maximum 512 bytes
|
||||
- `value` (required) - Maximum 25 MiB
|
||||
- `options` (object, optional):
|
||||
- `expiration` (number) - Absolute expiration time (seconds since epoch)
|
||||
- `expirationTtl` (number) - TTL in seconds from now (minimum: 60)
|
||||
- `metadata` (any) - JSON-serializable metadata (maximum: 1024 bytes)
|
||||
|
||||
**Returns:**
|
||||
- `Promise<void>`
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
// Simple write
|
||||
await env.MY_KV.put('key', 'value');
|
||||
|
||||
// Write JSON
|
||||
await env.MY_KV.put('user:123', JSON.stringify({ name: 'John' }));
|
||||
|
||||
// Write with TTL
|
||||
await env.MY_KV.put('session', sessionData, {
|
||||
expirationTtl: 3600, // Expire in 1 hour
|
||||
});
|
||||
|
||||
// Write with absolute expiration
|
||||
const expirationTime = Math.floor(Date.now() / 1000) + 86400; // 24 hours
|
||||
await env.MY_KV.put('token', tokenValue, {
|
||||
expiration: expirationTime,
|
||||
});
|
||||
|
||||
// Write with metadata
|
||||
await env.MY_KV.put('config', configData, {
|
||||
metadata: {
|
||||
updatedAt: Date.now(),
|
||||
updatedBy: 'admin',
|
||||
version: 2,
|
||||
},
|
||||
});
|
||||
|
||||
// Write with everything
|
||||
await env.MY_KV.put('key', 'value', {
|
||||
expirationTtl: 600,
|
||||
metadata: { source: 'api' },
|
||||
});
|
||||
```
|
||||
|
||||
**Limits:**
|
||||
- **Key size**: Maximum 512 bytes
|
||||
- **Value size**: Maximum 25 MiB
|
||||
- **Metadata size**: Maximum 1024 bytes (JSON serialized)
|
||||
- **Write rate**: Maximum **1 write per second per key**
|
||||
- **Expiration minimum**: 60 seconds
|
||||
|
||||
---
|
||||
|
||||
## Delete Operations
|
||||
|
||||
### `delete()` - Delete Key
|
||||
|
||||
Delete a key-value pair.
|
||||
|
||||
**Signature:**
|
||||
```typescript
|
||||
delete(key: string): Promise<void>
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `key` (string, required) - Key to delete
|
||||
|
||||
**Returns:**
|
||||
- `Promise<void>` - Always succeeds, even if key doesn't exist
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
// Delete single key
|
||||
await env.MY_KV.delete('my-key');
|
||||
|
||||
// Delete multiple keys
|
||||
const keys = ['key1', 'key2', 'key3'];
|
||||
await Promise.all(keys.map(key => env.MY_KV.delete(key)));
|
||||
```
|
||||
|
||||
**Note:** For bulk delete of >10,000 keys, use the REST API.
|
||||
|
||||
---
|
||||
|
||||
## List Operations
|
||||
|
||||
### `list()` - List Keys
|
||||
|
||||
List keys in the namespace.
|
||||
|
||||
**Signature:**
|
||||
```typescript
|
||||
list<Metadata>(options?: KVListOptions): Promise<KVListResult<Metadata>>
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
```typescript
|
||||
interface KVListOptions {
|
||||
prefix?: string; // Filter keys by prefix
|
||||
limit?: number; // Max keys to return (default: 1000, max: 1000)
|
||||
cursor?: string; // Pagination cursor
|
||||
}
|
||||
```
|
||||
|
||||
**Returns:**
|
||||
```typescript
|
||||
interface KVListResult<Metadata> {
|
||||
keys: {
|
||||
name: string;
|
||||
expiration?: number; // Seconds since epoch
|
||||
metadata?: Metadata;
|
||||
}[];
|
||||
list_complete: boolean; // true if no more keys
|
||||
cursor?: string; // Use for next page
|
||||
}
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```typescript
|
||||
// List all keys (up to 1000)
|
||||
const result = await env.MY_KV.list();
|
||||
|
||||
// List with prefix
|
||||
const users = await env.MY_KV.list({ prefix: 'user:' });
|
||||
|
||||
// List with limit
|
||||
const recent = await env.MY_KV.list({ limit: 100 });
|
||||
|
||||
// Pagination
|
||||
let cursor: string | undefined;
|
||||
do {
|
||||
const result = await env.MY_KV.list({ cursor });
|
||||
|
||||
// Process keys
|
||||
console.log(result.keys);
|
||||
|
||||
cursor = result.list_complete ? undefined : result.cursor;
|
||||
} while (cursor);
|
||||
```
|
||||
|
||||
**Important:**
|
||||
- Keys are **always** sorted lexicographically (UTF-8)
|
||||
- **Always check `list_complete`**, not `keys.length === 0`
|
||||
- Empty `keys` array doesn't mean no more data (tombstones exist)
|
||||
- When paginating with `prefix`, pass `prefix` with each cursor request
|
||||
|
||||
---
|
||||
|
||||
## Type Definitions
|
||||
|
||||
### KVGetOptions
|
||||
|
||||
```typescript
|
||||
interface KVGetOptions<Type> {
|
||||
type: Type; // "text" | "json" | "arrayBuffer" | "stream"
|
||||
cacheTtl?: number; // Edge cache duration (minimum: 60 seconds)
|
||||
}
|
||||
```
|
||||
|
||||
### KVPutOptions
|
||||
|
||||
```typescript
|
||||
interface KVPutOptions {
|
||||
expiration?: number; // Seconds since epoch
|
||||
expirationTtl?: number; // Seconds from now (minimum: 60)
|
||||
metadata?: any; // Max 1024 bytes serialized
|
||||
}
|
||||
```
|
||||
|
||||
### KVGetWithMetadataResult
|
||||
|
||||
```typescript
|
||||
interface KVGetWithMetadataResult<Value, Metadata> {
|
||||
value: Value | null;
|
||||
metadata: Metadata | null;
|
||||
}
|
||||
```
|
||||
|
||||
### KVListOptions
|
||||
|
||||
```typescript
|
||||
interface KVListOptions {
|
||||
prefix?: string;
|
||||
limit?: number; // Default: 1000, max: 1000
|
||||
cursor?: string;
|
||||
}
|
||||
```
|
||||
|
||||
### KVListResult
|
||||
|
||||
```typescript
|
||||
interface KVListResult<Metadata = unknown> {
|
||||
keys: {
|
||||
name: string;
|
||||
expiration?: number;
|
||||
metadata?: Metadata;
|
||||
}[];
|
||||
list_complete: boolean;
|
||||
cursor?: string;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Limits
|
||||
|
||||
| Feature | Limit |
|
||||
|---------|-------|
|
||||
| Key size | 512 bytes |
|
||||
| Value size | 25 MiB |
|
||||
| Metadata size | 1024 bytes (JSON) |
|
||||
| Writes per key per second | 1 |
|
||||
| Operations per Worker invocation | 1,000 |
|
||||
| List limit | 1,000 keys |
|
||||
| Minimum cacheTtl | 60 seconds |
|
||||
| Minimum expiration | 60 seconds |
|
||||
| Namespaces per account (Free) | 1,000 |
|
||||
| Namespaces per account (Paid) | 1,000 |
|
||||
| Storage per account (Free) | 1 GB |
|
||||
| Storage per account (Paid) | Unlimited |
|
||||
| Read operations per day (Free) | 100,000 |
|
||||
| Read operations per day (Paid) | Unlimited |
|
||||
| Write operations per day (Free) | 1,000 |
|
||||
| Write operations per day (Paid) | Unlimited |
|
||||
|
||||
---
|
||||
|
||||
## Consistency Model
|
||||
|
||||
### Eventually Consistent
|
||||
|
||||
- Writes are **immediately visible** in the same location
|
||||
- Writes take **up to 60 seconds** to propagate globally
|
||||
- Cached reads may return stale data during propagation
|
||||
|
||||
### Implications
|
||||
|
||||
```typescript
|
||||
// Tokyo datacenter
|
||||
await env.KV.put('counter', '1');
|
||||
const value1 = await env.KV.get('counter'); // "1" ✅
|
||||
|
||||
// London datacenter (within 60 seconds)
|
||||
const value2 = await env.KV.get('counter'); // Might be old value ⚠️
|
||||
|
||||
// After 60+ seconds globally
|
||||
const value3 = await env.KV.get('counter'); // "1" ✅
|
||||
```
|
||||
|
||||
**For strong consistency, use [Durable Objects](https://developers.cloudflare.com/durable-objects/).**
|
||||
|
||||
---
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Common Errors
|
||||
|
||||
1. **429 Too Many Requests**
|
||||
- Cause: >1 write/second to same key
|
||||
- Solution: Implement retry with exponential backoff
|
||||
|
||||
2. **Value too large**
|
||||
- Cause: Value >25 MiB
|
||||
- Solution: Validate size before writing
|
||||
|
||||
3. **Metadata too large**
|
||||
- Cause: Metadata >1024 bytes serialized
|
||||
- Solution: Validate JSON size before writing
|
||||
|
||||
4. **Invalid cacheTtl**
|
||||
- Cause: cacheTtl <60 seconds
|
||||
- Solution: Use minimum 60 seconds
|
||||
|
||||
5. **Operations limit exceeded**
|
||||
- Cause: >1000 KV operations in Worker invocation
|
||||
- Solution: Use bulk operations
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- [Official KV Documentation](https://developers.cloudflare.com/kv/)
|
||||
- [KV API Reference](https://developers.cloudflare.com/kv/api/)
|
||||
- [KV Limits](https://developers.cloudflare.com/kv/platform/limits/)
|
||||
- [How KV Works](https://developers.cloudflare.com/kv/concepts/how-kv-works/)
|
||||
Reference in New Issue
Block a user