Analog
Analog is a Vite-based Angular meta-framework powered by Nitro. Add voidPlugin() to the Vite config for binding inference, typed DB, and migrations during development.
Setup
1. Create a new project
npx create-analog@latest my-app
cd my-app2. Install dependencies
npm install -D void wrangler nitro-cloudflare-devThe wrangler dependency is needed for local Cloudflare bindings. nitro-cloudflare-dev creates a Cloudflare platform proxy during dev 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.1-8b-instruct', {
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
Void gives you a typed env layer: declare keys in env.ts, read them via import { env } from "void/env", and get schema validation at build + deploy time plus a client-leak guard that fails the build if a server-only key reaches the browser. See the env vars guide for the full feature set.
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;
});