存储应用配置数据是 Workers KV 的理想使用场景。配置数据可以包括为每个用户或租户个性化应用的数据、为用户组启用功能、使用允许列表/拒绝列表限制访问等。这些使用场景可能具有高读取量,Workers KV 高度可缓存,可确保从 Workers 应用进行低延迟读取。
在本示例中,应用配置数据用于为每个用户个性化 Workers 应用。配置数据存储在外部应用和数据库中,并使用 REST API 写入 Workers KV。
在某些情况下,配置数据的权威来源可能存储在 Workers KV 之外。 如果是这种情况,请使用 Workers KV REST API 将配置数据写入 Workers KV 命名空间。
以下外部 Node.js 应用演示了一个简单脚本,从数据库读取用户数据并使用 REST API 库将其写入 Workers KV。
const postgres = require('postgres');
const { Cloudflare } = require('cloudflare');
const { backOff } = require('exponential-backoff');
if(!process.env.DATABASE_CONNECTION_STRING || !process.env.CLOUDFLARE_EMAIL || !process.env.CLOUDFLARE_API_KEY || !process.env.CLOUDFLARE_WORKERS_KV_NAMESPACE_ID || !process.env.CLOUDFLARE_ACCOUNT_ID) {
console.error('Missing required environment variables.');
process.exit(1);
}
// Setup Postgres connection
const sql = postgres(process.env.DATABASE_CONNECTION_STRING);
// Setup Cloudflare REST API client
const client = new Cloudflare({
apiEmail: process.env.CLOUDFLARE_EMAIL,
apiKey: process.env.CLOUDFLARE_API_KEY,
});
// Function to sync Postgres data to Workers KV
async function syncPreviewStatus() {
console.log('Starting sync of user preview status...');
try {
// Get all users and their preview status
const users = await sql`SELECT id, preview_features_enabled FROM users`;
console.log(users);
// Create the bulk update body
const bulkUpdateBody = users.map(user => ({
key: user.id,
value: JSON.stringify({
preview_features_enabled: user.preview_features_enabled
})
}));
const response = await backOff(async () => {
console.log("trying to update")
try{
const response = await client.kv.namespaces.bulkUpdate(process.env.CLOUDFLARE_WORKERS_KV_NAMESPACE_ID, {
account_id: process.env.CLOUDFLARE_ACCOUNT_ID,
body: bulkUpdateBody
});
}
catch(e){
// Implement your error handling and logging here
console.log(e);
throw e; // Rethrow the error to retry
}
});
console.log(`Sync complete. Updated ${users.length} users.`);
} catch (error) {
console.error('Error syncing preview status:', error);
}
}
// Run the sync function
syncPreviewStatus()
.catch(console.error)
.finally(() => process.exit(0));DATABASE_CONNECTION_STRING = <DB_CONNECTION_STRING_HERE>
CLOUDFLARE_EMAIL = <CLOUDFLARE_EMAIL_HERE>
CLOUDFLARE_API_KEY = <CLOUDFLARE_API_KEY_HERE>
CLOUDFLARE_ACCOUNT_ID = <CLOUDFLARE_ACCOUNT_ID_HERE>
CLOUDFLARE_WORKERS_KV_NAMESPACE_ID = <CLOUDFLARE_WORKERS_KV_NAMESPACE_ID_HERE>-- Create users table with preview_features_enabled flag
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
username VARCHAR(100) NOT NULL,
email VARCHAR(255) NOT NULL,
preview_features_enabled BOOLEAN DEFAULT false
);
-- Insert sample users
INSERT INTO users (username, email, preview_features_enabled) VALUES
('alice', '[email protected]', true),
('bob', '[email protected]', false),
('charlie', '[email protected]', true);在此代码片段中,Node.js 应用从 Postgres 数据库读取用户数据,并使用 Cloudflare REST API Node.js 库将用于 Workers 应用配置的用户数据写入 Workers KV。 该应用还使用指数退避来处理错误时的重试。
配置数据现在位于 Workers KV 命名空间中,我们可以在 Workers 应用中使用它为每个用户个性化应用。
// Example configuration data stored in Workers KV:
// Key: "user-id-abc" | Value: {"preview_features_enabled": false}
// Key: "user-id-def" | Value: {"preview_features_enabled": true}
interface Env {
USER_CONFIGURATION: KVNamespace;
}
export default {
async fetch(request, env) {
// Get user ID from query parameter
const url = new URL(request.url);
const userId = url.searchParams.get('userId');
if (!userId) {
return new Response('Please provide a userId query parameter', {
status: 400,
headers: { 'Content-Type': 'text/plain' }
});
}
const userConfiguration = await env.USER_CONFIGURATION.get<{
preview_features_enabled: boolean;
}>(userId, {type: "json"});
console.log(userConfiguration);
// Build HTML response
const html = `
<!DOCTYPE html>
<html>
<head>
<title>My App</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.preview-banner {
background-color: #ffeb3b;
padding: 10px;
text-align: center;
margin-bottom: 20px;
border-radius: 4px;
}
</style>
</head>
<body>
${userConfiguration?.preview_features_enabled ? `
<div class="preview-banner">
🎉 You have early access to preview features! 🎉
</div>
` : ''}
<h1>Welcome to My App</h1>
<p>This is the regular content everyone sees.</p>
</body>
</html>
`;
return new Response(html, {
headers: { "Content-Type": "text/html; charset=utf-8" }
});
}
} satisfies ExportedHandler<Env>;{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "<ENTER_WORKER_NAME>",
"main": "src/index.ts",
"compatibility_date": "2025-03-03",
"observability": {
"enabled": true
},
"kv_namespaces": [
{
"binding": "USER_CONFIGURATION",
"id": "<YOUR_BINDING_ID>"
}
]
}此代码将使用 URL 中的路径并在 KV 存储中查找与该路径关联的文件。它还在响应中设置正确的 MIME 类型,以告知浏览器如何处理响应。要从 KV 存储检索值,此代码使用 arrayBuffer 来正确处理二进制数据,如图像、文档以及视频/音频文件。
为了优化性能,你可以选择将值合并到更少的键值对中。这样做可能会受益于更高的缓存效率和更低的延迟。
例如,你可以将所有用户的配置存储在单个键值对中,而不是将每个用户的配置存储在单独的键值对中。如果配置数据较小且可以轻松管理在单个键值对中(Workers KV 值的大小限制为 25 MiB),这种方法可能适合你的使用场景。