feat: new Farcaster Express API + Turborepo restructure - #18
Merged
Conversation
dylsteck
force-pushed
the
feat/data-layer-implementation
branch
4 times, most recently
from
March 7, 2026 23:36
3ef25c7 to
e4ff9c3
Compare
Co-authored-by: Dylan Steck <dylan.steck@coinbase.com>
- Add packages/data-layer: Elysia server with Redis cache, request coalescing - Upstream clients: Neynar, Snapchain, Farcaster API, Optimism keys - Routes: /v1/fids/:fid/stats, messages, signers/enriched, signers/:signer/messages|stats - Routes: /v1/users, /v1/casts, /v1/fids/:fid/keys, /v1/snapchain/info - Next.js proxies: /api/fid/[fid]/stats, /api/signers/enriched, /api/signers/messages - Migrate useFidStats, useSignerMessages, useAppsWithSigners to data layer - Add CI workflow for Next.js + data-layer build - Add DATA_LAYER_URL to .env.example Co-authored-by: Dylan Steck <dylan.steck@coinbase.com>
- Move Next.js app to apps/web - Move data layer to apps/api - Add turbo.json, Turborepo scripts - Root package.json: workspaces apps/*, packages/* - Update CI, README, .gitignore - dev, dev:api, dev:all, build all use turbo Co-authored-by: Dylan Steck <dylan.steck@coinbase.com>
Co-authored-by: Dylan Steck <dylan.steck@coinbase.com>
Co-authored-by: Dylan Steck <dylan.steck@coinbase.com>
Co-authored-by: Dylan Steck <dylan.steck@coinbase.com>
…API-only - lib/server.ts: getNeynarCast, getNeynarUser, getNeynarUserByUsername, getFarcasterKeys now use data layer - api/neynar/*, api/snapchain/cast: proxy to data layer - .env.example: NEYNAR_API_KEY and REDIS_URL are API-only; DATA_LAYER_URL is web-only Co-authored-by: Dylan Steck <dylan.steck@coinbase.com>
- Rename data-layer.ts to api.ts, dataLayerFetch to apiFetch, DATA_LAYER_URL to API_URL - Proxy all web API routes to apps/api (snapchain, farcaster, signers, neynar) - Add /v1/fids/:fid/signers to API for raw on-chain signers - Remove direct snapchain/farcaster/neynar libs from web (dead code) - Update .env.example and README Co-authored-by: Dylan Steck <dylan.steck@coinbase.com>
Co-authored-by: Dylan Steck <dylan.steck@coinbase.com>
Co-authored-by: Dylan Steck <dylan.steck@coinbase.com>
Co-authored-by: Dylan Steck <dylan.steck@coinbase.com>
Co-authored-by: Dylan Steck <dylan.steck@coinbase.com>
…fore export) Co-authored-by: Dylan Steck <dylan.steck@coinbase.com>
…rror Co-authored-by: Dylan Steck <dylan.steck@coinbase.com>
… not instantiated) Co-authored-by: Dylan Steck <dylan.steck@coinbase.com>
…ibility Node.js ESM requires explicit file extensions in relative imports. ERR_MODULE_NOT_FOUND was caused by imports like './lib/errors' without .js
…cel deploy docs to AGENTS.md
- Cache: fidSignersEnriched 1hr, remove VERY_LONG, align Redis/web TTLs - Security: strict validation (fid, hash, eventId, etc), path traversal middleware - Security: stop leaking err.message, sanitize API errors in prod - Security: CORS allowlist, rate limiting, NEYNAR_API_KEY required in prod - Security: cache key sanitization, web proxy validation - Bug: fix frame URL profiles→fids - Quality: remove dead Skeleton.tsx, proxy.ts - Quality: replace any types, add PruneEvent type
dylsteck
force-pushed
the
feat/data-layer-implementation
branch
from
March 7, 2026 23:53
7bb4916 to
1c2d1ca
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Creates a unified Farcaster API that proxies all our third-party requests (Neynar, Snapchain, Farcaster API, Optimism RPC). Caching, request coalescing, and a single backend—simpler frontend, fewer duplicate calls. Also restructures the repo as a Turborepo monorepo.
apps/api— Express server with Redis cache and request coalescingapps/web— Casterscan Next.js app (moved from root)useFidStats,useSignerMessages,useAppsWithSignersnow hit the API via Next.js proxy routesArchitecture
flowchart TB subgraph Client["Browser"] WebApp["Next.js (apps/web)"] end subgraph NextJS["Next.js API Routes"] ProxyStats["/api/fid/[fid]/stats"] ProxyEnriched["/api/signers/enriched"] ProxyMessages["/api/signers/messages"] end subgraph DataLayer["Data Layer (apps/api)"] direction TB Cache["Redis Cache"] Coalesce["Request Coalescing"] Routes["Express Routes"] Routes --> Cache Routes --> Coalesce end subgraph Upstream["Upstream APIs"] Neynar["Neynar API"] Snapchain["Snapchain REST"] Farcaster["Farcaster API"] Optimism["Optimism RPC"] end WebApp --> ProxyStats WebApp --> ProxyEnriched WebApp --> ProxyMessages ProxyStats --> DataLayer ProxyEnriched --> DataLayer ProxyMessages --> DataLayer DataLayer --> Neynar DataLayer --> Snapchain DataLayer --> Farcaster DataLayer --> OptimismData Flow (Profile Page Example)
sequenceDiagram participant User participant Web participant Proxy participant API participant Redis participant Snapchain participant Neynar User->>Web: Visit /fids/3 Web->>Proxy: GET /api/fid/3/stats Proxy->>API: GET /v1/fids/3/stats API->>Redis: Check cache alt Cache hit Redis-->>API: Return cached else Cache miss API->>Snapchain: getCastsByFid, getReactionsByFid, etc. Snapchain-->>API: Raw data API->>Redis: Store (TTL 15min) end API-->>Proxy: { casts, reactions, links, verifications } Proxy-->>Web: JSON Web->>Proxy: GET /api/signers/enriched?fid=3 Proxy->>API: GET /v1/fids/3/signers/enriched API->>Snapchain: getAllCastsByFid, getOnChainSignersByFid, etc. API->>Neynar: getUsers (app profiles) API-->>Proxy: Apps with signers Proxy-->>Web: JSON Web-->>User: Render profileMonorepo Structure
flowchart LR subgraph Root["/"] Turbo["turbo.json"] Pkg["package.json"] end subgraph Apps["apps/"] Web["web/ (Next.js)"] API["api/ (Express)"] end subgraph WebContents["apps/web"] App["app/"] Public["public/"] NextConfig["next.config.js"] end subgraph APIContents["apps/api"] Src["src/"] Cache["cache/"] Upstream["upstream/"] Routes["routes/"] Services["services/"] end Pkg --> Web Pkg --> API Turbo --> Web Turbo --> APIAPI Surface (Data Layer)
/health/v1/fids/:fid/stats/v1/fids/:fid/messages/v1/fids/:fid/signers/enriched/v1/fids/:fid/signers/:signer/messages/v1/fids/:fid/signers/:signer/stats/v1/fids/:fid/keys/v1/users/:fid/v1/users/by-username/:username/v1/users/bulk/v1/casts/:hash?format=for hub variants)/v1/snapchain/info/v1/snapchain/events/:eventIdMigrated Hooks
useFidStats/api/farcaster/[fid]/*/api/fid/[fid]/statsuseSignerMessages/api/signers/messagesuseAppsWithSigners/api/signers/enriched(direct Snapchain + Neynar)Commands
bun run devbun run dev:apibun run dev:webbun run buildbun run startEnv Vars
NEYNAR_API_KEY— Required for user/cast lookups (API only)API_URL— For web app, points to API (defaulthttp://localhost:4000dev,https://api.casterscan.comprod)REDIS_URL— Optional; cache disabled if unset (API only)ALLOWED_ORIGINS— Optional; CORS allowlist, comma-separated (API only). Default: casterscan.com, localhost:3000Technical Notes
.jsextensions for Node.js ESM compatibility