Skip to content

Latest commit

 

History

History
148 lines (112 loc) · 5.54 KB

File metadata and controls

148 lines (112 loc) · 5.54 KB

Todo Frontend

Next.js web application that provides the UI for the TODO app, with built-in OpenTelemetry instrumentation.

Architecture

graph LR
    Browser[Browser] -->|HTTP| Next[Next.js Server]
    Next -->|SSR| Page[Server Component<br/>page.tsx]
    Page -->|fetch| Backend[Backend API]
    Browser -->|Client Actions| API[API Route<br/>/api/todos]
    API -->|fetch| Backend
    Next --> OTel[OpenTelemetry SDK]
    OTel -->|OTLP HTTP| Collector[OTel Collector]
Loading

The frontend is built with Next.js and React (TypeScript). It uses a hybrid rendering strategy:

  • Server-side: The main page (page.tsx) is a React Server Component that fetches todos from the backend at request time and renders the initial HTML.
  • Client-side: Interactive operations (create, toggle, delete) are handled by client components (TodoList, AddTodo, TodoItem) that call the frontend's own API proxy routes.

Source Layout

File / Directory Purpose
src/app/page.tsx Server component – fetches and renders the todo dashboard
src/app/api/todos/route.ts API proxy – forwards client requests to the backend with tracing
src/components/TodoList.tsx Client component – manages todo state and user interactions
src/components/TodoItem.tsx Client component – renders a single todo row
src/components/AddTodo.tsx Client component – form for creating new todos
src/instrumentation.ts OpenTelemetry SDK initialization (Next.js instrumentation hook)
next.config.ts Next.js configuration (output: "standalone")

Connection to the Backend

sequenceDiagram
    participant B as Browser
    participant F as Frontend (Next.js)
    participant BE as Backend (Express)
    participant R as Redis

    B->>F: GET / (page load)
    F->>BE: GET ${BACKEND_URL}/todos
    BE->>R: KEYS todo:*
    R-->>BE: keys
    BE-->>F: JSON array
    F-->>B: Rendered HTML

    B->>F: POST /api/todos
    F->>BE: POST ${BACKEND_URL}/todos
    BE->>R: HSET todo:{id}
    R-->>BE: OK
    BE-->>F: 201 Created
    F-->>B: JSON response
Loading

The frontend connects to the backend through the BACKEND_URL environment variable, which must be set (the app will crash on startup without it). This URL should include the base path if one is configured on the backend (e.g. http://traefik/api).

Two connection paths exist:

  1. Server-side renderingpage.tsx calls fetch("${BACKEND_URL}/todos") directly during SSR.
  2. Client-side actions – Client components call /api/todos on the frontend, which proxies the request to ${BACKEND_URL}/todos on the backend.

Configuration

Configuration is done via environment variables.

Variable Default Description
BACKEND_URL (none – required) Backend API base URL including any base path (e.g. http://traefik/api)
OTEL_SERVICE_NAME todo-frontend Service name reported to the OTel collector
OTEL_EXPORTER_OTLP_ENDPOINT http://localhost:4318 OTLP HTTP endpoint for traces, metrics, and logs
OTEL_EXPORTER_OTLP_PROTOCOL Protocol (http/protobuf in docker-compose)
OTEL_TRACES_EXPORTER Traces exporter type (otlp)
OTEL_METRICS_EXPORTER Metrics exporter type (otlp)
OTEL_LOGS_EXPORTER Logs exporter type (otlp)
NEXT_RUNTIME Must be set to nodejs for OpenTelemetry to activate

OpenTelemetry Integration

flowchart TD
    subgraph Frontend Process
        Hook[instrumentation.ts<br/>register hook]
        SSR[Server Components]
        APIRoute[API Route Handlers]
    end
    Hook -->|Traces| Collector[OTel Collector]
    Hook -->|Metrics| Collector
    Hook -->|Logs| Collector
    APIRoute -->|Custom Spans| Hook
Loading

OpenTelemetry is integrated using Next.js's instrumentation.ts hook. The register() function is called automatically by Next.js at startup. It only initializes the SDK when NEXT_RUNTIME equals nodejs (skipped in Edge runtime and during build).

Traces

  • Auto-instrumentation for outbound HTTP requests is enabled.
  • Manual spans are created in the API proxy (src/app/api/todos/route.ts) for each operation:
    • frontend.todos.list – listing todos
    • frontend.todos.create – creating a todo
    • frontend.todos.toggle – toggling checked state
    • frontend.todos.delete – deleting a todo

Each span records relevant attributes (todo.id, todo.title, todo.count) and captures exceptions on failure.

Metrics & Logs

Metrics and logs are exported via OTLP HTTP with a 10-second export interval, matching the backend configuration.

Building the Docker Image

The Dockerfile uses a multi-stage build with Node 25 Alpine and Next.js standalone output.

# Build the image
docker build -t todo-frontend ./frontend

# Run the container
docker run -p 3000:3000 \
  -e BACKEND_URL=http://host.docker.internal:4000 \
  todo-frontend
Stage What happens
builder Installs dependencies (npm ci), runs next build producing a standalone bundle
runtime Copies the .next/standalone output and static assets, exposes port 3000, starts with node server.js

The standalone output mode (configured in next.config.ts) bundles all dependencies into a self-contained directory, resulting in a smaller production image.

Local Development

# Install dependencies
npm install

# Start development server
npm run dev

# Build for production
npm run build

# Start production build
npm start