agentsclimarketplace

Cloudflare bindings

Skill smicolon/ai-kit/packs/hono/skills/cloudflare-bindings

Convention packs for any AI coding tool - agents, skills, commands, and rules for 15 tools including Claude Code, Cursor, Windsurf, and Copilot

Install
npx -y skills add smicolon/ai-kit --skill cloudflare-bindings

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 6 stars6 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

This skill activates when working with Cloudflare Workers bindings like D1, KV, R2, Durable Objects, or environment variables. It provides patterns for database access, caching, file storage, and secrets management.

SKILL.md

7.8 KB, as published. Nobody here has run it

Cloudflare Bindings

Patterns for Cloudflare Workers bindings in Hono.

Type Definitions

Define all bindings in a central type:

// types/bindings.ts
export type Env = {
  Bindings: {
    // D1 Database
    DB: D1Database

    // KV Namespace
    KV: KVNamespace

    // R2 Bucket
    BUCKET: R2Bucket

    // Durable Object
    COUNTER: DurableObjectNamespace

    // Environment Variables
    ENVIRONMENT: 'development' | 'staging' | 'production'
    API_KEY: string
    JWT_SECRET: string
  }
  Variables: {
    user: User
    requestId: string
  }
}

D1 Database

Basic Queries

app.get('/users', async (c) => {
  const db = c.env.DB

  // Select all
  const { results } = await db
    .prepare('SELECT * FROM users WHERE deleted_at IS NULL')
    .all()

  return c.json({ data: results })
})

app.get('/users/:id', async (c) => {
  const db = c.env.DB
  const id = c.req.param('id')

  // Select one
  const user = await db
    .prepare('SELECT * FROM users WHERE id = ?')
    .bind(id)
    .first()

  if (!user) {
    return c.json({ error: 'User not found' }, 404)
  }

  return c.json({ data: user })
})

Insert and Update

app.post('/users', async (c) => {
  const db = c.env.DB
  const { email, name } = c.req.valid('json')
  const id = crypto.randomUUID()

  const result = await db
    .prepare('INSERT INTO users (id, email, name) VALUES (?, ?, ?)')
    .bind(id, email, name)
    .run()

  return c.json({ data: { id, email, name } }, 201)
})

app.put('/users/:id', async (c) => {
  const db = c.env.DB
  const id = c.req.param('id')
  const { name } = c.req.valid('json')

  await db
    .prepare('UPDATE users SET name = ?, updated_at = datetime("now") WHERE id = ?')
    .bind(name, id)
    .run()

  return c.json({ data: { id, name } })
})

Transactions (Batch)

app.post('/transfer', async (c) => {
  const db = c.env.DB
  const { fromId, toId, amount } = c.req.valid('json')

  // D1 batch for transaction-like behavior
  const results = await db.batch([
    db.prepare('UPDATE accounts SET balance = balance - ? WHERE id = ?')
      .bind(amount, fromId),
    db.prepare('UPDATE accounts SET balance = balance + ? WHERE id = ?')
      .bind(amount, toId),
    db.prepare('INSERT INTO transfers (from_id, to_id, amount) VALUES (?, ?, ?)')
      .bind(fromId, toId, amount)
  ])

  return c.json({ success: true })
})

Pagination

app.get('/users', async (c) => {
  const db = c.env.DB
  const { page, limit } = c.req.valid('query')
  const offset = (page - 1) * limit

  const [users, countResult] = await Promise.all([
    db.prepare('SELECT * FROM users LIMIT ? OFFSET ?')
      .bind(limit, offset)
      .all(),
    db.prepare('SELECT COUNT(*) as total FROM users')
      .first<{ total: number }>()
  ])

  return c.json({
    data: users.results,
    meta: {
      page,
      limit,
      total: countResult?.total || 0
    }
  })
})

KV Namespace

Basic Operations

// Get
app.get('/cache/:key', async (c) => {
  const kv = c.env.KV
  const key = c.req.param('key')

  const value = await kv.get(key)

  if (!value) {
    return c.json({ error: 'Not found' }, 404)
  }

  return c.json({ data: value })
})

// Get as JSON
const data = await kv.get('config', 'json')

// Get with metadata
const { value, metadata } = await kv.getWithMetadata('key')

// Put
await kv.put('key', 'value')

// Put with TTL (seconds)
await kv.put('session', token, { expirationTtl: 3600 })

