This page provides a high-level introduction to Casterscan, its architecture, and core design principles. It is intended as an entry point for understanding the system before diving into specific components.
For detailed information about specific subsystems:
Casterscan is a block explorer for Farcaster, deployed at casterscan.com. It provides URL-based access to Farcaster protocol data including casts, user profiles, signers, keys, and real-time Snapchain events.
Core design goals:
snap.farcaster.xyz:3383), Farcaster API, and Optimism RPC into a unified REST API at api.casterscan.com/fids/3, /casts/0xabc..., /snapchain/events/123)Capabilities:
GET /v1/snapchain/events/:eventIdneynar-hub, farcaster-hub, farcaster-api)GET /v1/fids/:fid/stats and GET /v1/fids/:fid/keysGET /v1/fids/:fid/signers/:signerKey/statsSources: README.md1-20
Casterscan is built as a Turborepo monorepo containing two primary applications that work together:
High-Level System Architecture
Critical architectural pattern: The browser never directly calls the Express API. All requests flow through Next.js API proxy routes in app/api/*, which forward to the Express backend. This provides server-side caching, CORS simplification, and security (the backend URL is hidden from clients).
Sources: README.md39-42 AGENTS.md14-21 package.json1-23
The repository follows a workspace-based monorepo structure managed by Turborepo:
| Workspace | Path | Purpose | Port |
|---|---|---|---|
| Web | apps/web | Next.js frontend with SSR, API proxy routes, and UI components | 3000 |
| API | apps/api | Express REST API with Redis caching and upstream integrations | 4000 |
| Packages | packages/* | Shared code, UI components, utilities, and schemas | N/A |
The root package.json defines unified scripts using Turbo's --filter and --parallel flags:
| Script | Command | Effect |
|---|---|---|
bun run dev | turbo run dev --filter=web --filter=api --parallel | Starts both apps/web and apps/api concurrently |
bun run dev:web | turbo run dev --filter=web | Starts apps/web only (:3000) |
bun run dev:api | turbo run dev --filter=api | Starts apps/api only (:4000) |
bun run build | turbo run build | Builds all workspaces |
bun run start | turbo run start --filter=web | Starts production web server |
bun run lint | turbo run lint | Runs linters across all workspaces |
Package manager: [email protected] serves as both the package manager and runtime, providing fast dependency resolution and native TypeScript support.
For detailed workspace configuration and build system mechanics, see Monorepo Structure.
Sources: package.json1-23 README.md39-42
| Layer | Technology | Purpose |
|---|---|---|
| Runtime | Bun 1.3.4 | Package management, TypeScript execution, bundling |
| Build System | Turbo 2.3.0 | Monorepo orchestration, caching, parallel execution |
| Frontend Framework | Next.js 15 + React 19 | Server-side rendering, routing, API proxy layer |
| UI Components | Shadcn/ui + Tailwind CSS | Component library and utility-first styling |
| Backend Framework | Express.js | REST API routing and middleware |
| Validation | Zod | Runtime type validation and schema enforcement |
| Caching | ioredis | Redis client for multi-layered caching strategy |
| Blockchain Client | viem | Ethereum/Optimism RPC interactions |
Sources: package.json19-22 apps/api/README.md1-3 AGENTS.md33-34
Request Lifecycle: Browser to Upstream Services
Key architectural principles:
Proxy Pattern: The browser never calls apps/api directly. All requests flow through apps/web/app/api/* routes, which use apiFetch() from apps/web/app/lib/api.ts1-15 to proxy to API_URL.
Multi-Layered Caching:
Cache-Control headers in Next.js responses)getCachedOrFetch() in apps/api/src/cache/cache.ts1-100coalescedFetch() in apps/api/src/cache/cache.ts1-100Environment Configuration: API_URL env var controls backend location:
http://localhost:4000 (default)https://api.casterscan.comFor detailed request flow analysis, see Request Flow.
Sources: AGENTS.md14-21 apps/web/app/api/farcaster/user/route.ts1-17 apps/web/app/api/farcaster/[fid]/signers/route.ts:1-29
The Next.js frontend defines proxy routes in app/api/* that map to Express API endpoints:
| Next.js Proxy Route | Express API Endpoint | Purpose |
|---|---|---|
/api/snapchain/info | GET /v1/snapchain/info | Snapchain node information |
/api/snapchain/event | GET /v1/snapchain/events/:eventId | Individual Snapchain event |
/api/fid/[fid]/stats | GET /v1/fids/:fid/stats | Cast/reaction/link counts for FID |
/api/signers/enriched | GET /v1/fids/:fid/signers/enriched | Signers with app profile enrichment |
/api/farcaster/[fid]/signers | GET /v1/fids/:fid/signers | Raw signer list for FID |
/api/farcaster/[fid]/keys | GET /v1/fids/:fid/keys | Auth addresses and signer keys |
/api/farcaster/[fid]/casts | GET /v1/fids/:fid/messages | Messages/casts by FID |
/api/farcaster/user | GET /v1/users/:fid | User profile by FID |
/api/farcaster/cast | GET /v1/casts/:hash | Cast details by hash |
Example proxy route implementation:
The pattern in apps/web/app/api/farcaster/user/route.ts5-16 shows how Next.js routes proxy to the Express API:
fid)apiFetch('/v1/users/${fid}') which prepends API_URLCache-Control header using constants from CACHE_TTLS in apps/web/app/lib/utils.ts1-50This ensures the browser never sees or calls api.casterscan.com directly.
For complete API endpoint documentation, see API Endpoint Reference.
Sources: AGENTS.md48-65 apps/web/app/api/farcaster/user/route.ts1-17 apps/web/app/api/farcaster/[fid]/keys/route.ts:1-27
Casterscan uses a dual-deployment model with separate Vercel deployments for frontend and backend:
Critical deployment order: The API must be deployed first to obtain its URL, which is then configured as the API_URL environment variable in the Web app deployment.
Environment variables:
| Variable | App | Required | Description |
|---|---|---|---|
NEYNAR_API_KEY | API | Yes | Neynar API key for user/cast lookups |
API_URL | Web | No | Backend URL (default: localhost:4000 dev, api.casterscan.com prod) |
REDIS_URL | API | No | Redis connection string (cache disabled if unset) |
ALLOWED_ORIGINS | API | No | CORS allowed origins |
For complete deployment procedures, see Deployment.
Sources: README.md31-37 apps/api/README.md6-9 AGENTS.md119-125
Every atomic unit of Farcaster/Snapchain data has a unique URL route in [apps/web/app/]:
app/casts/[hash]/page.tsx → /casts/0xabc...app/fids/[fid]/page.tsx → /fids/3app/fids/[fid]/signers/[signer]/page.tsx → /fids/3/signers/0x123...app/snapchain/events/[eventId]/page.tsx → /snapchain/events/12345The browser never calls apps/api directly. All API requests flow through apps/web/app/api/* routes:
apiFetch() in apps/web/app/lib/api.ts1-15 prepends API_URL to all pathsCache-Control headers for browser cachingapi.casterscan.com) remains invisible to browser clientsFour cache layers optimize performance:
Cache-Control headers in Next.js responsesgetCachedOrFetch() in apps/api/src/cache/cache.ts1-100coalescedFetch() prevents duplicate concurrent requestsRuntime validation using Zod schemas:
validateParams(fidSchema) in apps/api/src/lib/validate.ts1-50validateQuery(querySchema) for query string validationvalidateBody(bodySchema) for POST request bodiesasyncHandler() wrapper catches async errors and passes to error middlewareClear separation in [apps/api/src/]:
fidsRouter.ts, castsRouter.ts, etc.)fid.ts, cast.ts, signer.ts)cache.ts)neynar.ts, snapchain.ts, etc.)Sources: README.md17-19 AGENTS.md14-21 apps/api/README.md1-3
Local development setup:
The bun run dev command uses turbo run dev --filter=web --filter=api --parallel from package.json11-17 to start both applications concurrently.
See Development Guide for coding conventions and feature development workflows.
Navigate to specific sections based on your interest:
Sources: README.md22-29 AGENTS.md140-148
Refresh this wiki