Analog
Use Void with Analog for resource detection, typed database queries, and migrations. Analog keeps its own build and Cloudflare integration; Void plugs into its Vite configuration.
Setup
1. Create a new project
npx create-analog@latest my-app
cd my-app2. Install dependencies
npm install -D void nitro-cloudflare-devVoid provides the local Cloudflare binding runtime. nitro-cloudflare-dev creates the platform proxy during development so that void/db, void/kv, and other runtime helpers can access bindings.
3. Configure vite.config.ts
import { resolve } from 'node:path';
import { defineConfig } from 'vite';
import analog from '@analogjs/platform';
import { voidPlugin } from 'void';
export default defineConfig({
resolve: {
mainFields: ['module'],
},
plugins: [
analog({
ssr: true,
nitro: {
preset: 'cloudflare-module',
modules: ['nitro-cloudflare-dev'],
alias: {
// Nitro has its own bundler that doesn't use Vite aliases.
// Duplicate @schema here so Nitro can resolve it.
'@schema': resolve(__dirname, 'db/schema.ts'),
},
},
}),
voidPlugin(),
],
});4. Create wrangler.jsonc
voidPlugin() auto-syncs inferred bindings into this file on dev startup:
{
"name": "my-app",
"compatibility_date": "2026-02-24",
"compatibility_flags": ["nodejs_compat", "nodejs_als"],
}nodejs_compat is required for Nitro's Cloudflare runtime. nodejs_als is required for void/* runtime helpers (e.g. void/db, void/kv).
5. Deploy
void auth login
void deployUsing Void Platform Features
Most Void platform features work with Analog. Use them in page server loaders, actions, and API routes:
Void-managed auth is not supported in framework mode yet. Use Better Auth's official integration directly for now.
Database
// src/app/pages/users.server.ts
import type { PageServerLoad } from '@analogjs/router';
import { db } from 'void/db';
import { users } from '@schema';
export const load = async ({ event }: PageServerLoad) => {
return { users: await db.select().from(users) };
};⚠️ Analog limitation
Analog uses Nitro, which bundles server routes outside of Vite's plugin pipeline. The schema cannot be injected into the db instance, so db.query.* relational queries are not available in Analog. Use the standard query builder API (db.select().from(table)) instead.
KV Storage
import type { PageServerLoad } from '@analogjs/router';
import { kv } from 'void/kv';
export const load = async ({ event }: PageServerLoad) => {
return { settings: await kv.get('app:settings') };
};Blob Storage
import type { PageServerLoad } from '@analogjs/router';
import { storage } from 'void/storage';
export const load = async ({ event }: PageServerLoad) => {
return { avatar: await storage.get('avatars/user-1.png') };
};AI
// src/server/routes/api/summarize.ts
import { eventHandler } from 'h3';
import { ai } from 'void/ai';
export default eventHandler(async () => {
return ai.run('@cf/meta/llama-3.3-70b-instruct-fp8-fast', {
prompt: 'Summarize the latest news',
});
});Cron Jobs
// crons/daily-cleanup.ts
import { defineScheduled } from 'void';
export const cron = '0 0 * * *';
export default defineScheduled(async () => {
// runs daily at midnight
});Queue Consumers
// queues/emails.ts
import { defineQueue } from 'void';
export default defineQueue<{ to: string; subject: string }>(async (batch) => {
for (const msg of batch.messages) {
// process each message
msg.ack();
}
});Environment Variables
Declare environment variables in env.ts, then read them with import { env } from "void/env". Void supplies types, checks values during build and deploy, and stops the build if client code references a server-only key. See Environment Variables.
void/env replaces the H3 event context and import { env } from "cloudflare:workers" for env-var access. Keep those around when you need raw binding access (D1, KV, R2, etc.).
Accessing Bindings Directly
You can also access raw Cloudflare bindings via the H3 event context or the cloudflare:workers module:
// src/server/routes/api/users.ts
import { eventHandler } from 'h3';
import { env } from 'cloudflare:workers';
export default eventHandler(async () => {
const { results } = await env.DB.prepare('SELECT * FROM users').all();
return results;
});