OpenSEO Rank Tracking Architecture: A Deep Dive into the Layered Implementation

OpenSEO implements rank tracking as a five-layer, type-safe architecture separating UI components, API validation, business logic, data persistence, and scheduled automation through Cloudflare Workers.

This article examines how the every-app/open-seo repository structures its rank tracking system, from the TanStack-powered frontend to the DataForSEO API integration. The architecture emphasizes clean separation of concerns with Zod validation, shared cost-calculation utilities, and Drizzle ORM for database operations.

Architecture Overview: Five Distinct Layers

OpenSEO's rank tracking architecture follows a predictable data flow through five specialized layers. Each layer has a single responsibility and communicates through well-defined interfaces.

Layer 1: Frontend and Routing

The user interface exposes rank tracking functionality through dedicated routes and feature navigation.

This TanStack Router-based approach ensures type-safe links between the navigation and page components.

Layer 2: API Validation with Zod

All incoming requests pass through strict schema validation before reaching business logic.

The schemas live in src/types/schemas/rank-tracking.ts and include:

  • createConfigSchema — validates new tracking configurations
  • triggerCheckSchema — validates rank check requests
  • Additional schemas for config updates and metric queries

Server functions apply these validators via createServerFn(...).validator(...) pattern.

Layer 3: Service Layer and Business Logic

The RankTrackingService class in src/server/features/rank-tracking/services/RankTrackingService.ts orchestrates all rank tracking operations. It coordinates:

  • Configuration CRUD operations
  • Live vs. queued check selection
  • Credit limit enforcement
  • Metric refresh scheduling

This service relies on shared utilities from src/shared/rank-tracking.ts for calculations that must remain consistent between server and client.

Layer 4: Repository and Data Access

Database operations are abstracted through RankTrackingRepository in src/server/features/rank-tracking/repositories/RankTrackingRepository.ts. This Drizzle ORM wrapper handles:

  • rank_tracking_configs table queries
  • Keyword history retrieval
  • Result matrix storage and lookup

The repository pattern allows the service layer to remain database-agnostic.

Layer 5: Scheduler and Cloudflare Workers

Periodic execution uses Cloudflare Workers with calculated next-run times. The scheduler:

  • Enqueues tasks via the waitUntil mechanism
  • Respects user-selected intervals (daily, weekly, monthly)
  • Handles drift compensation for delayed runs

How a Rank Check Executes: Step-by-Step Flow

Understanding the complete request lifecycle reveals how these layers interact:

  1. User initiates request — Frontend calls triggerRankCheck from src/serverFunctions/rank-tracking.ts

  2. Validation gate — Zod schema triggerCheckSchema parses the payload

  3. Service routing decisionRankTrackingService.triggerCheck selects:

    • Live endpoint — Instant results, higher cost
    • Queued endpoint — Batched processing, lower cost
  4. Credit estimationestimateRankCheckCredits calculates cost based on:

    • Keyword count
    • Device selection (desktop, mobile, or both)
    • SERP depth (number of result pages)
    • Method (live vs. queued)

    Exceeding the approved limit throws rankCheckCostApprovalError

  5. External API call — Service submits request to DataForSEO API

  6. Persistence — Response stored via RankTrackingRepository

  7. Optional metric refreshrefreshKeywordMetrics updates derived statistics

  8. UI query methods — Frontend retrieves data through:

    • getLatestRankResults
    • getRankKeywordHistory
    • getRankConfigTrend
    • getRankPositionMatrix

Shared Utilities: Cost and Scheduling Mathematics

The src/shared/rank-tracking.ts file contains pure functions used across both frontend and backend:

Credit Estimation Formula

// From estimateRankCheckCredits in src/shared/rank-tracking.ts
function estimateRankCheckCredits(params: {
  keywordCount: number;
  devices: DeviceType[];
  serpDepth: number;
  method: 'live' | 'queued';
}): { costUsd: number; costCredits: number }

