KV Storage
Fast key-value storage for caching and session data
Overview
Add fast, global key-value storage to your project backend for caching and session data.
KV Storage is perfect for:
- User state & preferences
- Session storage & caching
- Rate limiting & counters
- Quick reads with low latency worldwide
Getting Started
$ playcademy init # Select "Yes" for KV storage$ playcademy kv initNot a Database
KV is a simple key-value store, not a relational database.
For complex data relationships or write-heavy workloads, use the Database integration instead.
Managing KV Storage
CLI Commands
All KV commands work with both local and remote storage:
| Command | Description | Local | Remote |
|---|---|---|---|
kv list | List all keys | ✓ | ✓ |
kv get <key> | Get a value | ✓ | ✓ |
kv set <key> <value> | Set a value | ✓ | ✓ |
kv delete <key> | Delete a key | ✓ | ✓ |
kv inspect <key> | Show key metadata | ✓ | ✓ |
kv stats | Show storage stats | ✓ | ✓ |
kv seed <file> | Load test data | ✓ | ✓ |
kv clear | Clear all keys | ✓ | ✓ |
kv dump | Export data as JSON/CSV | ✓ | ✓ |
View Full Command Documentation
See KV CLI Reference for all options (--raw, --json,
--force, etc.)
Local Development
Start your dev server to use KV locally:
$ playcademy dev # or `vite dev` if using @playcademy/vite-pluginExample CLI commands:
$ playcademy kv set user:123:state '{"score": 100}' # Set a value$ playcademy kv get user:123:state # Get a value$ playcademy kv list # List all keys$ playcademy kv stats # Show statisticsRemote Operations
Add --remote to work with your deployed app's KV storage:
$ playcademy kv list --remote # Staging$ playcademy kv list --remote --env production # Production$ playcademy kv set config:flags '{"beta": true}' --remote # Set in staging$ playcademy kv seed seeds/kv.json --remote # Seed staging with test dataUsing KV Storage
Access KV storage via c.env.KV in your API routes.
User-Scoped Keys
KV keys are often scoped to an authenticated user. Access the platform user via c.get('playcademyUser'):
export async function GET(c: Context): Promise<Response> {
const playcademyUser = c.get('playcademyUser')
if (!playcademyUser) {
return c.json({ error: 'Not authenticated' }, 401)
}
const state = await c.env.KV.get(`user:${playcademyUser.sub}:state`)
return c.json({
success: true,
data: state ? JSON.parse(state) : null,
})
}export async function POST(c: Context): Promise<Response> {
const playcademyUser = c.get('playcademyUser')
if (!playcademyUser) {
return c.json({ error: 'Not authenticated' }, 401)
}
const { score, level } = await c.req.json()
const state = {
score,
level,
lastPlayed: new Date().toISOString(),
}
await c.env.KV.put(`user:${playcademyUser.sub}:state`, JSON.stringify(state), {
metadata: {
email: playcademyUser.email,
name: playcademyUser.name,
},
})
return c.json({ success: true, data: state })
}export async function DELETE(c: Context): Promise<Response> {
const playcademyUser = c.get('playcademyUser')
if (!playcademyUser) {
return c.json({ error: 'Not authenticated' }, 401)
}
await c.env.KV.delete(`user:${playcademyUser.sub}:state`)
return c.json({ success: true })
}Authentication Context Required
playcademyUser is populated when your app runs on the Playcademy platform and requests are made via the SDK.
For local development:
- Vite Plugin: Use Platform Mode to get a mock user
- Godot: Make sure to configure your local development environment
- Standalone apps: Set up the Authentication Integration for your own user accounts
KV API surface:
| Method | Description | Example |
|---|---|---|
get(key) | Retrieve a value | await c.env.KV.get('user:123') |
put(key, value) | Store a value | await c.env.KV.put('user:123', 'data') |
delete(key) | Remove a key | await c.env.KV.delete('user:123') |
list(options?) | List keys | await c.env.KV.list({ prefix: 'user:' }) |
User Identity in KV Metadata
Attaching email and name as metadata on user-scoped writes (as shown in the "Write Data" tab above) enables playcademy kv list to display human-readable identity alongside each key:
user:07167f8e-...:savedata Wyatt Victore <wyatt@school.edu>
user:0f6133c8-...:savedata Vivian Ajouz <vivian@school.edu>Use playcademy kv dump to export all data as JSON or CSV with identity included.
Advanced: Expiration & Metadata
You can also use expirationTtl for data that should expire:
await c.env.KV.put('session:abc', data, {
expirationTtl: 3600, // Expire in 1 hour
metadata: { version: '1.0' },
})Key Naming Patterns
Use prefixes to organize and manage your keys:
| Pattern | Use Case | Example |
|---|---|---|
user:{sub}:* | User-scoped data | user:${playcademyUser.sub}:state |
session:{sessionId}:* | Session data | session:abc123 |
ratelimit:* | Rate limiting | ratelimit:user:${playcademyUser.sub}:api |
cache:* | Cached data | cache:leaderboard:daily |
config:* | Configuration | config:feature-flags |
Why Prefixes Matter
| Benefit | How It Helps |
|---|---|
| Organization | Similar data types can be grouped together |
| Filtering | You can use KV.list({ prefix: 'user:' }) to find related keys |
| Debugging | Quickly identify usage patterns in playcademy kv stats |
| Cleanup | Bulk clear data by prefix during testing |
Deployment
The remote KV namespace is automatically created when you deploy for the first time:
$ playcademy deploy