// Put with expiration (Unix timestamp)
await kv.put('temp', data, { expiration: Date.now() / 1000 + 3600 })

// Put with metadata
await kv.put('user:123', JSON.stringify(user), {
  metadata: { createdAt: Date.now() }
})

// Delete
await kv.delete('key')

// List keys
const { keys } = await kv.list({ prefix: 'user:' })

Caching Pattern

app.get('/expensive-data', async (c) => {
  const kv = c.env.KV
  const cacheKey = 'expensive-data'

  // Try cache first
  const cached = await kv.get(cacheKey, 'json')
  if (cached) {
    return c.json({ data: cached, cached: true })
  }

  // Compute expensive data
  const data = await computeExpensiveData()

  // Cache for 5 minutes
  await kv.put(cacheKey, JSON.stringify(data), { expirationTtl: 300 })

  return c.json({ data, cached: false })
})

Rate Limiting

const rateLimiter = createMiddleware<Env>(async (c, next) => {
  const kv = c.env.KV
  const ip = c.req.header('CF-Connecting-IP') || 'unknown'
  const key = `ratelimit:${ip}`

  const count = parseInt(await kv.get(key) || '0')

  if (count >= 100) {
    return c.json({ error: 'Rate limit exceeded' }, 429)
  }

  await kv.put(key, String(count + 1), { expirationTtl: 60 })

  await next()
})

R2 Bucket

Upload Files

app.post('/upload', async (c) => {
  const bucket = c.env.BUCKET
  const body = await c.req.parseBody()
  const file = body['file'] as File

  if (!file) {
    return c.json({ error: 'No file provided' }, 400)
  }

  const key = `uploads/${crypto.randomUUID()}-${file.name}`

  await bucket.put(key, file.stream(), {
    httpMetadata: {
      contentType: file.type
    },
    customMetadata: {
      originalName: file.name,
      uploadedAt: new Date().toISOString()
    }
  })

  return c.json({ key }, 201)
})

Download Files

app.get('/files/:key', async (c) => {
  const bucket = c.env.BUCKET
  const key = c.req.param('key')

  const object = await bucket.get(key)

  if (!object) {
    return c.json({ error: 'File not found' }, 404)
  }

  const headers = new Headers()
  headers.set('Content-Type', object.httpMetadata?.contentType || 'application/octet-stream')
  headers.set('Content-Length', String(object.size))

  return new Response(object.body, { headers })
})

List Files

app.get('/files', async (c) => {
  const bucket = c.env.BUCKET
  const prefix = c.req.query('prefix') || ''

  const listed = await bucket.list({
    prefix,
    limit: 100
  })

  return c.json({
    data: listed.objects.map(obj => ({
      key: obj.key,
      size: obj.size,
      uploaded: obj.uploaded
    }))
  })
})

Delete Files

app.delete('/files/:key', async (c) => {
  const bucket = c.env.BUCKET
  const key = c.req.param('key')

  await bucket.delete(key)

  return c.body(null, 204)
})

Environment Variables

Access in Handlers

app.get('/config', (c) => {
  const env = c.env.ENVIRONMENT

  return c.json({
    environment: env,
    features: {
      debug: env === 'development'
    }
  })
})

Secrets in Middleware

const authMiddleware = createMiddleware<Env>(async (c, next) => {
  const token = c.req.header('Authorization')?.replace('Bearer ', '')

  if (!token) {
    throw new HTTPException(401, { message: 'Missing token' })
  }

  // Use secret from environment
  const payload = await verify(token, c.env.JWT_SECRET)
  c.set('user', payload)

  await next()
})

wrangler.toml Configuration

name = "my-app"
main = "src/index.ts"
compatibility_date = "2024-01-01"

[vars]
ENVIRONMENT = "production"

[[d1_databases]]
binding = "DB"
database_name = "my-app-db"
database_id = "xxxx-xxxx-xxxx"

[[kv_namespaces]]
binding = "KV"
id = "xxxx-xxxx-xxxx"

[[r2_buckets]]
binding = "BUCKET"
bucket_name = "my-app-bucket"

# Secrets are set via wrangler secret put
# wrangler secret put JWT_SECRET

Local Development

.dev.vars (for local secrets):

JWT_SECRET=dev-secret-key
API_KEY=dev-api-key
# Run with local D1
wrangler d1 create my-app-db --local

# Run with local KV
wrangler kv:namespace create KV --local

# Start dev server
wrangler dev

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.