# Quick Start (/docs/quick-start)



## Install [#install]

<CodeBlockTabs defaultValue="pnpm" groupId="package-manager">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="deno">
      deno
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm add envapt
    ```
  </CodeBlockTab>

  <CodeBlockTab value="npm">
    ```bash
    npm install envapt
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn add envapt
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun add envapt
    ```
  </CodeBlockTab>

  <CodeBlockTab value="deno">
    ```bash
    deno add jsr:@materwelon/envapt
    ```
  </CodeBlockTab>
</CodeBlockTabs>

## Read a value [#read-a-value]

Put a variable in your `.env`:

```dotenv
PORT=8080
```

Read it as a number, with a fallback for when it is unset:

```ts twoslash
import { Envapter } from 'envapt';
// ---cut---
const port = Envapter.getNumber('PORT', 3000);
//    ^?
```

The fallback supplies the default and removes `undefined` from the type. See [Envapter](/docs/envapter) for the full reader API and [Converters](/docs/converters) for non-primitive types.

## Or bind it to a class [#or-bind-it-to-a-class]

The [`@Envapt`](/docs/decorators) decorator binds a value to a typed field. The default form is a TC39 Stage 3 accessor decorator.

```ts twoslash
import { Envapt, Converters } from 'envapt';
// ---cut---
class Config {
    @Envapt('PORT', { converter: Converters.Number, fallback: 3000 })
    static accessor port: number;
}
```

<Callout>
  Static fields use `static accessor x: T`, instance fields use `accessor x!: T`. If your project already runs on
  `experimentalDecorators`, import the legacy form from `envapt/legacy` instead. See
  [Decorators](/docs/decorators#declaring-decorated-fields).
</Callout>

## Mirror loaded values to process.env [#mirror-loaded-values-to-processenv]

Import `envapt/config` to load the `.env` cascade and mirror every loaded key into `process.env` in one step, so `process.env.PORT` works without touching `Envapter`. This is the same pattern as the `dotenv/config` import, so code that reads `process.env` directly still sees the values.

```ts twoslash
import 'envapt/config';

const port = process.env.PORT; // the loaded keys are now on process.env
```

It is the side-effect form of `Envapter.syncProcessEnv = true` plus an eager load, and follows the same collision rules. Unlike `dotenv/config`, it loads the whole per-environment [cascade](/docs/environment), not a single `.env`.

Preload it without editing your entry file:

```bash
node --import envapt/config app.js   # ESM
node -r envapt/config app.js         # CommonJS
```

For typed reads on top, keep using [`Envapter`](/docs/envapter).

## Requirements [#requirements]

envapt runs on Node `>=20`, Bun `>=1.3`, Deno `>=2.5`, Cloudflare Workers, and the browser. On Node, Bun, and Deno a source binds on import, on Workers and in the browser you bind one yourself. The functional API needs no build step on any runtime. The default decorator API uses TC39 accessor decorators, which run on Bun and Deno executing a `.ts` file directly. The legacy decorators (`envapt/legacy`) need compilation with `experimentalDecorators`. See [Compatibility](/docs/compatibility).
