Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
tencentedgeone avatar

Makers Storage

  • 30 installs
  • 2k repo stars
  • Updated July 16, 2026
  • tencentedgeone/edgeone-makers-tools

Use when working on frontend tasks.

About

Makers Storage is a skill that helps with frontend work. It supports teams during the build phase of development. Use this skill to improve your frontend processes and deliverables.

  • Makers
  • Storage

Makers Storage by the numbers

  • 30 all-time installs (skills.sh)
  • Ranked #416 of 782 Skill Development skills by installs in the Skillselion catalog
  • Data as of Jul 30, 2026 (Skillselion catalog sync)
npx skills add https://github.com/tencentedgeone/edgeone-makers-tools --skill makers-storage

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs30
repo stars2k
Last updatedJuly 16, 2026
Repositorytencentedgeone/edgeone-makers-tools

What it does

Use when working on frontend tasks.

Files

SKILL.mdMarkdownGitHub ↗

KV Storage

EdgeOne Pages KV is a globally distributed key-value persistent storage service deployed across multiple edge nodes. Data follows an eventual consistency model and synchronizes globally within 60 seconds.

⚠️ KV Storage is only available in Edge Functions — NOT supported in Node Functions.

Prerequisites (MUST complete before using KV)

You must enable KV Storage in the EdgeOne Pages console before writing any code.

Step 1 — Enable KV Storage

1. Log in to the EdgeOne Pages console 2. Navigate to the "KV Storage" page 3. Click "Apply Now" to activate (free tier includes 1 GB storage)

Step 2 — Create a Namespace

A namespace is the unit of data isolation (like a separate database):

1. On the "KV Storage" page, click "Create Namespace" 2. Enter a name (e.g. my-kv-store) 3. Wait for creation to complete

One account can create up to 10 namespaces.

Step 3 — Bind Namespace to Project

After creating a namespace, bind it to your EdgeOne Pages project and assign a variable name.

Option A — From KV Storage page: 1. Open the namespace → "Associated Projects" tab 2. Click "Bind Project" 3. Select project and set the variable name (e.g. my_kv)

Option B — From Project settings: 1. Open project details → "KV Storage" menu 2. Click "Bind Namespace" 3. Select the namespace and set the variable name (e.g. my_kv)

The variable name becomes a global variable in your Edge Function code. Different namespaces can use different variable names.

---

Core Concept: KV is a Global Variable

⚠️ CRITICAL: The KV namespace is accessed as a global variable — it is NOT on context.env.
// ❌ WRONG — KV is NOT in context.env
export async function onRequest(context) {
  const KV = context.env.KV;
  await KV.get('key');
}

// ✅ CORRECT — my_kv is a global variable (name set when binding)
export async function onRequest(context) {
  const value = await my_kv.get('key');
}

---

API Reference

All methods are called on the global KV variable (e.g. my_kv).

put — Write data

put(key: string, value: string | ArrayBuffer | ArrayBufferView | ReadableStream): Promise<void>
  • key: Key name (≤ 512 bytes, alphanumeric and underscores only)
  • value: Value (≤ 25 MB)
  • Returns Promise<void> — always await to confirm write
await my_kv.put('count', '100');
await my_kv.put('user', JSON.stringify({ name: 'Alice' }));

get — Read data

get(key: string, type?: 'text' | 'json' | 'arrayBuffer' | 'stream'): Promise<string | object | ArrayBuffer | ReadableStream | null>
  • type defaults to 'text'; use 'json' to auto-deserialize
  • Returns null if key does not exist
const count = await my_kv.get('count');          // '100' (string)
const user  = await my_kv.get('user', 'json');   // { name: 'Alice' }
const buf   = await my_kv.get('file', 'arrayBuffer');

delete — Remove data

delete(key: string): Promise<void>
await my_kv.delete('count');

list — Enumerate keys

list(options?: { prefix?: string; limit?: number; cursor?: string }): Promise<ListResult>

ListResult:

{
  complete: boolean;  // true if all keys have been returned
  cursor: string;     // pagination cursor for next page
  keys: Array<{ name: string }>;
}

Single page:

const result = await my_kv.list({ prefix: 'user:' });
// result.keys → [{ name: 'user:123' }, { name: 'user:456' }]

Paginate through all keys:

let allKeys = [];
let result;
let cursor;
do {
  result = await my_kv.list({ prefix: 'user:', limit: 256, cursor });
  allKeys.push(...result.keys);
  cursor = result.cursor;
} while (!result.complete);

---

Examples

Page view counter

// edge-functions/api/counter.js

export async function onRequest({ request }) {
  // my_kv is a global variable — NOT context.env.my_kv
  let count = await my_kv.get('page_views');
  count = count ? Number(count) + 1 : 1;

  await my_kv.put('page_views', String(count));

  return new Response(JSON.stringify({ views: count }), {
    headers: { 'Content-Type': 'application/json' },
  });
}

CRUD with KV

// edge-functions/api/users/[id].js

export async function onRequestGet({ params }) {
  const user = await my_kv.get(`user:${params.id}`, 'json');
  if (!user) {
    return new Response(JSON.stringify({ error: 'Not found' }), {
      status: 404,
      headers: { 'Content-Type': 'application/json' },
    });
  }
  return new Response(JSON.stringify(user), {
    headers: { 'Content-Type': 'application/json' },
  });
}

