Explore tokens, charts, favorites and profiles — backed by Supabase and Redis.
Next.js · Supabase · Redis Cloud · WalletConnect
- Overview
- Features
- Tech Stack
- Architecture
- Project Structure
- Environment (.env)
- Supabase Setup
- Development
- Logging
- API Reference
- Authentication Flow
- Price Cache
- Deployment
- Production Checklist
- Troubleshooting
- Technical Details
- Visual Guide
CryptoLens is a dApp to explore tokens, manage favorites, and visualize market metrics with secure authentication (Google + Wallet via SIWE/personal_sign), user profiles in Supabase, and centralized caching in Redis. Built with Next.js (Pages Router), React, RainbowKit/wagmi, Supabase, and Tailwind/Shadcn. Live demo: https://cryptolens.casaislabs.com/
- Token exploration with price, 24h change, market cap, and volume.
- User favorites persisted with Supabase RLS.
- Advanced filters and virtualized grid for performance.
- Sign in with Google and wallet (SIWE + fallback personal_sign) always enabled.
- User profiles with social links and linked wallet status.
- Centralized price cache in Redis with cron; CoinGecko primary with CoinMarketCap fallback.
Next.js+Reactnext-auth(Google + Credentials for wallet)@supabase/supabase-js(API + RLS)wagmi+@rainbow-me/rainbowkit(WalletConnect)ioredis+node-cron(price cache)tailwindcss+@/components/ui/*(Shadcn)lucide-react,sonner
flowchart LR
Client[Next.js Client] -->|OAuth| NextAuth
Client -->|Wallet| WalletConnect
NextAuth --> Supabase
API[/pages/api/*/] --> Redis[(Redis Cache)]
API --> CoinGecko
API --> CoinMarketCap
Supabase --> DB[(Postgres + RLS)]
sequenceDiagram
participant U as User
participant C as Client (Next.js)
participant A as NextAuth
participant W as Wallet (RainbowKit/WC)
participant S as Supabase
U->>C: Click "Sign-In with Wallet"
C->>W: Request signature (SIWE/personal_sign)
W-->>C: Signature returned
C->>A: Verify signature, create session
A->>S: Call RPC with JWS (RLS enforced)
S-->>A: Authorized response
A-->>C: Session established
pages/— Pages Router, APIs (api/*), views (index,dashboard,login,profile,token/[id]).components/— Header, filters, token cards, wallet connection.lib/— Supabase, JWT, price cache, utilities.public/—favicon.svg,logo.svg.create_supabase_tables.sql— Schema, RPC, and RLS policies.
Create a .env file at the project root:
# Environment
NODE_ENV=development
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=put_a_strong_random_secret_here
# Supabase
SUPABASE_URL=https://<your-project>.supabase.co
SUPABASE_KEY=<anon_public_key>
SUPABASE_JWT_SECRET=<supabase_jwt_secret>
# WalletConnect (RainbowKit)
NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID=<walletconnect_project_id>
# Google OAuth
GOOGLE_CLIENT_ID=<google_client_id>
GOOGLE_CLIENT_SECRET=<google_client_secret>
# CoinMarketCap (fallback)
COINMARKETCAP_API_KEY=<cmc_api_key>
# Redis (price cache)
REDIS_HOST=<redis_host>
REDIS_PORT=<redis_port>
REDIS_USERNAME=<redis_username>
REDIS_PASSWORD=<redis_password>
# Logging
LOG_LEVEL=info- Supabase:
SUPABASE_URL: Supabase Dashboard → Settings → API → Project URL.SUPABASE_KEY(anon): Supabase Dashboard → Settings → API →anon publickey.SUPABASE_JWT_SECRET: Supabase Dashboard → Settings → API →JWT Secret(keep distinct fromNEXTAUTH_SECRET; do not reuse).
- Google OAuth:
- Google Cloud Console → APIs & Services → OAuth consent screen (External type, basic info).
- Create credentials: Credentials → Create Credentials → OAuth client ID → Web application.
- Authorized settings:
- Authorized redirect URIs:
http://localhost:3000/api/auth/callback/googleand production domainhttps://your-domain/api/auth/callback/google. - Authorized JavaScript origins:
http://localhost:3000and your production domain.
- Authorized redirect URIs:
- Copy
GOOGLE_CLIENT_IDandGOOGLE_CLIENT_SECRET.
- WalletConnect:
- Create a project at https://cloud.walletconnect.com.
- Copy the
Project IDintoNEXT_PUBLIC_WALLETCONNECT_PROJECT_ID.
- CoinMarketCap:
- Create an account at https://pro.coinmarketcap.com and generate an
API Key. - Note rate limits and plan restrictions; this is used as a fallback when CoinGecko fails.
- Create an account at https://pro.coinmarketcap.com and generate an
- Redis (Upstash or other provider):
- Provision a database at Upstash (or Aiven/Redis Cloud).
- Copy
host,port,username, andpassword. - Note: if your provider requires TLS, adjust
ioredisconnection options accordingly (many dev environments work without TLS; verify production requirements). - Recommendation: Redis Cloud (managed, production-grade): https://cloud.redis.io/
- Open your Supabase project →
SQL Editor. - Open the local file
create_supabase_tables.sql. - Copy and paste its entire content into the Supabase SQL Editor.
- Execute section by section (tables
profilesandfavorites, indexes, FK, transactional RPCreplace_user_favorites, verification tasks, and RLS policies). - Verify at the end using the “VERIFICATION” section.
Notes:
- RLS policies rely on the
subclaim from JWT: the code generates a JWS from the NextAuth token to operate under RLS. - Wallet link/unlink updates
profiles.wallet_addressand validates duplicates.
- Install deps:
npm install. - Start dev:
npm run dev→ visithttp://localhost:3000. - Build:
npm run buildthennpm run start. - Lint:
npm run lint.
npm run dev— Start Next.js dev servernpm run build— Build production bundlenpm run start— Start production servernpm run lint— Run ESLint
- Pino-only logging is used across server APIs; Sentry was removed.
- View logs in development in the terminal; in production via your platform's stdout/logs.
- Control verbosity with
LOG_LEVEL(debug,info,warn,error). Default isinfo. - Per-request correlation via
requestIdfrom middleware; appears on API logs. - Core logger:
lib/logger.jswith robustErrorserialization and child loggers per request. - Instrumented APIs:
pages/api/tokens/all.jsandpages/api/auth/[...nextauth].js. - Optional: install
pino-prettylocally for human-readable dev output.
GET /api/fetchTokens?ids=all|favorites|id1,id2,...— Returns price snapshots.GET /api/token/[id]— Returns detailed token data and 7-day chart (with fallbacks).POST /api/updateFavorites— Upserts favorites under RLS.POST /api/updateProfile— Updates profile fields (validated links).POST /api/wallet— Wallet challenge, link/unlink, status.
- Google: standard OAuth via NextAuth.
- Wallet: SIWE (recommended) with fallback
personal_sign. - Wallet provider is always active (the environment flag was removed).
lib/priceCache.jspre-warms prices and stores in Redis.- Cron every 5 minutes; fallback to CoinMarketCap when CoinGecko fails.
- Vercel or similar: configure all
.envvariables in your platform settings. - Set
NEXTAUTH_URLto your real domain. - Use distinct secrets for
NEXTAUTH_SECRETandSUPABASE_JWT_SECRETto follow best security practices.
- Secrets: Generate a unique
NEXTAUTH_SECRET; keep different fromSUPABASE_JWT_SECRET. - Supabase: Run
create_supabase_tables.sql; verify RLS policies and RPC. - OAuth: Confirm Google redirect URIs and consent screen publishing.
- Wallet: SIWE domain matches your production host; enable WalletConnect project in production.
- Redis: Use
https://cloud.redis.io/; verify TLS and credentials; set appropriate TTLs inlib/priceCache.js. - Rate limits: Configure upstream API rate limits and enable backoff logic.
- Observability: Pino JSON logs; integrate platform log dashboards; optional
pino-prettyin development. - Security headers: Globally enforced in
middleware.js(CSP, HSTS in prod, X-Content-Type-Options, X-Frame-Options, Referrer-Policy, Permissions-Policy). Review CSP for any external domains needed. - Next.js Images: Review
next.config.mjsimages.remotePatternsfor allowed domains. - Backups: Store
.env.productionsecurely; rotate keys periodically.
- “Missing Supabase env” → Ensure
SUPABASE_URLandSUPABASE_KEY. - “HMAC/JWT secret required” → Define
NEXTAUTH_SECRET(and optionallySUPABASE_JWT_SECRET). - “Google OAuth redirect error” → Check authorized URIs.
- “WalletConnect does not open” → Verify
NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID. - “CMC data fallback 403/401” → Confirm
COINMARKETCAP_API_KEYand rate limits. - “Redis connection error” → Verify host/port/user/password and TLS requirements.
- “Domain mismatch (SIWE)” → The SIWE domain must match
NEXTAUTH_URL/host:- Dev:
http://localhost:3000. - Prod: your actual domain.
- Dev:
- “403 on mutating API from another origin” → CSRF checks
Origin. Send from same-origin or avoid cookies for cross-domain. - “CORS preflight blocked cross-domain” → CORS is restricted to same-origin; ensure frontend and API share domain/port.
This project is intended as a base for a Web3 dashboard. Adapt and extend as needed.
- Do not reuse Supabase's
JWT Secret. Use a unique value. - Recommended commands to generate a 64-byte random secret:
- PowerShell (Windows):
[Convert]::ToBase64String([Security.Cryptography.RandomNumberGenerator]::GetBytes(64)) - Node.js:
node -e "console.log(require('crypto').randomBytes(64).toString('base64'))" - OpenSSL (if available):
openssl rand -base64 64
- PowerShell (Windows):
- Store the generated string in
NEXTAUTH_SECRETin.env.
- Architecture: Next.js Pages Router serving UI and API routes. Client UI in
pages/*.jsandcomponents/*. Server-only logic inpages/api/*andlib/*. - Authentication: NextAuth with Google provider and a Credentials provider for wallet. Wallet sign-in supports
siweandpersonal_sign. Challenge cookie is signed via HMAC; verification runs inpages/api/auth/[...nextauth].js. - JWT → Supabase:
lib/jwt.jsbuilds a JWS from the NextAuth token to call Supabase under RLS. Policies use claimsubfor per-user access. - Supabase Schema:
create_supabase_tables.sqlcreatesprofilesandfavorites, adds indexes and FK, and defines transactional RPCreplace_user_favorites(p_user_id, p_token_ids). - RLS Policies: allow each user to operate only on their own rows in
profilesandfavoritesusing(auth.jwt() ->> 'sub') = user_id. - Data Flow:
- Favorites: UI →
pages/api/updateFavorites.js→ Supabase RPC/insert under RLS → persisted per user. - Tokens list: UI →
pages/api/fetchTokens.js→ Redis cache (lib/priceCache.js) → CoinGecko primary, CMC fallback. - Token details + chart: UI →
pages/api/token/[id].js→ CoinGecko market data and chart; fallback to CMCquotes/latestif needed.
- Favorites: UI →
- Price Cache & Cron:
- Redis keys:
prices:all(TTL 300s). Functions:getAllPrices,refreshAllPrices. - Cron: every 5 minutes (
*/5 * * * *) to refresh aggregated prices.
- Redis keys:
- External Providers:
- CoinGecko: primary market/price source.
- CoinMarketCap: fallback via
listings/latestandquotes/latestusingCOINMARKETCAP_API_KEY. - WalletConnect (RainbowKit): configured in
lib/web3Config.jswithNEXT_PUBLIC_WALLETCONNECT_PROJECT_ID.
- Rate Limiting & Resilience:
pages/api/token/[id].jsincludes basic rate-limit and retries/backoffs around upstream APIs, with graceful fallbacks.pages/api/fetchTokens.jsfilters and validates data; if cache/network fails, falls back to direct upstream fetch.
- Performance & UX:
- Virtualized grid:
components/VirtualizedTokenGrid.jsxfor large lists. - Charts:
components/TokenChart.jsxwith Recharts and custom tooltips. - UI components: Shadcn (
components/ui/*), Tailwind utility classes, Skeletons, and Sonner toasts. - Accessibility: improved headings, focus, alt text (e.g.,
CryptoLens Logo).
- Virtualized grid:
- Security Notes:
- Global security headers via
middleware.js: CSP (strict; dev permitsunsafe-eval), HSTS (prod),X-Content-Type-Options=nosniff,X-Frame-Options=DENY,Referrer-Policy=strict-origin-when-cross-origin, and a restrictivePermissions-Policy. - CSRF protection: mutating API methods (
POST/PUT/PATCH/DELETE) require same-originOrigin. Cross-domain requests with cookies are rejected with 403. - CORS: same-origin only; preflight
OPTIONShandled with restricted headers and credentials. - Payload limits: ~1MB enforced in middleware for mutating API requests.
- Cookies: wallet challenge cookie uses
SameSite=Strict,HttpOnly, andSecurein production. - Distinct secrets:
NEXTAUTH_SECRETshould be different fromSUPABASE_JWT_SECRET. - Server-only modules:
lib/supabase.js,lib/profile.js,lib/priceCache.jsthrow if imported in browser. - Cookies: challenge cookie is HMAC-protected and time-bound.
- Global security headers via
- API Endpoints (summary):
GET /api/fetchTokens?ids=all|favorites|id1,id2,...— returns price snapshots.GET /api/token/[id]— returns detailed token data and 7-day chart (with fallbacks).POST /api/updateFavorites— upserts favorites under RLS.POST /api/updateProfile— updates profile fields (validated links).POST /api/wallet— wallet challenge, link/unlink, status.
- Configuration:
- Next.js config in
next.config.mjsand ESLint ineslint.config.mjs. - Absolute imports via
jsconfig.json. - Styles:
styles/globals.css.
- Next.js config in
The following images illustrate key parts of the CryptoLens experience and architecture. All assets live under public/ and are referenced with repository-relative paths so they render correctly on GitHub.
-
Landing & Branding
-
Login & Authentication
-
Dashboard Overview
-
Favorites / Watchlist
-
Filters & Sorting
-
Token Detail & Chart
-
Profile & Wallet Linking






