12. modulKonfiguráció és a négy szint
Nuxt for Devs · 12. modul

Konfiguráció: nuxt.config, runtimeConfig, TypeScript

Ez a modul egyetlen kérdés köré épül: hol lakik egy érték, és ki látja? Négy szint van, és a köztük lévő különbség nem stílus kérdése — az egyiken elhelyezett API-kulcs a felhasználó böngészőjében köt ki. Végigvesszük a négyet, megnézzük, hogyan ellenőrzöd, hogy nem szivárogtál, és hogy a .env-től a Cloudflare-secretig mi hova kerül.

12.1A négy szint

① BUILD-TIME KONSTANS nuxt.config értékei · app.config.ts · import.meta.env — a bundle-be ÉGVE, deploy nélkül nem változtatható kliens is látja ② runtimeConfig felső szint → csak a szerver látja · public: {…} → a kliens IS futásidőben felülírható env-változóval (NUXT_ prefix) — deploy nélkül állítható szerver-only rész public rész → kliens ③ vars (wrangler.jsonc) nem titok: látszik a configban és a dashboardon ④ secret (wrangler secret put) titkosítva tárolva, kiolvashatatlan — ide való minden kulcs + BINDINGOK — nem érték, hanem képesség D1, R2, KV, Hyperdrive, Queues: nincs bennük kulcs, amit el lehetne lopni (11. modul)
A ③ és a ④ ugyanoda érkezik a kódban (az env-be), de a tárolásuk és a láthatóságuk gyökeresen más.
SzintHol írodKi látjaVáltoztatható deploy nélkül?
① Build-timenuxt.config.ts, app.config.tsA kliens is (bele van fordítva)Nem
runtimeConfig (felső szint)nuxt.config.ts + envCsak a szerverIgen — env-változóval
runtimeConfig.publicugyanottA kliens isIgen
③ Cloudflare varswrangler.jsoncAki a repót vagy a dashboardot látjaDeployjal
④ Cloudflare secretwrangler secret putSenki — csak a futó kódIgen
A leggyakoribb hiba egyetlen szó. Valaki beírja a Stripe titkos kulcsát a runtimeConfig.public alá, mert ott „működik a kliensről is”. Onnantól a kulcs benne van a HTML-ben és a JS-bundle-ben — bárki megnézheti. A public szó azt jelenti: nyilvános. Ha egy értéknek a böngészőben nincs keresnivalója, nem kerülhet oda.

12.2runtimeConfig a gyakorlatban

// nuxt.config.ts
export default defineNuxtConfig({
  runtimeConfig: {
    // ── csak a szerver látja ──────────────────────────────
    sessionPassword: '',          // NUXT_SESSION_PASSWORD
    stripeSecretKey: '',          // NUXT_STRIPE_SECRET_KEY
    resendApiKey:    '',          // NUXT_RESEND_API_KEY

    upstream: {
      baseUrl: '',               // NUXT_UPSTREAM_BASE_URL
      token:   '',               // NUXT_UPSTREAM_TOKEN
    },

    // ── a kliens IS látja ─────────────────────────────────
    public: {
      appName:        'Scoope',    // NUXT_PUBLIC_APP_NAME
      siteUrl:        '',          // NUXT_PUBLIC_SITE_URL
      stripePublicKey:'',          // NUXT_PUBLIC_STRIPE_PUBLIC_KEY  ← a pk_ kezdetű, az OK
      sentryDsn:      '',          // NUXT_PUBLIC_SENTRY_DSN
    },
  },
})

Az üres string nem véletlen: a kulcsnak léteznie kell a konfigban, hogy az env-változó felül tudja írni. Ha nincs ott, a NUXT_… változó nem csinál semmit — ez a leggyakoribb „miért üres a config?” ok.

Az elnevezési szabály

runtimeConfig.stripeSecretKey        →  NUXT_STRIPE_SECRET_KEY
runtimeConfig.upstream.baseUrl       →  NUXT_UPSTREAM_BASE_URL
runtimeConfig.public.siteUrl         →  NUXT_PUBLIC_SITE_URL

Prefix mindig NUXT_, a beágyazott szintek aláhúzással, a camelCase pedig nagybetűs, aláhúzásos alakra bomlik. Az értékek automatikusan típusra konvertálódnak — ha a "3000"-et stringként akarod, idézőjelezd a .env-ben.

Olvasás — és a Workers-es csavar

// szerveroldalon: MINDIG add át az event-et
export default defineEventHandler(async (event) => {
  const cfg = useRuntimeConfig(event)      // ← az event nem opcionális szeszély
  const key = cfg.stripeSecretKey
})

