> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/wevm/incur/llms.txt
> Use this file to discover all available pages before exploring further.

# Variables

> Typed dependency injection for middleware and command handlers

Declare a `vars` schema on `Cli.create()` to enable typed variables. Middleware sets them with `c.set()`, and both middleware and command handlers read them via `c.var`.

## Basic Usage

```ts theme={null}
import { Cli, z } from 'incur'

type User = { id: string; name: string }

const cli = Cli.create('my-cli', {
  description: 'My CLI',
  vars: z.object({
    user: z.custom<User>(),
    requestId: z.string(),
    debug: z.boolean().default(true),
  }),
})

cli.use(async (c, next) => {
  c.set('user', await authenticate())
  c.set('requestId', crypto.randomUUID())
  await next()
})

cli.command('whoami', {
  run(c) {
    return { user: c.var.user, requestId: c.var.requestId, debug: c.var.debug }
  },
})
```

```sh theme={null}
$ my-cli whoami
# → user:
# →   id: u_123
# →   name: Alice
# → requestId: 550e8400-e29b-41d4-a716-446655440000
# → debug: true
```

## How It Works

1. **Declare the schema** — pass `vars` to `Cli.create()` with a Zod object schema
2. **Set values in middleware** — call `c.set(key, value)` to populate variables
3. **Read in handlers** — access `c.var.key` in middleware or command handlers

## Variable Defaults

Use `.default()` for variables that don't need middleware:

```ts theme={null}
const cli = Cli.create('my-cli', {
  description: 'My CLI',
  vars: z.object({
    debug: z.boolean().default(false),
    timeout: z.number().default(30_000),
  }),
})

cli.command('run', {
  run(c) {
    // debug and timeout are available without middleware
    return { debug: c.var.debug, timeout: c.var.timeout }
  },
})
```

## Middleware Example

### Authentication

```ts theme={null}
import { Cli, middleware, z } from 'incur'

type User = { id: string; name: string; admin: boolean }

const cli = Cli.create('my-cli', {
  description: 'My CLI',
  vars: z.object({ user: z.custom<User>() }),
})

const requireAuth = middleware<typeof cli.vars>((c, next) => {
  if (!c.var.user) return c.error({ code: 'AUTH', message: 'must be logged in' })
  return next()
})

const requireAdmin = middleware<typeof cli.vars>((c, next) => {
  if (!c.var.user?.admin) throw new Error('admin required')
  return next()
})

cli.command('deploy', {
  middleware: [requireAuth, requireAdmin],
  run() {
    return { deployed: true }
  },
})
```

```sh theme={null}
$ my-cli deploy
# Error (AUTH): must be logged in
```

### Request Context

```ts theme={null}
const cli = Cli.create('my-cli', {
  description: 'My CLI',
  vars: z.object({
    requestId: z.string(),
    startTime: z.number(),
  }),
})

cli.use(async (c, next) => {
  c.set('requestId', crypto.randomUUID())
  c.set('startTime', Date.now())
  await next()
})

cli.command('run', {
  run(c) {
    const duration = Date.now() - c.var.startTime
    return { requestId: c.var.requestId, duration }
  },
})
```

## Type Inference

The `vars` schema flows through generics to middleware and command handlers:

```ts theme={null}
const cli = Cli.create('my-cli', {
  vars: z.object({
    user: z.custom<User>(),
    requestId: z.string(),
  }),
})

cli.use((c, next) => {
  c.var.user // Type: User | undefined
  c.var.requestId // Type: string | undefined
  c.set('user', { id: 'u_123', name: 'Alice' })
  c.set('requestId', crypto.randomUUID())
  return next()
})

cli.command('whoami', {
  run(c) {
    c.var.user // Type: User | undefined
    c.var.requestId // Type: string | undefined
    return { user: c.var.user }
  },
})
```

## Middleware Type Hints

For external middleware, use the `middleware` helper with type parameters:

```ts theme={null}
import { middleware, z } from 'incur'

type User = { id: string; name: string }

const vars = z.object({ user: z.custom<User>() })

const auth = middleware<typeof vars>((c, next) => {
  c.var.user // Type: User | undefined
  c.set('user', await authenticate())
  return next()
})

const cli = Cli.create('my-cli', { vars })
cli.use(auth)
```

## Environment + Variables

Combine CLI-level `env` with `vars` for environment variables plus middleware state:

```ts theme={null}
const cli = Cli.create('my-cli', {
  description: 'My CLI',
  env: z.object({
    API_TOKEN: z.string(),
  }),
  vars: z.object({
    user: z.custom<User>(),
  }),
})

cli.use(async (c, next) => {
  // CLI-level env is available in middleware
  const token = c.env.API_TOKEN
  const user = await fetchUser(token)
  c.set('user', user)
  await next()
})

cli.command('whoami', {
  run(c) {
    // Both env and var are available
    return { user: c.var.user, hasToken: !!c.env.API_TOKEN }
  },
})
```

## Typing External Middleware

For middleware defined outside your CLI, pass both `vars` and `env` as type parameters:

```ts theme={null}
import { middleware, z } from 'incur'

type User = { id: string; name: string }

const vars = z.object({ user: z.custom<User>() })
const env = z.object({ API_TOKEN: z.string() })

const auth = middleware<typeof vars, typeof env>((c, next) => {
  c.var.user // Type: User | undefined
  c.env.API_TOKEN // Type: string
  return next()
})
```

## Per-Command Middleware

Per-command middleware inherits the CLI's `vars` schema:

```ts theme={null}
const cli = Cli.create('my-cli', {
  vars: z.object({ user: z.custom<User>() }),
})

const requireAdmin = middleware<typeof cli.vars>((c, next) => {
  if (!c.var.user?.admin) throw new Error('admin required')
  return next()
})

cli.command('deploy', {
  middleware: [requireAdmin],
  run() {
    return { deployed: true }
  },
})
```

## Best Practices

### Do

* Use `vars` for state set by middleware (user, session, etc.)
* Use `.default()` for static configuration (timeouts, flags)
* Use `z.custom<T>()` for complex types
* Type external middleware with `middleware<typeof cli.vars, typeof cli.env>()`

### Don't

* Don't use `vars` for arguments or options — use `args` and `options` schemas
* Don't use `vars` for environment variables — use `env` schema
* Don't mutate `c.var` directly — always use `c.set()`

## Related

* See [Middleware](/features/middleware) for composable before/after hooks
* See [Environment Variables](/core-concepts/schemas#environment-variables) for `env` schema
