nextjs

Next.js 16 — Arquitectura Avanzada y Patrones Modernos

1. Arquitectura Interna del Runtime en Next.js 16

Next.js 16 consolida varios runtimes:

  • Node.js Runtime
    • Ideal para lógica pesada, conexiones largas, ORMs, colas, workers.
  • Edge Runtime
    • Para lógica superligera, caché global, auth tokens, A/B testing.
  • Web Runtime
    • Código del cliente y server components en build.

Selección del runtime (patrón recomendado)

  • Colocar en server actions la mutación + validación.
  • Colocar lógica global de auth en proxy.ts (antes middleware).
  • Procesamiento intensivo ⇒ Node.js Runtime.
  • Respuestas micro-cacheadas ⇒ Edge Runtime.

Referencia oficial:


2. Patrón “Front + Actions + Services”

Patrón moderno para aplicaciones grandes:

2.1 Componentes (UI)

  • Server/Client components.
  • Minimizar client components (coste en bundle + hydration).

2.2 Server Actions

  • Validación, mutaciones, persistencia.
  • Desacoplarlos del UI exportando funciones puras.

2.3 Capa de Servicios

  • Clases o funciones puras donde vive la lógica de dominio.
  • Útil para:
    • Testing
    • Reutilización cross-rutas
    • Microservicios híbridos

Ejemplo:

// app/actions/create-user.ts
'use server'
import { userService } from '@/services/user-service'
export async function createUser(data) {
return userService.create(data)
}

`


3. Arquitectura de Datos en Next.js 16

El modelo recomendado para 2025:

3.1 Acceso a DB con Runtimes Múltiples

  • Node.js Runtime para ORMs: Prisma, Drizzle, Kysely.
  • Edge Runtime con:
  • PlanetScale
  • Neon (Postgres serverless)
  • Upstash Redis
  • LibSQL/Turso

3.2 Capa de Caché Multinivel

  • Caché declarativa (use cache)
  • Revalidaciones granulares (revalidateTag, revalidatePath)
  • Edge caché con Vercel Edge

3.3 Streaming de Datos (React 19 + Next 16)

  • Ideal para feeds, dashboards, reporting
  • Mantiene TTFB bajo

4. Arquitectura de Rutas y Dominios de Contexto

Patrón recomendado para proyectos grandes en App Router:

/app
(auth) → layouts y acciones de autenticación
(dashboard)
	analytics/
	billing/
	users/
	api/…

Principios:

  • Rutas agrupadas por contexto, no por tipo de archivo.
  • Cada grupo con su propio layout + loading + error.
  • Servicios compartidos debajo de /lib o /services.

5. Edge Patterns Modernos en Next.js 16

Next 16 mejora el modelo de proxy.ts, que centraliza el “network boundary”:

Casos recomendados:

  • Rate limiting
  • Token refresh
  • Redirecciones inteligentes
  • Internationalización basada en geolocalización
  • A/B testing + Feature flags

Documentación base:


6. Renderización Estratégica (SSR + ISR + PPR)

Next.js 16 habilita estrategias más granulares:

6.1 SSR Híbrido con Server Actions

- Uso para dashboards con datos frescos.

6.2 ISR + revalidateTag

- Revalidación selectiva según dominios de datos.
- Ideal para ecommerce, blogs, contenido masivo.

6.3 PPR (Partial Pre-Rendering)

- Divide página en fragmentos precalculados + runtime.
- Reduce TTFB y coste dinámico.

7. Colas, Jobs y Procesamiento Asíncrono

Next.js 16 funciona con colas externas usando Server Actions:

Patrones comunes

  • Webhooks + Server Actions
  • Colas:
  • Inngest
  • Trigger.dev
  • Upstash Queues

Buenas prácticas

  • Delegar trabajos pesados a workers
  • No bloquear server actions
  • Cachear resultados intermedios con tags

8. Observabilidad, Logging y Monitorización

Moderna arquitectura para producción:

Recomendado en Next.js 16

  • Vercel Observability Layer
  • Logging estructurado con Pino
  • Trazas distribuidas (OpenTelemetry integrado)
  • Uso de server-timing headers y analítica nativa

Docs:


9. Arquitectura de CI/CD y Contenedores

9.1 CI/CD

  • Tests en server actions
  • Pruebas de rutas route.js
  • Bundling con Turbopack en CI para detectar fallos

9.2 Contenedores

Patrón recomendado para Next.js 16:

  • Build multi-stage (Node 20+)
  • Adaptar runtime detectado por Next para evitar errores edge/node.

10. DX Avanzado: Monorepos, Workspaces y Generadores

Monorepo con Turborepo

  • Capa de servicios compartidos
  • Reutilización de UI components en librerías internas

Generadores modernos

  • Plop.js
  • Hygen
  • Create fuzzy-templates para scaffolding

11. Seguridad 2025 en Next.js 16

  • Protección de Server Actions (validación estricta)
  • Sanitización con Zod o Valibot
  • Tokens firmados en Edge Runtime
  • Cabeceras de seguridad automáticas en Vercel
  • Autorización contextual por layout/routes

12. Lista de Recursos (formato Obsidian)

Apuntes Next.js 16 — Arquitectura y Fundamentos


1. Panorama General de Next.js 16

  • Next.js 16 introduce mejoras en rendimiento, DX y caché explícita.
  • Anuncio oficial:

2. Bundler: Turbopack por Defecto


3. Caché Explícita con Cache Components

Next.js 16 introduce un sistema de caché manual y predecible, basado en componentes cacheables con una directiva especial:

'use cache'

`