// kliensoldalon: csak a public érhető el
const { public: pub } = useRuntimeConfig()
console.log(pub.siteUrl)
Miért kell az event Workers-en? Mert a környezeti változók nem egy globális process.env-ből jönnek, hanem a kérés kontextusából (a Worker env objektumából). A dokumentáció is azt ajánlja, hogy serverless környezetben add át az eventet — Workers-en ez a különbség aközött, hogy a friss értéket kapod, vagy egy build-time alapértelmezést. Vedd fel szokásnak: a useRuntimeConfig() a server/ alatt mindig kap eventet.

runtimeConfig vagy app.config.ts?

runtimeConfigapp.config.ts
Mikor dől el az értékFutásidőbenBuild-időben
Env-változóval felülírhatóIgenNem
Lehet benne titokIgen (a felső szinten)Soha — a bundle-be kerül
Nem-primitív értékekKorlátozottanIgen (objektum, függvény)
HMR fejlesztés közbenNincsVan
Mire valóKulcsok, URL-ek, környezetenként eltérő értékekTéma, UI-beállítás, arculat
Multitenant-tipp: az app.config.ts a build-időben ismert dolgokra való — a tenantonként eltérő arculat ezért nem ide való. Az futásidőben, a tenant-rekordból jön (4. modul), vagy layerekkel oldható meg, ha tényleg külön build készül tenantonként (14. modul).

12.3A szivárgás anatómiája — és hogyan ellenőrzöd

Négy úton kerülhet titok a kliensbe, és mind a négy csendes:

ÚtHogyan történik
public alá tett kulcsSzándékos, de rossz döntés. „A kliensről is kell hozzáférni” — nem kell, a szerveren keresztül kell.
Konstans a shared/-benA shared/ mindkét oldalra bekerül (2. modul). Egy export const ADMIN_KEY = '…' ott kimegy a böngészőbe.
Composable vagy komponensMinden, ami az app/ alatt van, a kliens-bundle része. Nincs kivétel.
PayloadNem konfiguráció, de ugyanaz a kimenet: a szerveri lekérés eredménye a HTML-be kerül (5. modul).

A tíz másodperces ellenőrzés

# build után nézd meg, hogy a titkaid nincsenek-e a kliens-bundle-ben
npm run build
grep -ril "sk_live\|SESSION_PASSWORD\|BEGIN PRIVATE KEY" .output/public/ || echo "tiszta"

# és konkrét értékre, ha van sejtésed:
grep -ro "sk_test_[A-Za-z0-9]*" .output/public/ | head
Tedd CI-lépéssé. Ez a néhány soros ellenőrzés a build után lefuttatva megfog minden olyan esetet, amit code review-ban átnéznének. Egy egyszerű minta-lista (sk_live, -----BEGIN, _SECRET, a saját kulcs-prefixeid) bőven elég — a lényeg, hogy legyen, és a build bukjon el, ha talál valamit. A 15. modul bundle-elemzése mellé kerülhet, ugyanabba a lépésbe.

12.4Cloudflare-oldal: vars, secret, binding

// wrangler.jsonc
{
  "name": "app",
  "main": ".output/server/index.mjs",
  "compatibility_date": "2026-08-01",
  "compatibility_flags": ["nodejs_compat"],

  "assets": { "directory": ".output/public", "binding": "ASSETS" },

  // ③ NEM titok — látszik a repóban és a dashboardon
  "vars": {
    "NUXT_PUBLIC_SITE_URL": "https://app.pelda.hu",
    "NUXT_PUBLIC_APP_NAME": "Scoope"
  },

  // + bindingok: nem érték, hanem képesség
  "hyperdrive":    [{ "binding": "APP_DB", "id": "…" }],
  "r2_buckets":    [{ "binding": "BUCKET", "bucket_name": "app-files" }],
  "kv_namespaces": [{ "binding": "CACHE", "id": "…" }]
}
# ④ a titkok NEM a configba mennek
npx wrangler secret put NUXT_SESSION_PASSWORD
npx wrangler secret put NUXT_STRIPE_SECRET_KEY
npx wrangler secret list
Két szabály, amit érdemes kiírni. ① Ami vars-ban van, azt tekintsd nyilvánosnak — a repóban van, a dashboardon látszik, a build-logban is megjelenhet. ② A vars és a bindingok nem öröklődnek a wrangler környezetek között: ha van [env.staging], ott újra fel kell sorolni őket, különben csendben eltűnnek. Ez a Cloudflare-anyag 12. moduljának „non-inheritable keys” szabálya, és tipikusan úgy derül ki, hogy stagingben nincs adatbázis.

A lánc: honnan hova jut egy érték

fejlesztés:   .env fájl          →  process.env  →  runtimeConfig  →  useRuntimeConfig()
éles:         wrangler secret    →  Worker env   →  runtimeConfig  →  useRuntimeConfig(event)
              wrangler vars      →  Worker env   →  runtimeConfig  →  useRuntimeConfig(event)

Vagyis a kódod egyetlen helyről olvas — useRuntimeConfig(event) —, és nem tudja, hogy az érték .env-ből, vars-ból vagy secretből jött. Ez a jó: a titkok kezelése üzemeltetési kérdés marad, nem szivárog be az alkalmazáslogikába.

12.5Környezetenkénti felülírás

Nuxt-oldalon a nuxt.config.ts támogat környezet-specifikus blokkokat, amiket a c12 old fel:

export default defineNuxtConfig({
  // közös alap
  routeRules: { '/api/**': { headers: { 'cache-control': 'no-store' } } },

  $development: {
    devtools: { enabled: true },
  },

  $production: {
    routeRules: {
      '/':        { prerender: true },
      '/blog/**': { prerender: true },
    },
  },

  // saját, névvel hivatkozott környezet
  $env: {
    staging: {
      runtimeConfig: { public: { siteUrl: 'https://staging.pelda.hu' } },
    },
  },
})
nuxt build --envName staging
Mikor melyiket? A $production/$development a viselkedés különbségeire való (devtools, prerender, source map). Az értékek környezetenkénti eltérése viszont maradjon a runtimeConfig + env-változó úton — mert azt deploy nélkül is állíthatod, és nem kell hozzá külön build. Ha egy staging-URL a build-be van égetve, akkor a staging és a production különböző artefakt, és nem tudod ugyanazt a buildet előléptetni.
És ez a Cloudflare-oldalon is számít. A „build once, deploy anywhere” elv az, ami lehetővé teszi, hogy a stagingben letesztelt pontosan ugyanaz a Worker-verzió menjen élesbe. Ha környezetenként külön buildelsz, akkor amit teszteltél, az nem az, ami éles lesz. A Cloudflare-anyag 8. modulja (versions és deployments) erre épül.

12.6A nuxt.config.ts — mit hova

Egy valós konfig gyorsan kétszáz sorosra hízik. Ez a sorrend segít, hogy később is megtaláld benne, amit keresel:

export default defineNuxtConfig({
  // 1. alapok
  compatibilityDate: '2026-08-01',
  srcDir: 'app',                      // Nuxt 4-ben ez az alapértelmezés
  devtools: { enabled: true },

  // 2. modulok — a sorrend SZÁMÍT, egymásra épülhetnek
  modules: ['@pinia/nuxt', '@nuxt/image', 'nuxt-auth-utils', '@nuxtjs/i18n'],

  // 3. amit az app tölt be
  css: ['~/assets/css/main.css'],

  // 4. auto-import finomhangolás (3. modul)
  components: [{ path: '~/components', pathPrefix: false }],
  imports: { dirs: ['composables/**'] },

  // 5. futásidejű értékek (12.2)
  runtimeConfig: { /* … */ },

  // 6. útvonal-viselkedés (7. modul)
  routeRules: { /* … */ },

  // 7. szerver és platform (2., 7., 11. modul)
  nitro: {
    preset: 'cloudflare_module',
    storage: { cache: { driver: 'cloudflare-kv-binding', binding: 'CACHE' } },
  },

  // 8. build-eszközök
  vite: { /* … */ },

  // 9. környezeti felülírások a végén — így látszik, mi tér el
  $development: { /* … */ },
  $production:  { /* … */ },
})
A modulok sorrendje nem mindegy. A modulok build-időben futnak le, egymás után, és módosíthatják a konfigot — ezért egy modul „nem működik”, ha egy másik utána írja felül azt, amit ő beállított. Ha valami megmagyarázhatatlanul nem érvényesül, a Nuxt DevTools „Modules” füle az első hely: ott látod, mi futott le és milyen sorrendben.

12.7TypeScript a Nuxt 4-ben

A Nuxt 4 nagy változása, hogy külön TypeScript-projekteket generál az app-, a szerver-, a megosztott és a builder-kódnak. Neked ehhez egyetlen fájlt kell karbantartanod:

// tsconfig.json — a gyökérben, ennyi
{
  "files": [],
  "references": [
    { "path": "./.nuxt/tsconfig.app.json" },
    { "path": "./.nuxt/tsconfig.server.json" },
    { "path": "./.nuxt/tsconfig.shared.json" },
    { "path": "./.nuxt/tsconfig.node.json" }
  ]
}

A haszna konkrét: a szerverkódban nem kapsz DOM-típusokat (tehát nem tudsz véletlenül document-et írni ott), az app-kódban pedig nem látszanak a Nitro-utilok. A 2. modul „melyik kódom hol fut” táblája így a fordító szintjén is érvényesül.

A bindingok típusai

npx wrangler types           # worker-configuration.d.ts a wrangler.jsonc alapján
// server/types/runtime-config.d.ts — hogy a runtimeConfig is típusos legyen
declare module 'nuxt/schema' {
  interface RuntimeConfig {
    sessionPassword: string
    stripeSecretKey: string
    upstream: { baseUrl: string; token: string }
  }
  interface PublicRuntimeConfig {
    appName: string
    siteUrl: string
    stripePublicKey: string
  }
}
export {}
Ez többet ad, mint kényelmet. Ha a RuntimeConfig és a PublicRuntimeConfig külön típus, akkor a fordító megfogja, ha kliensoldali kódból a szerver-only kulcsra hivatkozol — vagyis a 12.3-as szivárgás egyik útja fordítási hibává válik, nem code review-kérdéssé.

12.8Konfigurációs checklist

Minden titok a runtimeConfig felső szintjén van — semmi a public alatt, aminek nem való
Minden kulcs szerepel a configban üres alapértékkel, hogy az env-változó felül tudja írni
A server/ alatt minden useRuntimeConfig() kap event-et
Élesben a titkok wrangler secret-ben vannak, nem vars-ban
Környezetenként ugyanaz a build — az eltérés env-változóban, nem build-time konstansban
Wrangler-környezeteknél a vars és a bindingok újra fel vannak sorolva (nem öröklődnek)
Build után lefut a titok-kereső a .output/public/ fölött, és a CI bukik, ha talál
A RuntimeConfig típusok deklarálva vannak, hogy a rossz oldali hozzáférés fordítási hiba legyen
A .env a .gitignore-ban, és van .env.example a kulcsok listájával
Mi jön ezután? A 13. modul a modul-ökoszisztémáról szól: melyik Nuxt-modul működik Workers-en és melyik nem — @nuxt/image a sharp nélkül, i18n, fontkezelés, UI-könyvtárak —, plusz hogyan írj sajátot, ha ismétlődő setupod van.

12.9Ellenőrizd magad

  1. Beírtál egy kulcsot a runtimeConfig-ba, de futásidőben üres. Mi a két leggyakoribb ok?
    Válasz

    ① Az env-változó neve nem stimmel: NUXT_ prefix kell, a beágyazott szintek aláhúzással, a camelCase nagybetűs-aláhúzásos alakban (upstream.baseUrlNUXT_UPSTREAM_BASE_URL). ② A kulcs nem szerepel a runtimeConfig-ban alapértékkel — ha nincs ott, nincs mit felülírni, és a változó nem csinál semmit.

  2. Miért kell az event a useRuntimeConfig()-nak a szerveren, Workers alatt?
    Válasz

    Mert a környezeti változók nem globális process.env-ből jönnek, hanem a kérés kontextusából — a Worker env objektumából. Event nélkül build-time alapértelmezést kaphatsz a friss érték helyett. A Nuxt dokumentációja is ezt ajánlja serverless környezetre.

  3. Mi a különbség a vars és a secret között Cloudflare-en?
    Válasz

    A vars a wrangler.jsonc-ben van: a repóban látszik, a dashboardon olvasható, a build-logban megjelenhet — tekintsd nyilvánosnak. A secret (wrangler secret put) titkosítva tárolódik, és csak a futó kód olvashatja. A kódban mindkettő ugyanoda érkezik, de ami titok, az csak secretbe kerülhet.

  4. Miért baj, ha a staging-URL a build-be van égetve?
    Válasz

    Mert akkor a staging és a production különböző artefakt, tehát amit letesztelsz, az nem az, ami élesbe megy. Az értékek környezetenkénti eltérése maradjon runtimeConfig + env-változó úton — így ugyanazt a buildet léptetheted elő, ami a Cloudflare version/deployment modelljének is az alapja.

  5. Négy út, amin titok kerülhet a kliensbe. Melyek?
    Válasz

    runtimeConfig.public alá tett érték. ② Konstans a shared/-ben (mindkét oldalra bekerül). ③ Bármi az app/ alatt — composable, komponens, plugin. ④ A payload: a szerveri lekérés eredménye a HTML-be kerül (5. modul). Ellenőrzés: build után grep a .output/public/ fölött, CI-lépésként.

  6. Mikor használsz app.config.ts-t runtimeConfig helyett?
    Válasz

    Amikor az érték build-időben ismert, nyilvános, és jól jön a HMR meg a nem-primitív típusok támogatása: téma, UI-beállítás, arculati konstansok. Titkot vagy környezetenként eltérő értéket soha — azt nem lehet env-változóval felülírni, és a bundle-be kerül.

Előző11. modul — Adatbázis és bindings a server/-ben Következő 13. modul — Modul-ökoszisztéma Workers-szűrővel