Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/cloudflare/vinext/llms.txt

Use this file to discover all available pages before exploring further.

Cloudflare Bindings

Cloudflare Workers provide bindings to access platform resources like KV, D1, R2, Durable Objects, AI, and more. vinext applications can access these bindings directly in Server Components, Server Actions, Route Handlers, and middleware.

Overview

Bindings are environment variables of special types provided by the Cloudflare Workers runtime. They’re configured in wrangler.jsonc and accessed via the env parameter.

Binding Types

KV (Key-Value Store)

Globally distributed, eventually consistent key-value storage.
{
  "kv_namespaces": [
    { "binding": "MY_KV", "id": "abc123" }
  ]
}
interface Env {
  MY_KV: KVNamespace
}

export default {
  async fetch(request: Request, env: Env) {
    // Access KV
    await env.MY_KV.put('key', 'value')
    const value = await env.MY_KV.get('key')
    
    return handler.fetch(request)
  }
}
See KV Cache Handler for ISR integration.

D1 (SQL Database)

Serverless SQL database built on SQLite.
{
  "d1_databases": [
    { "binding": "DB", "database_id": "xyz789" }
  ]
}
import { getEnv } from 'vinext/cloudflare'

export async function GET() {
  const env = getEnv()
  const { results } = await env.DB.prepare('SELECT * FROM users').all()
  return Response.json(results)
}

R2 (Object Storage)

S3-compatible object storage.
{
  "r2_buckets": [
    { "binding": "MY_BUCKET", "bucket_name": "my-bucket" }
  ]
}
'use server'

import { getEnv } from 'vinext/cloudflare'

export async function uploadFile(formData: FormData) {
  const env = getEnv()
  const file = formData.get('file') as File
  const buffer = await file.arrayBuffer()
  
  await env.MY_BUCKET.put(file.name, buffer, {
    httpMetadata: {
      contentType: file.type
    }
  })
}

Durable Objects

Stateful serverless objects with strong consistency.
{
  "durable_objects": {
    "bindings": [
      { "name": "COUNTER", "class_name": "Counter" }
    ]
  }
}
import { getEnv } from 'vinext/cloudflare'

export async function GET() {
  const env = getEnv()
  const id = env.COUNTER.idFromName('global')
  const stub = env.COUNTER.get(id)
  const count = await stub.fetch('https://fake/increment').then(r => r.text())
  return Response.json({ count: parseInt(count) })
}

AI (Workers AI)

Serverless GPU inference for LLMs and other AI models.
{
  "ai": {
    "binding": "AI"
  }
}
import { getEnv } from 'vinext/cloudflare'

export async function POST(request: Request) {
  const env = getEnv()
  const { messages } = await request.json()
  
  const response = await env.AI.run('@cf/meta/llama-3.1-8b-instruct', {
    messages
  })
  
  return Response.json(response)
}

Vectorize (Vector Database)

Vector embeddings storage and similarity search.
{
  "vectorize": [
    { "binding": "VECTORIZE", "index_name": "my-index" }
  ]
}
import { getEnv } from 'vinext/cloudflare'

export async function POST(request: Request) {
  const env = getEnv()
  const { query } = await request.json()
  
  // Generate embedding
  const { data } = await env.AI.run('@cf/baai/bge-base-en-v1.5', {
    text: query
  })
  
  // Search vectors
  const results = await env.VECTORIZE.query(data[0], { topK: 10 })
  return Response.json(results)
}

Queues (Message Queue)

Asynchronous message queues.
{
  "queues": {
    "producers": [
      { "binding": "MY_QUEUE", "queue": "my-queue" }
    ]
  }
}
'use server'

import { getEnv } from 'vinext/cloudflare'

export async function sendEmail(to: string, subject: string, body: string) {
  const env = getEnv()
  await env.MY_QUEUE.send({ to, subject, body })
}

Service Bindings

Call other Workers directly.
{
  "services": [
    { "binding": "AUTH_SERVICE", "service": "auth-worker" }
  ]
}
import { getEnv } from 'vinext/cloudflare'

export async function POST(request: Request) {
  const env = getEnv()
  const { username, password } = await request.json()
  
  // Call auth worker
  const response = await env.AUTH_SERVICE.fetch('https://fake/verify', {
    method: 'POST',
    body: JSON.stringify({ username, password })
  })
  
  return response
}

ASSETS (Static Assets)

Access static files from the Workers Assets binding.
{
  "assets": {
    "directory": "dist/client",
    "binding": "ASSETS"
  }
}
The ASSETS binding is used internally by vinext for static file serving and image optimization. You typically don’t access it directly.

IMAGES (Cloudflare Images)

On-the-fly image resizing and transcoding.
{
  "images": {
    "binding": "IMAGES"
  }
}
See Image Optimization for usage.

Accessing Bindings

Worker Entry

Bindings are passed to your worker’s fetch handler:
import handler from 'vinext/server/app-router-entry'

interface Env {
  MY_KV: KVNamespace
  DB: D1Database
  MY_BUCKET: R2Bucket
  AI: Ai
}

export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    // Bindings are available in env
    const value = await env.MY_KV.get('key')
    
    return handler.fetch(request, env, ctx)
  }
}

Server Components

Use getEnv() to access bindings in Server Components:
import { getEnv } from 'vinext/cloudflare'

export default async function Posts() {
  const env = getEnv()
  const { results } = await env.DB.prepare('SELECT * FROM posts').all()
  
  return (
    <div>
      {results.map((post: any) => (
        <article key={post.id}>{post.title}</article>
      ))}
    </div>
  )
}

Server Actions

'use server'

import { getEnv } from 'vinext/cloudflare'

export async function createPost(formData: FormData) {
  const env = getEnv()
  const title = formData.get('title')
  const content = formData.get('content')
  
  await env.DB.prepare('INSERT INTO posts (title, content) VALUES (?, ?)')
    .bind(title, content)
    .run()
}

Route Handlers

import { getEnv } from 'vinext/cloudflare'

export async function GET() {
  const env = getEnv()
  const { results } = await env.DB.prepare('SELECT * FROM posts').all()
  return Response.json(results)
}

Middleware

import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
import { getEnv } from 'vinext/cloudflare'

export async function middleware(request: NextRequest) {
  const env = getEnv()
  const sessionId = request.cookies.get('session')?.value
  
  if (sessionId) {
    const session = await env.MY_KV.get(`session:${sessionId}`)
    if (session) {
      return NextResponse.next()
    }
  }
  
  return NextResponse.redirect(new URL('/login', request.url))
}

Type Safety

Define your Env interface once and use it everywhere:
export interface Env {
  // KV
  MY_KV: KVNamespace
  VINEXT_CACHE: KVNamespace
  
  // D1
  DB: D1Database
  
  // R2
  MY_BUCKET: R2Bucket
  
  // AI
  AI: Ai
  
  // Vectorize
  VECTORIZE: VectorizeIndex
  
  // Queues
  MY_QUEUE: Queue
  
  // Service bindings
  AUTH_SERVICE: Fetcher
  
  // Images
  IMAGES: any
  
  // Assets
  ASSETS: Fetcher
}
import type { Env } from '../types/env'
import handler from 'vinext/server/app-router-entry'

export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext) {
    return handler.fetch(request, env, ctx)
  }
}
Now TypeScript enforces correct binding types throughout your app.

getEnv() Implementation

The getEnv() helper uses AsyncLocalStorage to access bindings from anywhere in your app:
import { getEnv } from 'vinext/cloudflare'

const env = getEnv() // Returns Env from current request context
If called outside a request context, it throws an error.

Common Patterns

Database Connection Pool

D1 doesn’t require connection pooling (it’s serverless), but you can create a helper:
import { getEnv } from 'vinext/cloudflare'

export function getDB() {
  return getEnv().DB
}
import { getDB } from '@/lib/db'

export default async function Posts() {
  const db = getDB()
  const { results } = await db.prepare('SELECT * FROM posts').all()
  return <div>{/* ... */}</div>
}

Multi-Tenant KV

Use key prefixes for multi-tenant isolation:
import { getEnv } from 'vinext/cloudflare'

export function getTenantKV(tenantId: string) {
  const kv = getEnv().MY_KV
  
  return {
    async get(key: string) {
      return kv.get(`${tenantId}:${key}`)
    },
    async put(key: string, value: string) {
      return kv.put(`${tenantId}:${key}`, value)
    },
    async delete(key: string) {
      return kv.delete(`${tenantId}:${key}`)
    }
  }
}

Caching Wrapper

Wrap expensive operations with KV caching:
import { getEnv } from 'vinext/cloudflare'

export async function cached<T>(
  key: string,
  fn: () => Promise<T>,
  ttl: number = 3600
): Promise<T> {
  const kv = getEnv().MY_KV
  
  // Try cache
  const cached = await kv.get(key, 'json')
  if (cached) return cached as T
  
  // Compute
  const result = await fn()
  
  // Store
  await kv.put(key, JSON.stringify(result), { expirationTtl: ttl })
  
  return result
}
import { cached } from '@/lib/cache'

export default async function Posts() {
  const posts = await cached('posts', async () => {
    const response = await fetch('https://api.example.com/posts')
    return response.json()
  }, 60)
  
  return <div>{/* ... */}</div>
}

Development vs Production

Local Development

Use wrangler dev to access bindings locally:
wrangler dev
This starts a local Workers runtime with your configured bindings. D1 uses SQLite locally, KV uses in-memory storage.

vinext dev

The vinext dev command runs Vite’s dev server without Workers bindings. To test with bindings locally:
  1. Build the app: vinext build
  2. Run wrangler dev: wrangler dev
Or use the vinext deploy preview:
vinext deploy --preview

Examples

Full-Stack D1 App

import { getEnv } from 'vinext/cloudflare'
import { revalidatePath } from 'next/cache'

export default async function Todos() {
  const env = getEnv()
  const { results: todos } = await env.DB.prepare('SELECT * FROM todos').all()
  
  async function createTodo(formData: FormData) {
    'use server'
    const env = getEnv()
    const title = formData.get('title')
    await env.DB.prepare('INSERT INTO todos (title) VALUES (?)').bind(title).run()
    revalidatePath('/todos')
  }
  
  return (
    <div>
      <form action={createTodo}>
        <input name="title" required />
        <button>Add</button>
      </form>
      <ul>
        {todos.map((todo: any) => (
          <li key={todo.id}>{todo.title}</li>
        ))}
      </ul>
    </div>
  )
}
import { getEnv } from 'vinext/cloudflare'

export default async function Search({
  searchParams
}: {
  searchParams: Promise<{ q?: string }>
}) {
  const { q } = await searchParams
  if (!q) return <div>Enter a search query</div>
  
  const env = getEnv()
  
  // Generate embedding
  const { data } = await env.AI.run('@cf/baai/bge-base-en-v1.5', { text: q })
  
  // Search vectors
  const results = await env.VECTORIZE.query(data[0], { topK: 5 })
  
  return (
    <div>
      <h1>Results for "{q}"</h1>
      <ul>
        {results.matches.map((match: any) => (
          <li key={match.id}>{match.metadata.title}</li>
        ))}
      </ul>
    </div>
  )
}