OpenSEO Project Structure Explained: Full-Stack TypeScript Architecture Guide

The OpenSEO project is a full-stack TypeScript application built on TanStack React-Start and Drizzle ORM, organized into clear layers for entry points, server handlers, routing, API surfaces, workflows, and database access.

This comprehensive guide examines how the OpenSEO project structure separates concerns across UI, server logic, data models, and infrastructure. Whether you're contributing to the codebase, self-hosting, or studying modern full-stack patterns, understanding this architecture reveals how the every-app/open-seo repository delivers SEO automation through a type-safe, maintainable design.

Entry Point and Application Bootstrap

The application boots from src/start.ts, which creates the TanStack React-Start instance with mandatory middleware.

// src/start.ts – creates startInstance with CSRF middleware
import { createStart, createCsrfMiddleware } from '@tanstack/react-start';

export const startInstance = createStart({
  middleware: [createCsrfMiddleware()],
});

This file registers globalServerFunctionMiddleware, ensuring all server functions execute with consistent request wrapping and security headers before reaching business logic.

Server Handler and Request Routing

The Cloudflare Workers entry point lives in src/server.ts. This handler determines authentication mode and routes requests across four distinct paths.

Three authentication modes are supported:

  • hosted – full SaaS with OAuth
  • cloudflare_access – Cloudflare Access integration
  • local_noauth – development bypass

The fetch function in src/server.ts implements this routing logic:

Route prefix Handler Destination
/agents/* routeChatAgents Durable Object chat agents
/auth/* openSeoOAuthProvider Hosted OAuth flow
/api/gsc/oauth/callback, /api/* handleSelfHostedOpenSeoMcpRequest Self-hosted MCP
All other paths appFetch TanStack React UI

Each request wraps the PostgreSQL client via withPgClient, ensuring database connections are managed consistently across all execution paths.

Typed Routing System

Navigation relies on src/routeTree.gen.ts, an auto-generated file providing compile-time type safety for every route.

// Using the typed route tree in components
import { useNavigate } from '@tanstack/react-router';
import { routeTree } from '@/routeTree.gen';

function ProjectNavigation() {
  const navigate = useNavigate();
  
  // TypeScript ensures 'routeTree.projects' exists
  return (
    <button onClick={() => navigate({ to: routeTree.projects })}>
      View Projects
    </button>
  );
}

Route files map directly to src/routes/..., with the generator ensuring any file addition or rename propagates type changes immediately.

API Surface and Server Functions

The MCP (Meta Control Plane) exposes REST-like endpoints through files in src/serverFunctions/*.ts. These functions execute in the same Worker context as the requesting UI, sharing database connections and authentication state.

Key server function modules:

// Calling a server function from client code
import { $keywords } from '@/serverFunctions/keywords';

async function fetchSuggestions(query: string) {
  // Executes in Worker, returns typed response
  const result = await $keywords.search({ query });
  return result.suggestions;
}

The $ prefix convention distinguishes server-callable functions from regular utilities, with TypeScript enforcing parameter and return types across the network boundary.

Background Workflows

Long-running operations execute as workflow classes in src/server/workflows/*.ts. These handle CPU-intensive or time-delayed tasks without blocking user requests.

Core workflow implementations:

Workflows instantiate via direct API calls or the Worker's scheduled export in src/server.ts, enabling cron-like execution for recurring SEO monitoring.

// Scheduling a rank check from server code
import { runScheduledRankChecks } from '@/server/features/rank-tracking/services/scheduledRankChecks';

export const $rankTracking = {
  async schedule(projectId: string) {
    await runScheduledRankChecks({ PROJECT_ID: projectId });
    return { status: 'queued' };
  },
};

Database Layer and Schema

Drizzle ORM provides type-safe database access with dual-target support for SQLite (Cloudflare D1) and PostgreSQL.

Component Location Purpose
Core schema src/db/schema.ts Table definitions shared across dialects
Postgres client src/db/pg/client.ts Connection pooling and query execution
D1/SQLite adapter src/db/index.ts Edge runtime compatibility
Migrations drizzle-pg/ PostgreSQL-specific migration files

The schema duplication in drizzle-pg/ allows identical TypeScript types while optimizing for each database's capabilities—critical for supporting both Cloudflare's D1 (SQLite) in production and PostgreSQL in self-hosted deployments.

Shared Utilities and Authentication

Cross-cutting concerns live in dedicated directories with comprehensive test coverage.

Shared utilities (src/shared/*.ts):

Authentication modules (src/lib/auth-*.ts):

  • auth-config.ts – OAuth provider creation via createOpenSeoOAuthProvider
  • Turnstile captcha validation for bot protection
  • Session management across auth modes

Protected routes apply middleware from src/middleware/ensure-user/*.ts, enforcing authorization before handler execution.

Documentation, Testing, and Scripts

The repository includes operational guides and comprehensive test suites.

Directory Contents
docs/*.md Self-hosting guides including SELF_HOSTING_DOCKER.md
src/**/*.test.ts Unit tests for utilities and server functions
e2e/*.spec.ts Playwright end-to-end tests for critical user flows
scripts/*.ts Data seeding, migration utilities, and operational tools

Summary

  • OpenSEO project structure separates concerns across eight distinct layers: entry point, server handler, routing, API surface, workflows, database, shared utilities, and authentication
  • src/server.ts routes requests across agents, OAuth, MCP, and UI paths with withPgClient wrapping every database interaction
  • Server functions in src/serverFunctions/*.ts execute type-safe RPC calls within the same Worker context as the requesting UI
  • Background workflows in src/server/workflows/*.ts handle audits and rank checks without blocking user requests
  • Drizzle ORM with dual schema support enables identical types across D1 (SQLite) and PostgreSQL deployments
  • Auto-generated src/routeTree.gen.ts provides compile-time navigation safety throughout the React application

Frequently Asked Questions

What framework does OpenSEO use for its frontend and backend?

OpenSEO builds on TanStack React-Start for server-rendered React applications, with Drizzle ORM handling database operations. This combination provides type safety from database schema through API responses to UI components, all executing within Cloudflare Workers for edge deployment.

How does OpenSEO handle authentication for different deployment modes?

The src/server.ts handler determines authentication mode via getAuthMode, supporting three configurations: hosted for full SaaS OAuth, cloudflare_access for enterprise Cloudflare Access integration, and local_noauth for development environments. Each mode routes through appropriate handlers in src/lib/auth-*.ts files.

Where are API endpoints defined in the OpenSEO codebase?

API endpoints reside in src/serverFunctions/*.ts as TypeScript functions rather than traditional route handlers. These server functions—such as $keywords.search in src/serverFunctions/keywords.ts—are wrapped by globalServerFunctionMiddleware and callable from client code with full type safety via the $ prefix convention.

How does OpenSEO manage background jobs like site audits?

Long-running tasks execute as workflow classes in src/server/workflows/*.ts. The SiteAuditWorkflow.ts and RankCheckWorkflow.ts classes run either through direct invocation from server functions or via the scheduled export in src/server.ts for cron-triggered execution, leveraging Cloudflare Durable Objects for state persistence.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →