# How to Set Up a Local Development Environment for OpenSEO Using TanStack Start

> Learn to set up your OpenSEO local development environment with TanStack Start. Follow simple steps to install dependencies, configure credentials, and run the dev server.

- Repository: [Every App/open-seo](https://github.com/every-app/open-seo)
- Tags: getting-started
- Published: 2026-08-15

---

**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)](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`](https://github.com/every-app/open-seo/blob/main/package.json).

Run the following command to enable Corepack:

```bash
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:

```bash
pnpm install --frozen-lockfile

```

This command respects the exact versions defined in [`pnpm-lock.yaml`](https://github.com/every-app/open-seo/blob/main/pnpm-lock.yaml), ensuring consistency across environments.

### Database Migration

OpenSEO uses **SQLite D1** as the default local database. Initialize it with:

```bash
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)](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:

```bash
cp .env.example .env.local

```

Edit `.env.local` to include these required variables:

- **`DATAFORSEO_API_KEY`**: Base64-encoded `login:password` string (e.g., `printf '%s' 'mylogin:mysecret' | base64`)
- **`AUTH_MODE`**: Set to `local_noauth` to bypass Cloudflare Access and inject a dummy `admin@localhost` user

## Start the Development Server

OpenSEO provides two development scripts in [`package.json`](https://github.com/every-app/open-seo/blob/main/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:

```bash
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)](https://github.com/every-app/open-seo/blob/main/web/vite.config.ts), the `tanstackStart` plugin generates the server entry and enables prerendering:

```typescript
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`](https://github.com/every-app/open-seo/blob/main/web/wrangler.jsonc) file declares the Cloudflare Workers configuration:

```json
{
  "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)](https://github.com/every-app/open-seo/blob/main/web/src/routes/_marketing/index.tsx):

```typescript
import { createFileRoute } from "@tanstack/react-router";

export const Route = createFileRoute("/_marketing")({
  component: () => <MarketingHome />,
});

```

TanStack Router automatically generates the route tree (see [`routeTree.gen.ts`](https://github.com/every-app/open-seo/blob/main/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)](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:

```typescript
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:

```bash
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)](https://github.com/every-app/open-seo/blob/main/web/vite.config.ts) and [`web/wrangler.jsonc`](https://github.com/every-app/open-seo/blob/main/web/wrangler.jsonc), with setup instructions in [[`docs/LOCAL_DEVELOPMENT.md`](https://github.com/every-app/open-seo/blob/main/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_noauth` and `DATAFORSEO_API_KEY` in `.env.local` before starting the server
- **Development**: Use `pnpm dev:agents` for 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)](https://github.com/every-app/open-seo/blob/main/docs/LOCAL_POSTGRES.md), including connection string setup and migration commands.