The calculation applies:

  • LIVE_BASE_PAGE_COST_USD or QUEUED_BASE_PAGE_COST_USD per page
  • Multiplication by keyword-device pair count
  • Rounding with SEO_DATA_COST_MARKUP for credit conversion

Next-Run Time Computation

// From computeNextCheckAt in src/shared/rank-tracking.ts
function computeNextCheckAt(
  scheduleInterval: 'daily' | 'weekly' | 'monthly' | 'manual',
  fromDate: Date
): Date

Scheduling behavior:

  • Daily: Adds 24 hours
  • Weekly: Adds 7 days
  • Monthly: Adds ~30 days with random hour selection (04:00–09:00 UTC) for load distribution
  • Manual: Returns null (no automatic scheduling)

Key Source Files Reference

File Path Purpose
src/types/schemas/rank-tracking.ts Zod validation schemas for all rank tracking endpoints
src/shared/rank-tracking.ts Cost constants, estimation functions, scheduling logic, display utilities
src/serverFunctions/rank-tracking.ts TanStack Server Function wrappers exposing API to frontend
src/server/features/rank-tracking/services/RankTrackingService.ts Core business logic: configs, triggers, cost checks, metrics
src/server/features/rank-tracking/repositories/RankTrackingRepository.ts Drizzle ORM data access for rank tracking tables
web/src/routes/_marketing/features/rank-tracking.tsx React-based rank tracking dashboard page
web/src/lib/feature-pages.ts Navigation registration for "Rank Tracking" feature
web/src/routeTree.gen.ts Generated route tree including /features/rank-tracking

Design Decisions and Trade-offs

Type Safety Through Zod

Using Zod schemas at the API boundary catches validation errors before they reach business logic. The .validator() integration with TanStack Server Functions provides end-to-end type safety from client to database.

Shared Module Pattern

Placing estimateRankCheckCredits and computeNextCheckAt in src/shared/ rather than server-only allows:

  • Frontend cost previews before submission
  • Consistent calculation logic across environments
  • Reduced bundle overhead through tree-shaking

Credit-First Architecture

Cost estimation runs before any external API call, preventing:

  • Unexpected billing surprises
  • Failed requests due to insufficient credits
  • Manual refund workflows

The rankCheckCostApprovalError exception provides clear feedback when limits are exceeded.

Summary

OpenSEO's rank tracking architecture demonstrates several effective patterns for SaaS feature development:

  • Five-layer separation — UI, validation, service, repository, and scheduler each have distinct responsibilities
  • Type safety everywhere — Zod schemas and TanStack Server Functions eliminate runtime validation failures
  • Cost transparency — Shared estimation utilities provide accurate pricing before API calls execute
  • Cloud-native scheduling — Cloudflare Workers with calculated next-run times handle automation without dedicated infrastructure
  • Repository abstraction — Drizzle ORM queries remain isolated from business logic for database flexibility

Frequently Asked Questions

How does OpenSEO estimate rank check costs before execution?

estimateRankCheckCredits in src/shared/rank-tracking.ts multiplies per-page DataForSEO pricing constants by the number of keyword-device pairs and SERP depth, then applies the SEO_DATA_COST_MARKUP multiplier. This runs on both client and server to enable pre-submission cost displays and server-side validation.

What determines whether a rank check uses live or queued processing?

The RankTrackingService.triggerCheck method selects the endpoint based on cost and latency requirements. Live checks return immediately but cost more per request; queued checks batch processing for lower cost but require polling or webhook completion. The estimation function accounts for this in its cost calculation.

How does the scheduler handle monthly intervals without date drift?

computeNextCheckAt uses calendar-aware date addition rather than fixed 30-day increments. For monthly schedules, it also randomizes the execution hour between 04:00 and 09:00 UTC to distribute server load across the user base rather than triggering all monthly checks simultaneously.

Can the rank tracking system support additional search engines or devices?

The architecture supports extension through the DeviceType union and configuration schemas. Adding new device types requires updating src/types/schemas/rank-tracking.ts, the shared device counting utilities, and corresponding DataForSEO API parameters. The repository and service layers remain unchanged as they operate on abstracted device identifiers.

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 →