Activación

const nextConfig = {
  cacheComponents: true,
}
export default nextConfig

Recursos oficiales

Nuevas APIs relacionadas


4. Enrutamiento y Prefetching Mejorados


5. Reemplazo de middleware.tsproxy.ts


6. Next.js DevTools MCP

  • Nueva integración con el estándar Model Context Protocol (MCP).
  • Permite a agentes/AI acceder a:

    • info del proyecto
    • rutas
    • estado del build
    • logs
  • Recurso:


7. Mejoras en Server Actions

  • Rendimiento optimizado.
  • Menos sobrecarga en el servidor.
  • Mutaciones más rápidas.
  • Integración con caché y PPR.
  • Recurso:


8. Partial Pre-Rendering (PPR)

  • Sistema híbrido:
    • partes renderizadas estaticamente
    • partes que se resuelven bajo demanda
  • Reduce TTFB y mejora caché granular.
  • Recurso:


9. Mejoras DX (Developer Experience)

  • Turbopack Dev Server más estable.
  • Errores con stacktrace mejorado.
  • Renderizado más consistente entre server/client.
  • Recurso:


10. Resumen de Cambios Clave en Next.js 16

  • Turbopack por defecto → velocidades enormes.
  • Caché explícita con use cache.
  • Sistema de tags para revalidación manual.
  • Nuevo proxy.ts reemplaza middleware.ts.
  • DevTools integrados con MCP.
  • Mejoras en enrutamiento y prefetch.
  • PPR más estable y eficiente.
  • Server Actions más rápidas.

11. Lecturas Recomendadas

Apuntes Next.js 14 — Arquitectura, Fundamentos y Recursos


1. Introducción a Next.js

Next.js es un framework React moderno optimizado para:

  • Renderizado híbrido (SSR, SSG, ISR)
  • Server Components por defecto
  • Server Actions
  • Rutas avanzadas con App Router
  • Optimizaciones automáticas
  • Integración profunda con Vercel

2. Comandos Esenciales

  • Crear proyecto:
	npx create-next-app@latest
	```


---

## 3. Plantillas y Ejemplos Oficiales
### 3.1 Repositorio de ejemplos
- [ ] [Next.js examples — GitHub](https://github.com/vercel/next.js/tree/canary/examples)

### 3.2 Plantillas de Vercel
- [MongoDB Starter](https://vercel.com/templates/next.js/mongodb-starter)
- [Tailwind Blog Starter](https://vercel.com/templates/next.js/tailwind-css-starter-blog)
- [Más plantillas Next + Tailwind](https://vercel.com/templates/tailwind)

---

## 4. Starters y Boilerplates útiles
- [Relivator Next.js Starter (revalidator)](https://github.com/blefnk/relivator-nextjs-starter)
- [Sass Starter (Rapidlaunch)](https://github.com/alifarooq9/rapidlaunch)

---

## 5. [Testing](/testing/testing/) en Next.js
- Test unitarios con React Testing Library, Jest o Vitest  

---

## 6. Fundamentos y Conceptos Modernos de Next.js
### 6.1 Server Components
- Renderizados en el servidor por defecto.
- Evitan enviar JS al cliente salvo cuando se usa `"use client"`.

### 6.2 Server Actions
- Acceso a:
	- cookies
	- headers
	- mutaciones en server
	- múltiples acciones en un mismo formulario
	- revalidación automática
- Integración sencilla con formularios.

### 6.3 Hooks Modernos recomendados
- `useTransition` → para estados concurrentes.
- `useReducer` → estados complejos.
- Evitar el uso excesivo de `useEffect`.

### 6.4 Buenas prácticas
- Priorizar **Server Components**.
- Evitar fetch en el cliente salvo necesario.
- Reducir estados globales (usar server actions + fetch).
- Integración simple con Firebase, Prisma, Convex, etc.

---

## 7. Plantillas y Ejemplos Destacados 

### 7.1 E-commerce con Next.js
- [Next Amazona v2 (Next eCommerce)](https://github.com/basir/next-amazona-v2)

### 7.2 Blog con MongoDB
- [Next Blog — Repo completo](https://github.com/safak/next-blog/tree/completed)
- [Diagrama arquitectura DB](https://app.eraser.io/workspace/aSshPSKlaKr8XiddBdlv)

### 7.3 Clones de Notion (Next.js + Prisma + Tailwind)
- https://github.com/aafrzl/nextion
- https://github.com/abdallaamin/jotion
- https://github.com/konstantinmuenster/notion-clone
- https://github.com/dj1samsoe/notion-clone

### 7.4 App Threads Clone
- [Repo oficial (adrianhajdin)](https://github.com/adrianhajdin/threads)

### 7.5 App Gestor de Contraseñas — Next 14 + PWA
- [GitHub tarredev-password](https://github.dev/ratasi/tarredev-password)
- Incluye:
	- autenticación
	- rutas protegidas
	- Prisma + DB
	- PWA

---

## 8. Integraciones y Stacks útiles con Next.js
- **Convex + Tailwind + React + Next.js**
	- Uso recomendado para BaaS reactivo.
- **Firebase (Auth, Firestore, Storage)**  
- **Prisma ORM + PlanetScale / Neon / Supabase**
- **Auth.js (NextAuth)** para autenticación moderna.

## 9. Recursos
- [Next.js Official Learn — Foundations](https://nextjs.org/learn/foundations/about-nextjs/what-is-nextjs)

- NEXT.JS COOKBOOK
	- NEXT.JS COOKBOOK > Introduction



# Curso de Next 13 (2023) 

## Netflix Clone 🎈
- [github nextflix](https://github.com/rajput-hemant/nextflix)
- Tecnologías usadas: framer motion, authentication, css modules, ISR, login sin password, GraphQL, authentication SDK Hasura, deploy en Vercel
- Integración avanzada con animaciones, optimización de UI/UX y pipelines de despliegue

## Coffee Shop App ✔
- npx: instalación temporal, prueba de paquetes, npm vs npx — ¿Cuál es la Diferencia-
- Guías de actualización:
	- [guia de actualizaciones de next](https://nextjs.org/docs/pages/building-your-application/upgrading)
	- `npm audit`, `npm audit fix`, `npm install next@latest`
	- [webpack 5](https://nextjs.org/docs/messages/webpack5)
	- `--force`, actualizar react y react-dom
- [github coffee store](https://github.com/BernieTv/Coffee-Connoisseur/blob/main/pages/index.js)
- Localizador de stores de café
- Workspace: 

## Features

### Tecnologías
- Node.js, SEO, Static Site Generation, Server Side Rendering, Incremental Site Regeneration
- Hydration, Serverless Functions, Airtable, SWR, React Hooks
- React 18, [react server components](https://nextjs.org/docs/app/building-your-application/rendering/server-components)
- Server side streaming: [loading ui and streaming](https://nextjs.org/docs/app/building-your-application/routing/loading-ui-and-streaming)
- ES Modules, importación desde CDNs
- [ISR](https://nextjs.org/docs/pages/building-your-application/data-fetching/incremental-static-regeneration): revalidate, fallback
- Edge functions, Using Next.js’ middleware and Edge Functions - LogRocket Blog, [edge runtime api](https://www.framer.com/motion/)
- Comparación: Next.js vs Create React App
- SWC compiler, *websockets*

### Middleware
- Configuraciones entre API call y request
- Autenticación, protección de bots, redirects/rewrites, soporte para navegadores antiguos
- Flags, tests A/B, analítica, logging
- `pages/_middleware.js`
- Middleware API

### Serverless Functions
- Guardar credenciales en el lado del servidor
- API key, carpeta `/api`
- Funcionan bajo demanda: el servidor se enciende al recibir un request

### Entry Point: `_app`
- Componentes compartidos globalmente
- Afecta el body
- Cabezal con `<Head>` separado es posible

### Rendimiento
- Code splitting, `next build`, chunks
- Minificación sin necesidad de webpack
- Image optimization: carga lazy
- Prefetching de assets

### SEO
- Metadata, bots
- `<Head>` component
- Ranking, crawling, indexing
- Meta tags, alt en imágenes, semántica HTML
- Análisis de base de datos para contenido dinámico

### Renderizado y Data Fetching

#### Static Site Generation (SSG)
- Generación en build time
- Static data + external API
- Data almacenada en CDN
- `getStaticProps`

#### Server Side Rendering (SSR)
- Generado en cada request
- `getServerSideProps`
- Ideal para data que cambia rápido (ej: news feed)

#### Incremental Static Regeneration (ISR)
- Mezcla SSG + SSR
- Intervalos de regeneración
- Primera request stale, segunda fresh
- `getStaticProps` con `revalidate`

#### Client Side Rendering (CSR)
- Render con React en el cliente
- Prerender parcial + data fetching client
- [client side fetching](https://nextjs.org/docs/pages/building-your-application/data-fetching/client-side)
- Ideal para dashboards

#### Pre-rendering y Hydration
- HTML estático previo a React
- Bots y SEO
- Server rendering previo a hydration

### Routing
- File-system routing
- History API
- Rutas:
	- `index.js`: ruta raíz
	- Nested routes
	- Dynamic routes: `[id].js`
		- `import { useRouter } from 'next/router'`
		- Router query object
- Linking:
	- `next/link` — [link component](https://nextjs.org/docs/pages/api-reference/components/link)
	- Prop `scroll`: diferencias entre rutas dinámicas y no dinámicas
- API folder: backend logic + SSR

## Intro Componentes
- [x] [video curso](https://www.bilibili.com/video/BV1Um4y187Mz/?spm_id_from=333.337.search-card.all.click)
- [x] [video curso 2](https://www.bilibili.com/video/BV1Gc411N7ft/?spm_id_from=333.788.recommend_more_video.0)
- [x] [video cuso 3](https://www.bilibili.com/video/BV1Ss4y1M7HS/?p=11&spm_id_from=pageDriver)
- [ ] [video curso 4](https://www.bilibili.com/video/BV1Bs4y1M7nR/?spm_id_from=333.999.0.0)
- [ ] [video curso 5](https://www.bilibili.com/video/BV1Gh411F7BA/?spm_id_from=333.788.recommend_more_video.1)
- [x] crear app con next API y wireframe 🔺
- [ ] editar página 404 personalizada
- [ ] footer component
- [ ] fondo con [gradiente](https://meshgradient.com/)
- [ ] componente banner:
	- Props: buttonText, handleOnClick → `handleOnBannerBtnClick`
- Head component de Next
- useRouter: [next useRouter](https://nextjs.org/docs/pages/api-reference/functions/use-router)
- `next/link`
- `next/image`: [image component](https://nextjs.org/docs/pages/api-reference/components/image)
- [ ] Añadir fuentes:
	- En `_document.js` usando `<Head>`
	- Extender document model: [custom document](https://nextjs.org/docs/pages/building-your-application/routing/custom-document)
	- [Font Optimization](https://nextjs.org/docs/pages/building-your-application/optimizing/fonts)
- [ ] card component:
	- Props dinámicas `name` y `href`

### Otros y Refactor
- Condiciones de render: `stores.length > 0 &&`
- Error al acceder al JSON: nested arrays
- Destructuring y fallback: `router.fallback`
- Generación de paths dinámicos:
	- `{ params: { id: coffeeStore.id.toString() }}`
- Función de voto: `{handleUpVoteButton}`
- `/lib` directory:
	- `fetchCoffeeStores()`
	- `getUrlForCoffeeStores(latLong, 'coffee stores', limit)`
- Fetch para obtener ID de API
- `async` en `getStaticPaths`
- Fallback de imágenes:
	- `imgUrl={coffeeStore.imgUrl || 'url'}`

## CSS Confs
- CSS variables: `--var`, declarar en `:root {}`
- Mobile first, media queries, breakpoints
- [ ] Layout home con grid:
	- [CSS-Guia completa > 13-CSS Grid](/uncategorized/css-guia-completa/#13-css-grid)
	- columnas, containers, cardLayout
- Generar CSS tipo glass: [css glass generator](https://ui.glass/generator/)
- Clases CSS dinámicas que no colisionan
- Combinar clases con classNames:
	- `import cls from "classnames"`
- Icons SVG desde [google fonts icons](https://fonts.google.com/icons)



## getStaticProps
- [ ] [getStaticProps](https://nextjs.org/docs/pages/building-your-application/data-fetching/get-static-props)
- Recibir props en **Home** desde el servidor.
- Guardar contenido de cada card en la CDN; posible integrar una API.
- Devuelve **static data**.
- Usado en rutas dinámicas: ``[id].js``.
- Props como key/value; la key suele ser el **id**.
- Se ejecuta **solo en el servidor**, nunca en el cliente.
- Corre en **build time**, excepto en dev mode que corre en client/server.
- Devolver data como props y usar ``find()``:
	- Comparar un id dinámico con ``params.id``.
	- Convertir ``store.id`` mediante ``toString()``.
- ``useRouter()`` para acceder al id: ``router.query.id``.
- Usar ``params`` directamente o desde ``staticProps.params``.

### Ejemplo getStaticProps

```js
export async function getStaticProps(context) {
const { params } = context;
const stores = await fetchStores();
const store = stores.find((s) => s.id.toString() === params.id);

return {
	props: {
		coffeeStore: store || null,
	},
};
}

`


getStaticPaths

  • getStaticPaths
  • Genera páginas estáticas para rutas dinámicas.
  • Define los paths prerenderizados.
  • Solo se exporta en pages dinámicas.
  • Se usa junto con getStaticProps.
  • Solo corre en build time.
  • Devuelve array de paths y un fallback.

fallback key

  • fallback: false
  • Si la ruta no existe → 404.
  • fallback: true
  • Descarga la página bajo demanda.
  • Útil para muchas rutas.
  • Si el find() devuelve undefined → error en carga.
  • router.fallback == true
  • Indica que la página está generándose.

Ejemplo getStaticPaths

export async function getStaticPaths() {
const stores = await fetchStores();

const paths = stores.map((store) => ({
	params: { id: store.id.toString() },
}));

return {
	paths,
	fallback: true,
};
}

Obtener data de JSON o APIs

  • Usar id en href para rutas dinámicas.
  • Importar JSON y mapear contenido.
  • Configurar dominio de imágenes en next.config.js.

API Foursquare

  • Foursquare Docs
  • Place Search API para localizar stores.
  • Query de tiendas por coordenadas.
  • Limitar resultados + control de rate limiting.
  • Integrar en getStaticProps como función async:
  • fetch(url, options)
  • response.json()
  • props: { coffeeStores: data.results }

Variables de entorno

  • Environment variables
  • Guardar keys en process.env.
  • En options:
    Authorization: process.env.NEXT_PUBLIC_FOURSQUARE_API_KEY

API Unsplash

  • Unsplash API
  • Obtener imágenes dinámicas para cada store.
  • SDK: https://github.com/unsplash/unsplash-js
  • Función search.getPhotos().
  • Keys en env.
  • Mapear urls e integrarlas en fetchCoffeeStores.
  • Condicionales:
  • Si neighborhood vacío → ''
  • Si imgUrl vacía → null

Buscar tiendas cercanas — geolocation API

  • Geolocation API
  • Crear hook: useTrackLocation.
  • Obtener lat/long del usuario.
  • Solicitar permisos.
  • Manejar errores y estado:
  • isFindingLocation
  • setLocationErrorMsg('')

Ejemplo Hook

const useTrackLocation = () => {
const [latLong, setLatLong] = useState('');
const [errorMsg, setErrorMsg] = useState('');

function success(position) {
	const latitude = position.coords.latitude;
	const longitude = position.coords.longitude;
	setLatLong(`${latitude},${longitude}`);
	setErrorMsg('');
}

function error() {
	setErrorMsg('No se pudo obtener la ubicación');
}

return { latLong, errorMsg };
};

Context y Reducer

  • fetch async a fetchCoffeeStores con limit.
  • useEffect + try/catch en Home.
  • Crear state global con Context API.
  • En env usar NEXT_PUBLIC_.
  • [coffeeStores, setCoffeeStores] para stores cercanas.
  • Extraer desde context: const { coffeeStores, latLong } = state
  • Manejar errores:
  • coffeeStoresError
  • Problemas con props estáticas en [id].js + fallback.
  • fallback false garantiza 404 si no existe store.

Context API

  • Using React Context for State Management with Next.js
  • StoreProvider en _app.js.
  • Proveer: <StoreContext.Provider value=>

Reducer

  • Extracting State Logic into a Reducer – React
  • Manejo de múltiples estados.
  • ACTION_TYPES para dispatch.
  • storeReducer aplica cambios via payload.

Ejemplo Reducer

export const storeReducer = (state, action) => {
switch (action.type) {
	case ACTION_TYPES.SET_LAT_LONG:
		return { ...state, latLong: action.payload.latLong };
	case ACTION_TYPES.SET_COFFEE_STORES:
		return { ...state, coffeeStores: action.payload.coffeeStores };
	default:
		return state;
}
};

Data no prerenderizada + fallback en [id].js

  • Combinar props estáticas + context + SWR (opcional).
  • Acceder al id: const { id } = router.query
  • Obtener stores: const { state: { coffeeStores } } = useContext(StoreContext)
  • Usar initialProps.
  • Comprobar en useEffect:
  • Si hay stores.
  • Si initialProps está vacío → usar context.
  • async handleCreateCoffeeStore para crear store en DB si no existe.

Declarar props de store

const { id, name, voting, imgUrl, neighbourhood, address } = coffeeStore;

Dependencias del useEffect

[id, initialProps.coffeeStore, coffeeStores, initialProps]

Utilidad isEmpty

  • Crear utils/index.js.
  • Comprobar si un objeto no tiene keys.
  • Usado para decidir si usar fallback o props estáticas.

Código isEmpty

export const isEmpty = (obj) => {
return Object.keys(obj).length === 0;
};

Evitar circular dependencies

  • Guardar context en carpeta dedicada: store-context.js.
  • Importar solo desde _app.js o componentes que lo usen.

API Routes y Serverless Functions

  • API Routes
  • Arquitectura híbrida: server-side + client-side en el mismo proyecto.
  • Directorio: /pages/api/*.js → código backend dentro de Next.
  • Cada file exporta una única función como export default.
  • Firma estándar:
	export default function handler(req, res) {
		res.status(200).json({ message: 'OK' });
	}
	```

- El archivo define la **ruta dinámica**:
	- ``pages/api/coffee-store.js`` → ``/api/coffee-store``
	- ``pages/api/[slug].js`` → rutas capturadas automáticamente.
	- ``pages/api/[...slug].js`` → capturar *todas las rutas* (catch-all).
- Orden de prioridades del enrutado basado en el filesystem.

### REST y métodos HTTP
- GET → obtener recursos.
- POST → crear.
- PUT → actualizar.
- DELETE → eliminar.
- La forma final de la API depende de la ruta definida en el filesystem.

### Serverless Functions
- Lambdas ejecutadas bajo demanda (Vercel / Netlify).
- Infraestructura manejada por el proveedor.
- Timeout, cold starts, rate limits.
- Ideal para:
	- Ops ligeras.
	- Integración a DBs o APIs externas.
	- Auth + operaciones CRUD.

---

## Obtener stores por geolocación usando API Routes
- Crear endpoint: ``/api/getCoffeeStoresByLocation``.
- Reutilizar ``fetchCoffeeStores`` dentro de la API route.
- Ejemplo:
	
```js
	const getCoffeeStoresByLocation = async (req, res) => {
		try {
			const { latLong, limit } = req.query;
			const stores = await fetchCoffeeStores(latLong, limit);
			res.status(200).json(stores);
		} catch (error) {
			res.status(500).json({ message: 'Error fetching', error });
		}
	};
	```

- **Importante**: si no se hace ``return`` o ``res.end()`` → la función se queda pendiente.

### Llamar a la API en Home con useEffect

```js
const fetchedCoffeeStores = await fetch(
`/api/getCoffeeStoresByLocation?latLong=${latLong}&limit=30`
);
const data = await fetchedCoffeeStores.json();
setCoffeeStores(data);
setCoffeeStoresError('');

`

Integración con getStaticProps

  • Añadir fetchCoffeeStores para cargar data inicial.
  • Next permite ejecutarlo en servidor en build:
const coffeeStores = await fetchCoffeeStores();
return { props: { coffeeStores } };

Arquitectura + Persistencia en Airtable

  • CSR: obtener stores cercanas vía API Routes.
  • SSG: stores por ciudad pre-generadas.
  • Flujo mixto:
    1. SSG carga stores iniciales.
    2. Context transmite la data.
    3. Airtable persiste cada store si no existe.
  • Problema: al refrescar, el context se pierde → usar DB.

Configuración de Airtable

  • Crear base:
  • id (string)
  • name
  • voting (number)
  • address
  • neighbourhood
  • Docs:
  • https://airtable.com/developers/web/api/introduction
  • https://github.com/Airtable/airtable.js
  • Variables:
  • API key
  • Base ID
  • En .env.local: AIRTABLE_API_KEY=...
    AIRTABLE_BASE_ID=...

