How to Set Up a Local Development Environment for OpenSEO Using TanStack Start
Setting up OpenSEO locally requires Node 20+, Corepack for pnpm management, DataForSEO API credentials, and running pnpm dev:agents after installing dependencies and migrating the SQLite database.
OpenSEO is a full-stack SEO application built on TanStack Start, a zero-config framework combining Vite, React 19, and TanStack Router. This guide provides the exact steps to set up a local development environment for OpenSEO using TanStack Start, including architecture details from the source code and the specific commands needed to run the Cloudflare Workers-based development server.
Prerequisites
Before cloning the repository, ensure your system meets the runtime requirements defined in [docs/LOCAL_DEVELOPMENT.md](https://github.com/every-app/open-seo/blob/main/docs/LOCAL_DEVELOPMENT.md).
Node.js and Corepack
You need Node.js 20 or higher installed on your machine. The project uses Corepack (bundled with Node.js 24+) to manage the exact pnpm version declared in package.json.
Run the following command to enable Corepack:
corepack enable
This ensures the pnpm binary matches the packageManager field (pnpm@10.30.1). Using a globally installed pnpm version that differs from the lockfile will cause installation errors.
DataForSEO Account
Every API request in OpenSEO requires DataForSEO credentials. You must create an account and generate API keys, as these are not optional for local development.
Install Dependencies and Configure the Environment
Once prerequisites are met, install the project dependencies and prepare the local configuration.
Lockfile Installation
Navigate to the project root and install dependencies using the frozen lockfile:
pnpm install --frozen-lockfile
This command respects the exact versions defined in pnpm-lock.yaml, ensuring consistency across environments.
Database Migration
OpenSEO uses SQLite D1 as the default local database. Initialize it with:
pnpm run db:migrate:local
This creates the local database file and applies all existing schema migrations. For PostgreSQL support (optional), see [docs/LOCAL_POSTGRES.md](https://github.com/every-app/open-seo/blob/main/docs/LOCAL_POSTGRES.md).
Environment Variables
Copy the example environment file and configure your local secrets:
cp .env.example .env.local
Edit .env.local to include these required variables:
DATAFORSEO_API_KEY: Base64-encodedlogin:passwordstring (e.g.,printf '%s' 'mylogin:mysecret' | base64)AUTH_MODE: Set tolocal_noauthto bypass Cloudflare Access and inject a dummyadmin@localhostuser
Start the Development Server
OpenSEO provides two development scripts in package.json:
| Script | Purpose |
|---|---|
pnpm run dev |
Starts Vite on port 4322 (standard mode) |
pnpm dev:agents |
Starts Vite through portless with logging to .logs/dev-server.log (recommended for AI agents and worktrees) |
Run the recommended command:
mkdir -p .logs && pnpm dev:agents
The application launches at http://open-seo.localhost:1355. When using git worktrees, portless automatically prefixes the branch name (e.g., http://feature-x.open-seo.localhost:1355).
TanStack Start Architecture
Understanding how TanStack Start powers OpenSEO helps debug configuration issues and extend the application.
Vite Configuration
In [web/vite.config.ts](https://github.com/every-app/open-seo/blob/main/web/vite.config.ts), the tanstackStart plugin generates the server entry and enables prerendering:
import { tanstackStart } from "@tanstack/react-start/plugin/vite";
export default defineConfig({
plugins: [
tanstackStart({
prerender: {
enabled: true,
filter: ({ path }) => !/\.pdf(?:[?#]|$)/i.test(path),
},
}),
react(),
],
});
The plugin generates @tanstack/react-start/server-entry and wires up the TanStack Router. The prerender configuration excludes PDF routes from static generation.
Worker Entry Point
The web/wrangler.jsonc file declares the Cloudflare Workers configuration:
{
"main": "@tanstack/react-start/server-entry",
"assets": {
"directory": "./dist/client",
"html_handling": "drop-trailing-slash"
},
"kv_namespaces": [{ "binding": "BACKLINK_CHECK_KV", "id": "..." }],
"ratelimits": [{ "name": "BACKLINK_CHECK_RATE_LIMIT", "namespace_id": "1001", "simple": { "limit": 5, "period": 60 } }]
}
The main field points to the generated server entry, while KV namespaces and rate limits are configured but sandboxed during local development.
File-Based Routing
Routes are defined using createFileRoute from @tanstack/react-router. For example, in [web/src/routes/_marketing/index.tsx](https://github.com/every-app/open-seo/blob/main/web/src/routes/_marketing/index.tsx):
import { createFileRoute } from "@tanstack/react-router";
export const Route = createFileRoute("/_marketing")({
component: () => <MarketingHome />,
});
TanStack Router automatically generates the route tree (see routeTree.gen.ts), providing type-safe parameters and client-side navigation without boilerplate.
Server Functions
Secure server-side logic uses createServerFn. In [web/src/lib/content.functions.ts](https://github.com/every-app/open-seo/blob/main/web/src/lib/content.functions.ts), functions fetch DataForSEO data while keeping API keys server-side:
import { createServerFn } from "@tanstack/react-start";
export const fetchKeywordMetrics = createServerFn(
"fetchKeywordMetrics",
async (keyword: string) => {
const resp = await fetch(`https://api.dataforseo.com/v3/keyword_metrics?kw=${keyword}`, {
headers: { Authorization: `Basic ${process.env.DATAFORSEO_API_KEY}` },
});
return resp.json();
},
);
These functions are called from client components but execute on the worker runtime, protecting sensitive credentials.
Debugging and Logs
When using pnpm dev:agents, logs stream to .logs/dev-server.log. This file captures Cloudflare Worker startup errors and runtime output, useful for troubleshooting or providing context to AI coding agents.
Rotate the log file manually if it grows large:
mv .logs/dev-server.log .logs/dev-server-$(date +%s).log
Summary
- Runtime requirements: Node.js 20+, Corepack-enabled pnpm, and DataForSEO API credentials are mandatory
- Source files: Configuration lives in [
web/vite.config.ts](https://github.com/every-app/open-seo/blob/main/web/vite.config.ts) andweb/wrangler.jsonc, with setup instructions in [docs/LOCAL_DEVELOPMENT.md](https://github.com/every-app/open-seo/blob/main/docs/LOCAL_DEVELOPMENT.md) - Database: SQLite D1 runs locally via
pnpm run db:migrate:local, with optional PostgreSQL support available - Environment: Set
AUTH_MODE=local_noauthandDATAFORSEO_API_KEYin.env.localbefore starting the server - Development: Use
pnpm dev:agentsfor the best local experience with automatic logging and portless subdomain support
Frequently Asked Questions
What are the minimum Node.js requirements for OpenSEO?
OpenSEO requires Node.js 20 or higher. While the project runs on Node 20+, using Node 24+ provides native Corepack support, simplifying pnpm version management through the corepack enable command.
How do I obtain DataForSEO API credentials?
You must create an account at DataForSEO and generate API keys from their dashboard. The credentials require Base64 encoding (login:password format) and must be stored in the DATAFORSEO_API_KEY environment variable in your .env.local file.
What is the difference between pnpm dev and pnpm dev:agents?
pnpm dev starts the standard Vite development server on port 4322. pnpm dev:agents (recommended) starts the server through portless, which provides stable logging to .logs/dev-server.log and supports automatic subdomain routing for git worktrees (e.g., feature-branch.open-seo.localhost:1355).
Can I use PostgreSQL instead of SQLite for local development?
Yes, while SQLite D1 is the default (pnpm run db:migrate:local), OpenSEO supports PostgreSQL as an opt-in alternative. Configuration details are documented in [docs/LOCAL_POSTGRES.md](https://github.com/every-app/open-seo/blob/main/docs/LOCAL_POSTGRES.md), including connection string setup and migration commands.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →