Skip to content

Conversation

@corwintines
Copy link
Member

Description

Implements:

  • Creates abstract setter and getter functions for data layer
  • implements netlify blobs as storage
  • hourly and daily trigger.dev tasks
  • External fetches

@corwintines corwintines marked this pull request as draft December 4, 2025 23:26
@github-actions github-actions bot added the dependencies 📦 Changes related to project dependencies label Dec 4, 2025
@netlify
Copy link

netlify bot commented Dec 4, 2025

Deploy Preview for ethereumorg ready!

Name Link
🔨 Latest commit 668f798
🔍 Latest deploy log https://app.netlify.com/projects/ethereumorg/deploys/6938f8aad1eb8d00087b9743
😎 Deploy Preview https://deploy-preview-16801--ethereumorg.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
7 paths audited
Performance: 43 (🔴 down 9 from production)
Accessibility: 94 (no change from production)
Best Practices: 92 (🔴 down 8 from production)
SEO: 99 (no change from production)
PWA: 59 (no change from production)
View the detailed breakdown and full score reports

To edit notification comments on pull requests, go to your Netlify project configuration.

@github-actions github-actions bot added the tooling 🔧 Changes related to tooling of the project label Dec 5, 2025
@corwintines corwintines marked this pull request as ready for review December 5, 2025 00:34
Copy link
Member

@pettinarip pettinarip left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@corwintines this is looking really good.

I have one last architectural concern about how consumers will interact with the data-layer.

Right now, to get data you'd need to do this:

import { getData } from "@/data-layer/storage/getter"
import { FETCH_ETH_PRICE_TASK_ID } from "@/data-layer/api/fetchEthPrice"

This leaks internal details (storage paths, task IDs) into app code and loses type safety.

Suggested approach

  1. Data-layer exports a clean public API (src/data-layer/index.ts)
export async function getEthPrice(): Promise<EthPriceData | null>
export async function getL2beatData(): Promise<L2beatData | null>
// ...more

No Next.js imports here — keeps it framework agnostic so we can extract it later if needed.

  1. Next.js adapter handles caching (src/lib/data/index.ts)
import { unstable_cache } from "next/cache"
import * as dataLayer from "@/data-layer"

export const getEthPrice = unstable_cache(
  () => dataLayer.getEthPrice(),
  ["eth-price"],
  { revalidate: 3600 }
)
  1. App code uses the adapter
import { getEthPrice } from "@/lib/data"
const price = await getEthPrice() // cached + typed ✨

Why

  • Types flow automatically (no generics)
  • Data-layer stays isolated, no framework deps
  • Caching is the app's concern, not the data-layer's
  • Easy to extract to its own service later

Thoughts?

Comment on lines +17 to +31
## Files

- `index.ts` - TypeScript index file with task ID exports
- `*.json` - Individual mock data files for each task

## Last Generated

2025-12-05T21:54:56.350Z

## Statistics

- Total tasks: 21
- Successfully generated: 20
- Not found: 1

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this doesn't look relevant to consumers

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be useful depending on if we need to keep track of the last time a mocks were created, but will remove for now.

let store: ReturnType<typeof getStore> | null = null

function getBlobStore() {
if (!store) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can use early returns to make this more readable

if (store) {
  return store
}

// ... rest

} else {
// Try automatic configuration (works in Netlify environment)
// This will use NETLIFY_BLOBS_CONTEXT if available
store = getStore("data-layer")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if we are ever going to use this use case. Is this something you have in mind for other uses?

IMO we should always have a these vars. If not, throw to let us know something is not working.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For consistency and clarity, following the mockStorage name, we should call this netlifyBlobsStorage.

Comment on lines +45 to +46
export const netlifyBlobsStorage: StorageImplementation &
GetStorageImplementation = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

current typing around the storage is a bit fragmented and confusing.

would suggest unifying these types in types.ts and have 1 interface for the Storage.

// types.ts file

import type { tasks } from "./registry"

export type TaskId = (typeof tasks)[number]["id"]

/** Metadata stored alongside data */
export interface StorageMetadata {
  storedAt: string
}

/** Result shape when retrieving data with metadata */
export interface StorageResult<T> {
  data: T
  metadata: StorageMetadata
}

/**
 * Unified storage interface.
 * Implementations must provide both get and set methods.
 */
export interface Storage {
  get<T>(taskId: TaskId): Promise<StorageResult<T> | null>
  set(taskId: TaskId, data: unknown, metadata?: StorageMetadata): Promise<void>
}

then the implementation would be cleaner

export const netlifyBlobsStorage: Storage = {
  async get<T>(taskId: TaskId) { ... },
  async set(taskId: TaskId, data: unknown, metadata?: StorageMetadata) { ... },
}

for (const task of dailyTasks) {
try {
console.log(`Fetching data for task: ${task.id}`)
const data = await task.fetchFunction()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we are running these sequentially which can be a major bottleneck. We can parallelized by using await Promise.allSettled

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies 📦 Changes related to project dependencies tooling 🔧 Changes related to tooling of the project

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants