Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/santiagonieto09/portafolio/llms.txt

Use this file to discover all available pages before exploring further.

The portfolio is structured around three distinct layers that communicate in one direction only — domain ← infrastructure ← presentation. The domain layer defines pure TypeScript contracts and logic with no external dependencies. The infrastructure layer is the only code that knows GitHub exists; it fetches, maps, and caches API data before handing it back as domain types. The presentation layer — React components and TanStack Router routes — consumes domain types exclusively and never reaches into the infrastructure layer directly. This separation makes each layer independently testable and keeps browser bundles free of server-only code.

Layers

Domain layer (src/domain/)

Framework-agnostic TypeScript interfaces and pure functions. This code has no knowledge of React, fetch, or the GitHub SDK. It contains:
  • github/types.ts — the canonical data contracts (PortfolioSnapshot, Profile, Repository, ActivityItem, LanguageSlice, etc.) shared by every other layer.
  • github/language-colors.ts — a lookup map from language name to hex colour string, used for the language chart.
  • github/technology-detection.ts — pure function that infers technology badges from repo name, description, topics, and language list.
  • portfolio/repository-query.ts — pure filtering and sorting helpers for the repository explorer (search, language filter, sort order).
Because this layer has no I/O, every export can be unit-tested without mocking anything.

Infrastructure layer (src/infrastructure/github/)

The only code that knows the GitHub REST API exists. All files in this layer are server-only — the .server.ts filename suffix tells the Vite build to exclude them from the client bundle. It contains:
  • github-api.server.ts — the HTTP client (get<T>()), parallel fetch orchestration (fetchPortfolio()), per-repository enrichment (buildRepository()), and domain mapping. This is the sole file that makes outbound network requests to api.github.com.
  • snapshot-cache.ts — a dual-layer cache (in-memory + Cloudflare Cache API) that stores a serialised PortfolioSnapshot and avoids redundant GitHub API calls across requests and cold starts.
  • github-api-types.ts — raw GitHub REST response shapes (GitHubUser, GitHubRepo, GitHubEvent, etc.). These are provider contracts, intentionally kept separate from the domain types.

Presentation layer (src/components/, src/routes/)

React 19 components and TanStack Router file-based routes. This layer imports from the domain layer and from TanStack Query, but never from the infrastructure layer. The routes/index.tsx loader pre-populates the React Query cache server-side; components call useSuspenseQuery(portfolioQueryOptions()) and render from that cache.
The .server.ts filename convention is enforced by the Vite build pipeline. Any attempt to import github-api.server.ts from a client-only module causes a build error at compile time, making it structurally impossible for API credentials or server-side logic to leak into the browser bundle.

Request lifecycle

1

Browser or crawler requests /

An HTTP request arrives at the Nitro server runtime — either Vercel Edge on production or a Cloudflare Worker in the Lovable sandbox.
2

Nitro calls the TanStack Start loader

TanStack Router’s file-based routing matches the request to src/routes/index.tsx and invokes its loader function with the shared QueryClient context.
3

Loader calls ensureQueryData()

The loader calls context.queryClient.ensureQueryData(portfolioQueryOptions()). The query function configured inside portfolioQueryOptions() is getPortfolio() — a TanStack Start server function defined in src/lib/portfolio.functions.ts — which executes on the server without an HTTP round-trip.
4

Server function calls fetchPortfolio()

getPortfolio() dynamically imports and calls fetchPortfolio(GITHUB_USERNAME) from src/infrastructure/github/github-api.server.ts.
5

Cache check via readSnapshot()

fetchPortfolio calls readSnapshot(). If a PortfolioSnapshot is found in memory or the Cloudflare Cache API and is within the 7-day TTL, it is returned immediately — no GitHub API calls are made.
6

Cache miss: four parallel GitHub API requests

On a cache miss, fetchPortfolio fires four requests simultaneously using Promise.all: user profile, repositories (up to 100, sorted by last update), social accounts, and public events. Repository language and release data are then fetched with a concurrency limit of 3 to avoid secondary rate limits.
7

PortfolioSnapshot is built, cached, and returned

Raw API responses are mapped to domain types. The resulting PortfolioSnapshot is written to both the in-memory store and the Cloudflare Cache API (with a 7-day max-age), then returned up the call stack.
8

React Query cache is pre-populated

ensureQueryData stores the snapshot in the server-side QueryClient under the key ['portfolio'], making the data available to React components before rendering begins.
9

React renders the page server-side

TanStack Start runs the full React component tree on the server. The useSuspenseQuery call in PortfolioPage reads from the pre-populated cache — the Suspense boundary never shows a loading state. The complete HTML is streamed to the client.
10

Client hydrates; React Query serves from cache

The browser receives the HTML with an inlined dehydrated query state. React hydrates the page and React Query rehydrates its cache from that state. With staleTime: 1000 * 60 * 60 (one hour), the client will not refetch for 60 minutes after hydration.

Runtime targets

The application’s server adapter is Nitro, which compiles the same application code to different deployment targets without any changes to the source.
TargetWhen activeNotes
vercelProduction deployUses Vercel’s Build Output API; outputs to .vercel/output.
cloudflareLovable sandbox previewsLovable’s @lovable.dev/vite-tanstack-config overrides the Nitro preset automatically.
The vite.config.ts default is nitro: { preset: "vercel" }. The Lovable sandbox overrides this with its own Cloudflare preset at build time, so preview deployments work identically to production deploys at the application level. The only runtime difference visible to the application is whether caches.default (Cloudflare Cache API) is available — handled gracefully by the snapshot cache module.

Build docs developers (and LLMs) love