Crear Coffee Store (POST)

  • Endpoint: /api/createCoffeeStore
  • Pasos:
    1. Leer req.method
    2. Validar req.body
    3. Buscar si existe ID con findRecordByFilter
    4. Crear si no existe
    5. Minificar resultado
const records = await table.create([
{
	fields: {
		id,
		name,
		voting: voting || 0,
		address: address || '',
		neighbourhood: neighbourhood || '',
	},
},
]);

Minificación de registros

const getMinifiedRecord = (record) => ({
recordId: record.id,
...record.fields,
});

const getMinifiedRecords = (records) => {
return records.map(getMinifiedRecord);
};

Usar Postman para probar

  • GET: /api/getCoffeeStoreById?id=123
  • POST: /api/createCoffeeStore
  • PUT: /api/favouriteCoffeeStoreById (para votar)

Integración en [id].js

  • Componentes manejan data combinada:
    1. initialProps (SSG)
    2. Context (CSR)
    3. Airtable DB

Flujo createCoffeeStore en frontend

const handleCreateCoffeeStore = async (coffeeStore) => {
try {
	const res = await fetch('/api/createCoffeeStore', {
		method: 'POST',
		body: JSON.stringify({
			...coffeeStore,
			neighbourhood: coffeeStore.neighbourhood || '',
		}),
	});
	await res.json();
} catch (error) {}
};
  • Asegurar que IDs son strings.
  • Señal: votos no incrementan con SSG → depende de la DB.

SWR — Stale While Revalidate

  • https://swr.vercel.app/es-ES
  • Sincronizar votaciones entre UI y DB.
  • Soluciona actualización asíncrona + cache.

Implementación SWR

  • Hook:
const { data, error } = useSWR(
	`/api/getCoffeeStoreById?id=${id}`,
	fetcher
);
  • useEffect escucha cambios:
useEffect(() => {
	if (data && data.length > 0) {
		setCoffeeStore(data[0]);
		setVotingCount(data[0].voting);
	}
}, [data]);
  • Si error → mostrar mensaje.

### Upvote (PUT)

  • Endpoint: /api/favouriteCoffeeStoreById
  • Lógica:
    1. Validar ID
    2. Buscar registro con findRecordByFilter
    3. Incrementar voto:
	const calculateVoting = parseInt(record.voting) + 1;
	```

4. Actualizar:
	
```js
	const updateRecord = await table.update([
		{
			id: record.recordId,
			fields: { voting: calculateVoting },
		},
	]);
	```

5. Devolver minificado

### Efecto colateral

- La app genera nuevos inserts cuando encuentra stores cercanas desde Foursquare → se escriben en Airtable.

---

## Deploy: Vercel + Netlify

### Build

- ``npm run build`` → ``next build``
- ``next start`` → producción local
- Output con iconos para saber si las páginas son:
- SSG
- ISR
- SSR

### Vercel

- Deploy automático con Git.
- Logs de serverless functions.
- Envs separadas para dev/prod.
- CDNs sirven las páginas pre-generadas.

### Netlify

- Next.js plugin automático.
- Serverless functions soportadas.
- Imagenes:
- Requiere configuración extra si Next < 13.5
- ``next.config.js``:
	
```js
	images: {
		domains: ['images.unsplash.com'],
	}
	```

- Si falla → usar ``<img />`` HTML en vez de ``next/image``.

### Optimizaciones

- Comprobar con Pagespeed / Lighthouse.
- Asegurar:
- ``alt`` en imágenes.
- ``<Head>`` con metas por página.
- Meta description dinámica.

---

## Coffee Store — Snippets útiles

- `const Footer = () => {`
- `export default function Post({ postData, allPostsData }) {}`
- `import Document, { Html, Head, Main, NextScript } from 'next/document'`
- `class MyDocument extends Document {}`
- `className={cls("glass", styles.container)}`
- `module.exports = { images: { domains: ['...'], } }`
- `const getListOfCoffeeStorePhotos = async () => {}`
- `return unsplashResults.map(r => r.urls['small'])`
- `const [locationErrorMsg, setLocationErrorMsg] = useState('')`
- `const [isFindingLocation, setIsFindingLocation] = useState(false)`
- `const [coffeeStore, setCoffeeStore] = useState(initialProps.coffeeStore)`

- Airtable utils:
	- `const table = base('coffee-stores');`
	- `const findRecordByFilter = async (id) => {}`
	- `const createRecords = await table.create([ ... ])`
	- `return records.map(getMinifiedRecord)`
	- `const { id, name, neighbourhood, address, imgUrl, voting } = req.body`
# Omnivore Nextjs

```base
type: list
name: "Notas con #nextjs en Omnivore"
order:
  - property: date_saved
    direction: desc
columns:
  - file.name
  - date_saved
filters:
  and:
    - file.inFolder("Omnivore")
    - file.hasTag("nextjs")
views:
  - type: table
    name: Table
    sort:
      - property: file.mtime
        direction: DESC

Omnivore React

type: list
name: "Notas con #react en Omnivore"
order:
  - property: date_saved
    direction: desc
columns:
  - file.name
  - date_saved
filters:
  and:
    - file.inFolder("Omnivore")
    - file.hasTag("react", "React")
views:
  - type: table
    name: Table
    order:
      - file.name
      - source_domain
    sort:
      - property: file.mtime
        direction: DESC

Omnivore next js highlights

// Configuraciones
const tagName = "nextjs";
const headName = "Highlights";

// Crear un array para almacenar las páginas que contienen el tag "nextjs"
let pagesWithNextjs = [];

// Filtrar y recopilar las páginas que tienen el tag "nextjs"
dv.pages('"Omnivore"').forEach(page => {
    if (page.tags && page.tags.includes(tagName)) {
        pagesWithNextjs.push(page);
    }
});

// Ordenar las páginas por 'date_saved' en orden descendente
pagesWithNextjs.sort((a, b) => {
    return b.date_saved.localeCompare(a.date_saved);
});

// Iterar sobre cada página filtrada y ordenada
for (const page of pagesWithNextjs) {
    const content = await dv.io.load(page.file.path);
    const lines = content.split('\n');
    let output = [];
    let insideHead = false;

    // Recorrer cada línea del contenido
    for (const line of lines) {
        if (line.startsWith("## " + headName)) {
            insideHead = true;
        } else if (line.startsWith("# ") && insideHead) {
            insideHead = false;
            break;  // Salir del bucle cuando se encuentra el próximo encabezado
        } else if (insideHead) {
            output.push(line);
        }
    }

    // Mostrar el contenido dentro del encabezado "Highlights" si hay contenido
    if (output.length > 0) {
        // Mostrar el enlace a la página arriba del contenido de "Highlights"
        dv.el('p', page.file.link).addClass("page-link");

        // Mostrar el contenido dentro del encabezado "Highlights"
        dv.paragraph(output.join('\n')).addClass("omni-pr");
    }
}