Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
255 changes: 24 additions & 231 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,272 +3,65 @@
</br>
Nuxt Safe Runtime Config</h1>
<p align="center">
Validate Nuxt runtime config at build time using <b>Zod</b>, <b>Valibot</b>, <b>ArkType</b>, or any Standard Schema compatible library.
Validate Nuxt runtime config at build or runtime using <b>Zod</b>, <b>Valibot</b>, <b>ArkType</b>, or any Standard Schema compatible library.
</p>
<br/>

<p align="center">
<a href="https://www.npmjs.com/package/nuxt-safe-runtime-config">
<img src="https://img.shields.io/npm/v/nuxt-safe-runtime-config.svg" alt="npm version" />
</a>
<a href="https://www.npmjs.com/package/nuxt-safe-runtime-config">
<img src="https://img.shields.io/npm/dm/nuxt-safe-runtime-config.svg" alt="npm downloads" />
</a>
<a href="https://github.com/onmax/nuxt-safe-runtime-config/blob/main/LICENSE">
<img src="https://img.shields.io/github/license/onmax/nuxt-safe-runtime-config.svg" alt="License" />
</a>
<a href="https://nuxt.com">
<img src="https://img.shields.io/badge/Nuxt-3.0+-00DC82.svg" alt="Nuxt" />
</a>
<a href="https://www.npmjs.com/package/nuxt-safe-runtime-config"><img src="https://img.shields.io/npm/v/nuxt-safe-runtime-config.svg" alt="npm version" /></a>
<a href="https://www.npmjs.com/package/nuxt-safe-runtime-config"><img src="https://img.shields.io/npm/dm/nuxt-safe-runtime-config.svg" alt="npm downloads" /></a>
<a href="https://github.com/onmax/nuxt-safe-runtime-config/blob/main/LICENSE"><img src="https://img.shields.io/github/license/onmax/nuxt-safe-runtime-config.svg" alt="License" /></a>
<a href="https://nuxt.com"><img src="https://img.shields.io/badge/Nuxt-3.0+-00DC82.svg" alt="Nuxt" /></a>
</p>

<p align="center">
<a href="https://github.com/nuxt/nuxt/discussions/32301">
🔗 Related Nuxt RFC: Enable Standard Schema Validation in Nuxt Config
</a>
</p>
<p align="center">
<a href="https://nuxt-safe-runtime-config.vercel.app">Documentation</a>
</p>

## Features

- 🔒 **Build-time validation** with Zod, Valibot, ArkType, or any [Standard Schema](https://standardschema.dev/) library
- 🚀 **Runtime validation** (opt-in) validates config when the server starts
- ✨ **Auto-generated types** — `useSafeRuntimeConfig()` is fully typed without manual generics
- 🛠 **ESLint plugin** warns when using `useRuntimeConfig()` instead of the type-safe composable
- ⚡ **Zero runtime overhead** by default — validation happens at build time only

## Quick Setup
- **Build-time validation** with Zod, Valibot, ArkType, or any [Standard Schema](https://standardschema.dev/) library
- **Runtime validation** (opt-in) validates config when the server starts
- **Auto-generated types** - `useSafeRuntimeConfig()` is fully typed
- **ESLint plugin** warns when using `useRuntimeConfig()` instead of the type-safe composable

Install the module:
## Quick Start

```bash
npx nuxi module add nuxt-safe-runtime-config
```

## Usage

### 1. Define your schema

Use Zod, Valibot, ArkType, or any Standard Schema compatible library:

<details>
<summary>With Valibot</summary>

```typescript
```ts [nuxt.config.ts]
import { number, object, optional, string } from 'valibot'

const runtimeConfigSchema = object({
public: object({
apiBase: string(),
appName: optional(string()),
}),
const schema = object({
public: object({ apiBase: string() }),
databaseUrl: string(),
secretKey: string(),
port: optional(number()),
})
```

</details>

<details>
<summary>With Zod</summary>

```typescript
import { z } from 'zod'

const runtimeConfigSchema = z.object({
public: z.object({
apiBase: z.string(),
appName: z.string().optional(),
}),
databaseUrl: z.string(),
secretKey: z.string(),
port: z.number().optional(),
})
```

</details>

<details>
<summary>With ArkType</summary>

```typescript
import { type } from 'arktype'

const runtimeConfigSchema = type({
'public': {
'apiBase': 'string',
'appName?': 'string'
},
'databaseUrl': 'string',
'secretKey': 'string',
'port?': 'number'
})
```

</details>

### 2. Configure in nuxt.config.ts

```typescript
export default defineNuxtConfig({
modules: ['nuxt-safe-runtime-config'],

runtimeConfig: {
databaseUrl: process.env.DATABASE_URL || 'postgresql://localhost:5432/mydb',
secretKey: process.env.SECRET_KEY || 'default-secret-key',
databaseUrl: process.env.DATABASE_URL || '',
port: Number.parseInt(process.env.PORT || '3000'),
public: {
apiBase: process.env.PUBLIC_API_BASE || 'https://api.example.com',
appName: 'My Nuxt App',
},
},

safeRuntimeConfig: {
$schema: runtimeConfigSchema,
public: { apiBase: 'https://api.example.com' },
},
safeRuntimeConfig: { $schema: schema },
})
```

### 3. Use the type-safe composable

Access your validated config with full type safety — types are auto-generated from your schema:

```vue
<script setup lang="ts">
const config = useSafeRuntimeConfig()
// config.public.apiBase is typed as string
// config.secretKey is typed as string
// config.public.apiBase - string (typed)
</script>
```

## Configuration Options

| Option | Type | Default | Description |
| ------------------- | ------------------------------- | ----------------- | ------------------------------------------ |
| `$schema` | `StandardSchemaV1` | — | Your validation schema (required) |
| `validateAtBuild` | `boolean` | `true` | Validate during dev/build |
| `validateAtRuntime` | `boolean` | `false` | Validate when server starts |
| `onBuildError` | `'throw' \| 'warn' \| 'ignore'` | `'throw'` | How to handle build validation errors |
| `onRuntimeError` | `'throw' \| 'warn' \| 'ignore'` | `'throw'` | How to handle runtime validation errors |
| `logSuccess` | `boolean` | `true` | Log successful validation |
| `logFallback` | `boolean` | `true` | Log when using JSON Schema fallback |
| `jsonSchemaTarget` | `string` | `'draft-2020-12'` | JSON Schema version for runtime validation |

## Runtime Validation

By default, validation only runs at build time. Enable runtime validation to catch environment variable issues when the server starts:

```ts
export default defineNuxtConfig({
safeRuntimeConfig: {
$schema: runtimeConfigSchema,
validateAtRuntime: true,
},
})
```

Runtime validation uses [@cfworker/json-schema](https://github.com/cfworker/cfworker/tree/main/packages/json-schema) to validate the config after environment variables are merged. This lightweight validator (~8KB) works on all runtimes including edge (Cloudflare Workers, Vercel Edge, Netlify Edge). It catches issues like:

- Environment variables with wrong types (e.g., `NUXT_PORT=abc` when expecting a number)
- Missing required environment variables in production
- Invalid values that pass build-time checks but fail at runtime

## ESLint Integration
## Documentation

The module includes an ESLint plugin that warns when using `useRuntimeConfig()` instead of `useSafeRuntimeConfig()`.
Full documentation at **[nuxt-safe-runtime-config.vercel.app](https://nuxt-safe-runtime-config.vercel.app)**

### With @nuxt/eslint (Automatic)

If you use [@nuxt/eslint](https://eslint.nuxt.com), the rule is auto-registered. No configuration needed.

### Manual Setup

Add to your `eslint.config.mjs`:

```javascript
import { configs } from 'nuxt-safe-runtime-config/eslint'

export default [
configs.recommended,
// ... your other configs
]
```

Or configure manually:

```javascript
import plugin from 'nuxt-safe-runtime-config/eslint'

export default [
{
plugins: { 'safe-runtime-config': plugin },
rules: { 'safe-runtime-config/prefer-safe-runtime-config': 'warn' },
},
]
```

The rule includes auto-fix support — run `eslint --fix` to automatically replace `useRuntimeConfig()` calls.

## Type Safety

Types are auto-generated at build time from your schema's JSON Schema representation. The `useSafeRuntimeConfig()` composable returns a fully typed object — no manual generics needed:

```ts
const config = useSafeRuntimeConfig()
// config is fully typed based on your schema
```

Generated types are stored in `.nuxt/types/safe-runtime-config.d.ts` and automatically included in your project.

## Error Messages

When validation fails, you see detailed error messages:

```
[safe-runtime-config] Validation failed!
1. databaseUrl: Invalid type: Expected string but received undefined
2. public.apiBase: Invalid type: Expected string but received undefined
3. port: Invalid type: Expected number but received string
```

The module stops the build process until all validation errors are resolved.

## Why This Module?

Nuxt's built-in schema validation is designed for module authors and broader configuration. This module focuses specifically on **runtime config validation** using Standard Schema, allowing you to:

- Use your preferred validation library (Valibot, Zod, ArkType)
- Catch configuration errors at build time
- Optionally validate at runtime for environment variable issues
- Get full type safety in your components

## Contribution

<details>
<summary>Local development</summary>

```bash
# Install dependencies
pnpm install

# Generate type stubs
pnpm run dev:prepare

# Develop with the playground
pnpm run dev

# Build the playground
pnpm run dev:build

# Run ESLint
pnpm run lint

# Run Vitest
pnpm run test
pnpm run test:watch

# Release new version
pnpm run release
```
## License

</details>
MIT
8 changes: 8 additions & 0 deletions docs/.config/docs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
name: "nuxt-safe-runtime-config"
shortDescription: "Type-safe runtime config for Nuxt"
description: "Validate Nuxt runtime config at build or runtime using Zod, Valibot, ArkType, or any Standard Schema compatible library."
github: "onmax/nuxt-safe-runtime-config"
url: "https://nuxt-safe-runtime-config.vercel.app"
themeColor: "green"
landing:
contributors: true
11 changes: 11 additions & 0 deletions docs/.docs/app.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export default defineAppConfig({
ui: {
prose: {
codeIcon: {
valibot: 'custom:valibot',
zod: 'simple-icons:zod',
arktype: 'custom:arktype',
},
},
},
})
11 changes: 11 additions & 0 deletions docs/.docs/icons/arktype.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions docs/.docs/icons/valibot.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions docs/.docs/public/arktype.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions docs/.docs/public/icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions docs/.docs/public/valibot.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions docs/.docs/public/zod.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions docs/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
node_modules
.nuxt
.output
dist
.vercel
.env*.local
Loading
Loading