Skip to content

Repository files navigation

Heute: Nachrichten (DE)

A mobile-first German news app that adapts today's headlines to your CEFR level (A1–C1) using OpenAI.

Overview

  • Fetches 5 German RSS feeds daily (Tagesschau, ZEIT, Spiegel, FAZ, Süddeutsche)
  • Deduplicates and normalizes articles
  • Generates CEFR-leveled German summaries via OpenAI
  • Stores everything in Supabase
  • Single-page Next.js frontend with expandable card UI

Prerequisites


Setup

1. Clone and install

git clone <repo-url>
cd german-news-app
npm install

2. Configure environment variables

cp .env.local.example .env.local

Edit .env.local and fill in all values:

Variable Where to find it
NEXT_PUBLIC_SUPABASE_URL Supabase → Project Settings → API → Project URL
NEXT_PUBLIC_SUPABASE_ANON_KEY Supabase → Project Settings → API → anon key
SUPABASE_SERVICE_ROLE_KEY Supabase → Project Settings → API → service_role key
OPENAI_API_KEY platform.openai.com → API Keys
CRON_SECRET Any random string (e.g. openssl rand -hex 32)
OPENAI_MODEL Optional. OpenAI model ID (default: gpt-5-nano-2025-08-07). For GPT-5 models, sampling params like temperature are not sent (model requires defaults).
MAX_HEADLINES_PER_DAY Optional. Max headlines to generate OpenAI summaries for per run (default: 20). Set to 2 or 3 locally to limit API cost.

3. Run Supabase migration

Open the Supabase SQL Editor for your project and run the contents of:

supabase/migrations/001_initial.sql

This creates the four required tables: daily_issue, headline, headline_summary, pipeline_run.

4. Run locally

npm run dev

Visit http://localhost:3000.

The page will show an empty state until the pipeline has been run.


Triggering the Pipeline

The pipeline fetches today's RSS feeds, deduplicates headlines, and generates OpenAI summaries for all CEFR levels.

Manually via curl

curl -X POST http://localhost:3000/api/internal/run-daily-pipeline \
  -H "x-cron-secret: your-cron-secret-here"

Replace your-cron-secret-here with the value of CRON_SECRET in your .env.local.

A successful response looks like:

{
  "ok": true,
  "issueDate": "2026-02-19",
  "headlinesProcessed": 18,
  "summariesGenerated": 15,
  "errors": [],
  "durationMs": 42000
}

Note: The pipeline calls OpenAI once per new headline (generating all 5 levels in a single request). With 20 headlines it typically takes 1–3 minutes.

Re-running is safe

The pipeline is fully idempotent. Re-running on the same day:

  • Skips headlines already in the database (dedupe by SHA-256 key)
  • Skips headlines that already have all 5 summaries
  • Only generates missing summaries

Automated scheduling (production)

Set up a daily cron job to call the endpoint at a fixed time (e.g. 06:00 Europe/Berlin). Services that can trigger HTTP endpoints on a schedule:

  • Vercel Cron Jobs (add to vercel.json)
  • GitHub Actions scheduled workflow
  • Upstash QStash
  • Any cron service that can POST to a URL

Example vercel.json for Vercel Cron:

{
  "crons": [
    {
      "path": "/api/internal/run-daily-pipeline",
      "schedule": "0 5 * * *"
    }
  ]
}

Add CRON_SECRET as an environment variable in Vercel dashboard.


API Reference

GET /api/news/today?level=B1

Returns today's headlines at the specified CEFR level.

Query params:

  • level — one of A1, A2, B1, B2, C1 (default: B1)

Response:

{
  "issueDate": "2026-02-19",
  "level": "B1",
  "lastUpdated": "2026-02-19T06:42:00Z",
  "items": [
    {
      "id": "uuid",
      "titleDe": "...",
      "summaryDe": "...",
      "bulletsDe": ["...", "...", "..."],
      "sourceName": "Tagesschau",
      "canonicalUrl": "https://...",
      "publishedAt": "2026-02-19T05:00:00Z"
    }
  ]
}

POST /api/internal/run-daily-pipeline

Triggers the daily pipeline. Requires x-cron-secret header.


Project Structure

src/
  app/
    page.tsx                              — Main SPA page
    layout.tsx
    globals.css
    api/
      news/today/route.ts                 — GET headlines by level
      internal/run-daily-pipeline/route.ts — POST pipeline trigger
  components/
    LevelSelector.tsx
    HeadlineCard.tsx
    ExpandedCard.tsx
    SkeletonCard.tsx
  hooks/
    useNews.ts
  lib/
    types.ts
    supabase-browser.ts                   — Client-side (anon key)
    supabase-server.ts                    — Server-side (service role)
    rss.ts                                — RSS fetch + dedupe
    openai.ts                             — CEFR summary generation
    pipeline.ts                           — Full pipeline orchestration
supabase/
  migrations/
    001_initial.sql

About

An app that shares daily news from German news outlets, with content in different CEFR language level options.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages