# Converters (/docs/converters)



A converter turns the raw environment string into a typed value. Pass a built-in token to `getUsing`, a function to `getWith`, or either as the `converter` option on the [`@Envapt`](/docs/decorators) decorator. Without a converter, a value stays a `string`.

```ts twoslash
import { Envapter, Converters } from 'envapt';
// ---cut---
const timeout = Envapter.getUsing('TIMEOUT', Converters.Time, '30s');
//    ^?

const retries = Envapter.getWith('RETRIES', (raw, fallback) => (raw ? Number(raw) : fallback), 3);
//    ^?
```

## Built-in tokens [#built-in-tokens]

`Converters` carries one token per built-in type, plus the `array` builder.

{/* prettier-ignore-start */}

| Token                | Returns     | Parsed from                                               |
| -------------------- | ----------- | --------------------------------------------------------- |
| `Converters.String`  | `string`    | the raw value, unchanged                                  |
| `Converters.Number`  | `number`    | `Number(raw)`; `NaN` uses the fallback                    |
| `Converters.Integer` | `number`    | `Number(raw)`, must be a safe integer                     |
| `Converters.Float`   | `number`    | `Number(raw)`, `NaN` uses the fallback                    |
| `Converters.Boolean` | `boolean`   | `1`/`yes`/`true`/`on` and `0`/`no`/`false`/`off`          |
| `Converters.Bigint`  | `bigint`    | `BigInt(raw)`                                             |
| `Converters.Symbol`  | `symbol`    | `Symbol.for(raw)`                                         |
| `Converters.Json`    | `JsonValue` | `JSON.parse(raw)`                                         |
| `Converters.Url`     | `URL`       | `new URL(raw)`                                            |
| `Converters.Regexp`  | `RegExp`    | `/pattern/flags` form, or the whole string as the pattern |
| `Converters.Date`    | `Date`      | a millisecond timestamp or a UTC ISO 8601 string          |
| `Converters.Time`    | `number`    | a duration string like `30s`, returned as milliseconds    |
| `Converters.Port`    | `number`    | an integer in the `0-65535` range                         |
| `Converters.Email`   | `string`    | an address matching the WHATWG email pattern              |

{/* prettier-ignore-end */}

```ts twoslash
import { Envapter, Converters } from 'envapt';
// ---cut---
const port = Envapter.getUsing('PORT', Converters.Number, 3000);
//    ^?
const apiUrl = Envapter.getUsing('API_URL', Converters.Url);
//    ^?
```

A fallback removes `undefined` from the return type, the same way it does for the [primitive readers](/docs/envapter#fallbacks-shape-the-return-type). Without one, the return is `T | undefined`.

The fallback must itself be valid for the converter (a real email, an in-range port, a non-`NaN` number, a valid `Date`). An invalid one throws `FallbackConverterTypeMismatch`.

<Callout type="info">
  No converter throws on a failed conversion. The converter returns the fallback, or `undefined` when you gave none.
  Use `getRequired` to throw on a malformed or missing value instead.
</Callout>

## Number, Integer, and Float [#number-integer-and-float]

`Number` accepts anything JavaScript's `Number()` does, including floats, hex, and scientific notation. `Integer` and `Float` now parse with `Number` as well. `Integer` requires a safe integer, so `42px`, `3.9`, and values past 2^53 fall back. `Float` rejects trailing text like `3.14px` and keeps `Infinity` like `Number`. Both accept the same literal forms as `Number`, including `0x`, `0o`, and `0b`.

```ts twoslash
import { Envapter, Converters } from 'envapt';
// ---cut---
// MAX_CONNECTIONS=100
const maxConnections = Envapter.getUsing('MAX_CONNECTIONS', Converters.Integer); // 100

// PORT="8080abc" is not a clean integer, so it falls back
const port = Envapter.getUsing('PORT', Converters.Integer, 3000); // 3000
const sampleRate = Envapter.getUsing('SAMPLE_RATE', Converters.Float, 1.0);
```

## JSON [#json]

`Json` runs `JSON.parse`. Invalid JSON returns the fallback. The return type is `JsonValue`, the recursive union of JSON-representable values, so narrow it before use.

```ts twoslash
import { Envapter, Converters } from 'envapt';
// ---cut---
// FEATURE_FLAGS={"beta":true}
const flags = Envapter.getUsing('FEATURE_FLAGS', Converters.Json, {});
//    ^?
```

## URL, RegExp, Date [#url-regexp-date]

`Url` builds a `URL`, so the value must be absolute; a relative path throws inside the constructor and returns the fallback.

`Regexp` reads a `/pattern/flags` literal (flags limited to `g i m s u v y`). A value without the surrounding slashes is treated as the whole pattern with no flags.

`Date` accepts exactly two forms:

```ts twoslash
import { Envapter, Converters } from 'envapt';
// ---cut---
// RELEASED_AT=2024-01-15T10:30:00Z
const released = Envapter.getUsing('RELEASED_AT', Converters.Date);
//    ^?
```

<Callout type="warn">
  `Date` takes an all-digit millisecond timestamp, or a UTC ISO 8601 string ending in `Z` (`2024-01-15T10:30:00Z`,
  optionally with `.mmm` milliseconds). A date-only string, a local time, or an offset like `+02:00` returns the
  fallback. Store dates in UTC.
</Callout>

## Time and durations [#time-and-durations]

`Time` reads a duration and returns it in **milliseconds**. The raw value takes a number with an optional unit; a bare number is milliseconds.

```ts twoslash
import { Envapter, Converters } from 'envapt';
// ---cut---
// CACHE_TTL=15m
const ttl = Envapter.getUsing('CACHE_TTL', Converters.Time); // 900000
const grace = Envapter.getUsing('GRACE', Converters.Time, '1.5h');
//    ^?
```

The units are `ms`, `s`, `m`, `h`, `d`, `w`. The fallback may be a number (already milliseconds) or a time-string.

<Callout type="warn">
  A raw env value may omit the unit (a bare number is milliseconds). A **string fallback** must include the unit
  (`90m` or `1.5h`), since a unitless number is already expressed as a number fallback (`5400`). Both forms accept
  decimals. A malformed string fallback throws `EnvaptError` with code `MalformedTimeFallback`.
</Callout>

## Arrays [#arrays]

`Converters.array` splits the value on a delimiter and converts each element. The delimiter defaults to `,` and the element type defaults to `string`.

```ts twoslash
import { Envapter, Converters } from 'envapt';
// ---cut---
// CORS=https://a.com,https://b.com
const origins = Envapter.getUsing('CORS', Converters.array({ of: Converters.Url }));
//    ^?

// PORTS=3000 3001 3002
const ports = Envapter.getUsing('PORTS', Converters.array({ of: Converters.Number, delimiter: ' ' }));
//    ^?
```

The element type can be any scalar token except `Json` and `Regexp`, or your own `(raw: string) => T` function that converts a single element. An array fallback must already be an array of the element type.

```ts twoslash
import { Envapter, Converters } from 'envapt';
// ---cut---
const toUpstream = (raw: string) => {
    const [host, port] = raw.split(':');
    return { host, port: Number(port) };
};

// UPSTREAMS=10.0.0.1:8080,10.0.0.2:8080
const upstreams = Envapter.getUsing('UPSTREAMS', Converters.array({ of: toUpstream }));
//    ^?
```

Empty elements are dropped in normal mode. Under [strict mode](/docs/strict-mode) an empty element (a trailing comma, say) throws `EnvaptError` with code `EmptyArrayElement` instead of being filtered out.

<Callout type="danger">
  Under strict mode these throws come from env data, not your code. A trailing comma in a deployed `.env` throws
  `EmptyArrayElement` at read time, and an element your converter maps to `undefined` throws
  `ArrayElementConversionFailed`. A stray comma can stop startup.
</Callout>

## Custom converters [#custom-converters]

When no built-in fits, pass a function to `getWith`. It receives the raw value (`string`, or `undefined` when the variable is unset) and the fallback, and returns the typed value. The function owns its own parsing and failure handling.

```ts twoslash
import { Envapter } from 'envapt';
// ---cut---
const toSemver = (raw: string | undefined) => {
    const [major, minor, patch] = (raw ?? '0.0.0').split('.').map(Number);
    return { major, minor, patch };
};

// APP_VERSION=2.4.1
const version = Envapter.getWith('APP_VERSION', toSemver);
//    ^?
```

For a single array element you supply a `(raw: string) => T` function to `array({ of })` instead. That element function receives only the trimmed, non-empty element string. Returning `undefined` from it throws `EnvaptError` with code `ArrayElementConversionFailed`.

## Require a converted value [#require-a-converted-value]

Both readers take an options form that throws on a missing value instead of returning a fallback.

```ts twoslash
import { Envapter, Converters } from 'envapt';
// ---cut---
const dbUrl = Envapter.getRequired('DATABASE_URL', Converters.Url);
//    ^?
```

See [Fail-fast on missing values](/docs/envapter#fail-fast-on-missing-values) for `require`, and [Standard Schema](/docs/standard-schema) for validating a value through zod, valibot, or arktype.
