Skip to content
On this pageStarting the Dev Server
  1. Starting the Dev Server
  2. Miniflare: Local Cloud Simulation
  3. Hot Reload and Seed Changes
  4. TypeScript Type Generation
  5. MCP Server on Localhost
  6. Common Issues

EmDash Local Development Setup

Ben 3 min read

One of the things I appreciate most about EmDash is that the local development experience is complete. You don't need a Cloudflare account active, you don't need D1 or R2 provisioned, and you don't need environment variables configured. One command gets you a fully running CMS with admin panel, content API, and MCP server — all on localhost.

Here's exactly what happens when you run the dev server and how each piece fits together.

Starting the Dev Server

Everything starts with a single command:

Terminal
npx emdash dev

That command runs migrations, seeds demo content if the database is empty, generates TypeScript types, starts Miniflare with D1 and R2 emulation, and launches the Astro dev server with hot reload. The admin panel comes up at http://localhost:4321/_emdash/admin.

StepWhat it does
MigrationsCreates or updates the SQLite schema to match your collections
SeedingApplies seed.json if the database is empty — collections, taxonomies, menus, demo content
Type generationWrites emdash-env.d.ts with TypeScript types for all collections and fields
MiniflareStarts a local Worker runtime with D1 (SQLite file) and R2 (local filesystem) bindings
Dev serverAstro dev server starts with hot reload for templates and components

Miniflare: Local Cloud Simulation

Miniflare is Cloudflare's official local development runtime. It runs your Worker code inside a V8 isolate — the same runtime as production. D1 is simulated using a local SQLite file (data.db in your project root). R2 is simulated using your local filesystem. Media uploads go to a local directory instead of cloud storage.

The implication: the code that runs locally is identical to the code that runs in production. There's no "works on my machine" gap caused by environment differences. If it works in Miniflare, it works on Cloudflare.

The same code runs locally and in production. No Docker, no cloud services, no environment variables to configure.

Hot Reload and Seed Changes

Template and component changes hot-reload immediately — the browser updates without a full page refresh. Changes to seed.json require a server restart because seeding runs at startup, not continuously. If you add a new collection or change a field type, stop the dev server, make your seed changes, and restart.

Content you create through the admin panel during development persists in data.db. The seed only runs when the database is empty — so adding content in the admin doesn't get overwritten on restart.

TypeScript Type Generation

EmDash generates emdash-env.d.ts at startup. This file contains TypeScript types for every collection and field, so you get autocomplete and type checking when querying content. If the types look wrong or incomplete — which can happen on first run — delete the file and restart:

MCP Server on Localhost

The EmDash MCP server runs alongside the dev server at localhost:4321. It exposes 33 tools for content management, schema operations, media, search, taxonomies, and menus — all callable from AI agents without leaving the terminal.

Configure it in .mcp.json at your project root. The file is gitignored because it contains an auth token. You'll need to generate a token through the admin panel under Settings > API Keys.

Common Issues

Types wrong or missing after startup: Delete emdash-env.d.ts and restart npx emdash dev. Type generation runs after seeding, and occasionally the file gets written before the schema is fully resolved.

MCP tools 500-error in workerd: This is a known v0.1 bug. The fix requires @modelcontextprotocol/sdk as a direct dependency and three subpath imports added to vite.ssr.optimizeDeps.include in astro.config.mjs. This site has the workaround applied.

Admin wizard reappears on restart: This means data.db was deleted or corrupted. The database holds all content, accounts, and settings — treat it like production data during local development. Don't delete it unless you're intentionally resetting.

Deploy to production →