Claude-skill-registry data-layer
This skill provides patterns for working with the data-layer module. Use when creating/editing files in src/data-layer/, src/lib/data/, or adding new data sources.
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/data-layer" ~/.claude/skills/majiayu000-claude-skill-registry-data-layer && rm -rf "$T"
manifest:
skills/data/data-layer/SKILL.mdsource content
Data Layer
Architecture
src/data-layer/ ├── fetchers/ # Fetch functions (one per data source) ├── index.ts # Public API - typed getter functions ├── tasks.ts # KEYS constant + Trigger.dev scheduled tasks ├── storage.ts # get/set abstraction (Netlify Blobs or mock files) └── mocks/ # Mock data files for local development src/lib/data/ └── index.ts # Next.js caching adapter (createCachedGetter)
Key Files
tasks.ts - Single Source of Truth
Defines all task keys and scheduled jobs:
export const KEYS = { ETH_PRICE: "fetch-eth-price", L2BEAT: "fetch-l2beat", // ... } as const const DAILY: Task[] = [ [KEYS.APPS, fetchApps], [KEYS.EVENTS, fetchEvents], ] const HOURLY: Task[] = [ [KEYS.ETH_PRICE, fetchEthPrice], [KEYS.BEACONCHAIN, fetchBeaconChain], ]
index.ts - Simple Getters
One-liner passthrough functions:
export const getEthPrice = () => get<MetricReturnData>(KEYS.ETH_PRICE) export const getL2beatData = () => get<L2beatData>(KEYS.L2BEAT)
storage.ts - Storage Abstraction
Simple get/set that switches between Netlify Blobs (prod) and local JSON files (dev):
export async function get<T>(key: string): Promise<T | null> export async function set(key: string, data: unknown): Promise<void>
Uses
USE_MOCK_DATA=true env var for local development.
Rules
1. Getters must be pure passthrough
No transformations in
index.ts - just get<T>(KEYS.X):
// Correct export const getEventsData = () => get<EventItem[]>(KEYS.EVENTS) // Wrong - no transformations in getters export const getEventsData = () => { const data = await get<EventItem[]>(KEYS.EVENTS) return data?.map(transform) ?? null }
All transformations belong in the fetcher (
src/data-layer/fetchers/).
2. KEYS is the single source of truth
All task IDs are defined in
KEYS in tasks.ts. The getter in index.ts and the task tuple in DAILY/HOURLY must use the same key.
3. Expose via lib/data for caching
Add cached wrapper in
src/lib/data/index.ts:
export const getEventsData = createCachedGetter( dataLayer.getEventsData, ["events-data"], CACHE_REVALIDATE_DAY // or CACHE_REVALIDATE_HOUR )
Adding a New Data Source
-
Create fetcher in
:src/data-layer/fetchers/fetchNewData.tsexport async function fetchNewData(): Promise<YourDataType> { // Fetch and transform data here } -
Add key to
inKEYS
:src/data-layer/tasks.tsexport const KEYS = { // ...existing keys NEW_DATA: "fetch-new-data", } as const -
Add task tuple to
orDAILY
inHOURLY
:tasks.tsconst DAILY: Task[] = [ // ...existing tasks [KEYS.NEW_DATA, fetchNewData], ] -
Add getter in
:src/data-layer/index.tsexport const getNewData = () => get<YourDataType>(KEYS.NEW_DATA) -
Add mock file at
for local developmentsrc/data-layer/mocks/fetch-new-data.json -
Add cached wrapper in
:src/lib/data/index.tsexport const getNewData = createCachedGetter( dataLayer.getNewData, ["new-data"], CACHE_REVALIDATE_HOUR )