export async function onRequestPost({ params, request }) {
  const data = await request.json();
  await my_kv.put(`user:${params.id}`, JSON.stringify(data));
  return new Response(JSON.stringify({ success: true }), {
    status: 201,
    headers: { 'Content-Type': 'application/json' },
  });
}

export async function onRequestDelete({ params }) {
  await my_kv.delete(`user:${params.id}`);
  return new Response(JSON.stringify({ success: true }), {
    headers: { 'Content-Type': 'application/json' },
  });
}

List all keys with prefix

// edge-functions/api/users.js

export async function onRequestGet() {
  const users = [];
  let result;
  let cursor;

  do {
    result = await my_kv.list({ prefix: 'user:', limit: 256, cursor });
    const fetched = await Promise.all(
      result.keys.map(k => my_kv.get(k.name, 'json'))
    );
    users.push(...fetched.filter(Boolean));
    cursor = result.cursor;
  } while (!result.complete);

  return new Response(JSON.stringify({ users }), {
    headers: { 'Content-Type': 'application/json' },
  });
}

---

Common Errors

SymptomCauseFix
ReferenceError: my_kv is not definedKV not enabled or namespace not boundEnable KV in console → create namespace → bind to project
Accessing context.env.KV returns undefinedKV is a global variable, not on context.envUse my_kv.get(...) directly (global)
get() returns a Promise object, not the valueMissing awaitAlways await KV operations
KV works in production but fails locallyProject not linkedRun edgeone pages link
KV not available in Node FunctionsKV only works in Edge FunctionsMove KV logic to Edge Functions, or use an external database in Node Functions

---

Limits

ResourceLimit
Storage per account (free tier)1 GB
Key length≤ 512 bytes
Value size≤ 25 MB
Namespaces per account10
ConsistencyEventual (≤ 60 s global sync)
List results per call256 max
Supported runtimeEdge Functions only

---

Best Practices

1. Use key prefixes to organize data: user:123, cart:456, config:theme 2. Handle nullget() returns null for missing keys 3. Batch reads with Promise.all() after list() instead of sequential awaits 4. Always wrap writes in try/catch for error handling 5. Serialize objects — use JSON.stringify() for put() and 'json' type for get()

---

Local Development

# 1. Link to a remote project (required for KV access)
edgeone pages link

# 2. Start dev server
edgeone pages dev

# 3. Test
curl http://localhost:8088/api/counter

Production Deployment

1. Enable KV Storage in the console 2. Create a namespace 3. Bind it to the project (set the variable name) 4. Deploy:

   edgeone pages deploy

---

Blob Storage

Distributed object storage for Makers Functions. Ideal for images, documents, user uploads, AI-generated content.

Limits: 1GB storage per free-tier account. Currently Node.js SDK only (@edgeone/pages-blob).

Install

npm install @edgeone/pages-blob

Get a Store

import { getStore } from "@edgeone/pages-blob";

// Inside Makers Functions (auto-configured)
const store = getStore("my-store");

// With strong consistency
const store = getStore({ name: "my-store", consistency: "strong" });

Consistency Model

ModeBehaviorUse when
"eventual" (default)Edge-cached, fastest, data syncs in secondsContent display, tolerate brief staleness
"strong"Bypasses cache, reads from primary storageCounters, state machines, must read latest

API Reference

MethodDescription
store.set(key, value, opts?)Write an object (string / ArrayBuffer / Blob / ReadableStream)
store.setJSON(key, value, opts?)Write JSON (auto-serialized)
store.get(key, opts?)Read an object (returns null if not found)
store.getWithHeaders(key, opts?)Read with response headers (etag, content-type)
store.delete(key)Delete an object
store.list(opts?)List objects (prefix filter, directory grouping, pagination)
store.createUploadUrl(key, opts?)Generate pre-signed PUT URL for client-side direct upload

Write

await store.set("photos/cat.jpg", imageBuffer);
await store.set("notes/todo.txt", "Buy milk");
await store.setJSON("user/preferences", { theme: "dark", lang: "zh-CN" });

// Only write if key doesn't exist
await store.set("init.json", data, { onlyIfNew: true });

Read

const text = await store.get("hello.txt");                              // string
const json = await store.get("config.json", { type: "json" });         // parsed object
const buffer = await store.get("image.png", { type: "arrayBuffer" });  // ArrayBuffer
const stream = await store.get("large.zip", { type: "stream" });       // ReadableStream

// Strong consistency read
const fresh = await store.get("counter", { consistency: "strong" });

type options: "text" (default) | "json" | "arrayBuffer" | "blob" | "stream"

List

// All objects
const { blobs } = await store.list();

// By prefix
const { blobs } = await store.list({ prefix: "photos/" });

// Directory grouping
const { blobs, directories } = await store.list({ prefix: "photos/", directories: true });

// Manual pagination
const page1 = await store.list({ paginate: false });
const page2 = await store.list({ paginate: false, cursor: page1.cursor });

Client-Side Direct Upload

Generate a pre-signed URL for large file uploads without going through Functions:

const { url, key, expiresAt } = await store.createUploadUrl("files/photo.jpg", {
  expireSeconds: 3600,
  contentType: "image/jpeg",
});
// Client PUTs directly to `url`

Delete

await store.delete("photos/cat.jpg");  // silent if key doesn't exist

List All Stores

import { listStores } from "@edgeone/pages-blob";
const { stores } = await listStores();  // [{ name: "my-store" }, ...]

Related skills

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.