Skip to content

Commit

Permalink
Merge pull request #77 from expatfile/@HofmannZ/fix/server-components
Browse files Browse the repository at this point in the history
App Directory Support
  • Loading branch information
HofmannZ authored Oct 11, 2023
2 parents 0d87b1c + 66f1fcb commit db9f8bd
Show file tree
Hide file tree
Showing 63 changed files with 2,300 additions and 4,840 deletions.
4 changes: 2 additions & 2 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ module.exports = {
},
overrides: [
{
files: ['src/__mocks__/.*', '**/*.spec.ts'],
files: ['src/__mocks__/.*', '**/*.spec.ts', '**/*.spec.tsx'],
rules: {
'import/no-extraneous-dependencies': [
'error',
Expand All @@ -42,7 +42,7 @@ module.exports = {
settings: {
'import/resolver': {
node: {
extensions: ['.js', '.ts'],
extensions: ['.js', '.ts', '.jsx', '.tsx'],
},
},
},
Expand Down
2 changes: 1 addition & 1 deletion .github/actions/back-merge/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ runs:

steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
token: ${{ inputs.gh_token }}

Expand Down
24 changes: 0 additions & 24 deletions .github/dependabot.yml

This file was deleted.

12 changes: 6 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ jobs:

strategy:
matrix:
node-version: [16, 18]
node-version: [18, 20]

steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Setup pnpm
uses: ./.github/actions/setup-pnpm
Expand All @@ -39,11 +39,11 @@ jobs:

strategy:
matrix:
node-version: [16, 18]
node-version: [18, 20]

steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Setup pnpm
uses: ./.github/actions/setup-pnpm
Expand All @@ -61,11 +61,11 @@ jobs:

strategy:
matrix:
node-version: [16, 18]
node-version: [18, 20]

steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Setup pnpm
uses: ./.github/actions/setup-pnpm
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:

steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Setup pnpm
uses: ./.github/actions/setup-pnpm
Expand Down
168 changes: 51 additions & 117 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
![GitHub branch checks state][build-url] [![codecov][cov-img]][cov-url]
![GitHub branch checks state][build-url] [![codecov][cov-img]][cov-url] [![Known Vulnerabilities][snyk-img]][snyk-url]

# Next.js Runtime Environment Configuration

Populate your environment at **runtime** rather than **build time**.

- Isomorphic - Server and browser compatible (and even in middleware.)
- Static site generation support.
- Next.js 13 App Router compatible.
- `.env` support during development, just like [Next.js][nextjs-env-vars-order].

### Why we created this package 🤔
Expand All @@ -26,152 +26,88 @@ once, deploy many principle?

### This package 📦

`next-runtime-env` solves this problem by generating a JavaScript file that
contains the environment variables at runtime, so you no longer have to declare
your environment variables at build time. This file is loaded in the client,
safely exposing those variables to the browser. This allows you to follow the
build once, deploy many principle by providing differed runtime variables to the
same build.
`next-runtime-env` solves this problem by creating a context that exposes your environment variables at runtime, so you no longer have to declare
your environment variables at build time. The context provider safely exposes all environment variables prefixed with `NEXT_PUBLIC_` to the browser. This allows you to follow the build once, deploy many principle by providing differed runtime variables to the same build.

### Compatibility 🤝

Our approach is compatible with
[static site generation][static-generation-link] and supports middleware.

> 🏗️ We are actively working on supporting the new App directory and Server
> Components released in Next.js 13.
Because `next-runtime-env` is build on top of server components it is only compatible with Next.js 13 App Router. Use [version 1.x][pages-router-branch-link] for Next.js 13 Page Router support.

### Getting started 🚀

1. Add the following lines to your `next.config.js`:
Add the following lines to your `app/layout.tsx`:

```js
const { configureRuntimeEnv } = require('next-runtime-env/build/configure');

configureRuntimeEnv();
```

When the server starts, this generates an `__ENV.js` file in the `public` folder
containing allow-listed environment variables with a `NEXT_PUBLIC_` prefix.

2. Add the following to the head section of your `pages/_document.js`:
// app/layout.tsx
import { PublicEnvProvider } from 'next-runtime-env';

export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
<PublicEnvProvider>{children}</PublicEnvProvider>
</body>
</html>
);
}

```tsx
// pages/_document.tsx
<script src="/__ENV.js" />
// By default server components are statically generated at build-time. To make
// sure the env vars are actually loaded use, add the following line to server
// components that use [env]. 👇
export const dynamic = 'force-dynamic';
```

This will load the generated file in the browser.
The `PublicEnvProvider` will automatically expose all environment variables prefixed with `NEXT_PUBLIC_` to the context. If you want more control over which variables are exposed to the context, you can use the `EnvProvider` and define the exposed variables manually.

### Usage 🧑‍💻

In the browser, your variables will now be available at
`window.__ENV.NEXT_PUBLIC_FOO` and on the server at
`process.env.NEXT_PUBLIC_FOO`. For example:
In the browser your environment variables are now accessible using the `useEnvContext` hook. On the server you can use `process.env` because the layout is forced to be dynamic. For example:

```bash
# .env
NEXT_PUBLIC_FOO="foo"
BAR="bar"
NEXT_PUBLIC_BAZ="baz"
```

```tsx
// pages/some-page.tsx
type Props = {
bar: string;
};

export default function SomePage({ bar }: Props) {
return (
<div>
{window.__ENV.NEXT_PUBLIC_FOO} {bar}
</div>
);
}

export const getStaticProps: GetStaticProps = async (context) => {
return {
props: {
bar: process.env.BAR,
},
};
};
```

### Utilities 🛠

We have included some utility function to make it even easier to work with
environment variables.

#### `env(key: string): string | undefined`

Returns the value of the environment variable with the given key. If the
environment variable is not found, it returns undefined.

##### Example
> A `.env` file is not required, you can also declare your environment variables in whatever way you want.
```tsx
// pages/some-page.tsx
import { env } from 'next-runtime-env';
// app/client-page.tsx
'use client';

type Props = {
bar: string;
};
import { useEnvContext } from 'next-runtime-env';

export default function SomePage() {
const { NEXT_PUBLIC_FOO } = useEnvContext();

export default function SomePage({ bar }: Props) {
return (
<div>
{env('NEXT_PUBLIC_FOO')} {bar}
</div>
<main >
NEXT_PUBLIC_FOO: {NEXT_PUBLIC_FOO}
</main>
);
}

export const getStaticProps: GetStaticProps = async (context) => {
return {
props: {
bar: env('BAR'),
},
};
};
```

#### `allEnv(): ProcessEnv`

Returns all environment variables as a `ProcessEnv` object regardless of
the platform. This is useful if you want to destructure multiple env vars at
once.

##### Example

```tsx
// pages/some-page.tsx
import { allEnv } from 'next-runtime-env';

type Props = {
bar: string;
};

export default function SomePage({ bar }: Props) {
const { NEXT_PUBLIC_FOO, NEXT_PUBLIC_BAZ } = allEnv();
// app/server-page.tsx

export default function SomePage() {
return (
<div>
{NEXT_PUBLIC_FOO} {NEXT_PUBLIC_BAZ} {bar}
</div>
<main >
BAR: {process.env.BAR}
</main>
);
}
```

export const getStaticProps: GetStaticProps = async (context) => {
const { BAR } = allEnv();
### Utilities 🛠

return {
props: {
bar: BAR,
},
};
};
```
We have included some utility function to make it even easier to work with
environment variables.

#### `makeEnvPublic(key: string | string[]): void`

Expand All @@ -186,18 +122,14 @@ array of keys.

```js
// next.config.js
const { configureRuntimeEnv } = require('next-runtime-env/build/configure');
const { makeEnvPublic } = require('next-runtime-env/build/make-env-public');
const { makeEnvPublic } = require('next-runtime-env');

// Given that `FOO` is declared as a regular env var, not a public one. This
// will make it public and available as `NEXT_PUBLIC_FOO`.
makeEnvPublic('FOO');

// Or you can make multiple env vars public at once.
makeEnvPublic(['BAR', 'BAZ']);

// This will generate the `__ENV.js` file and include `NEXT_PUBLIC_FOO`.
configureRuntimeEnv();
```

### Maintenance 👷
Expand All @@ -212,11 +144,13 @@ Big thanks to the [react-env][react-env-repo] project, which inspired us. 🙏
[build-url]: https://img.shields.io/github/checks-status/expatfile/next-runtime-env/main
[cov-img]: https://codecov.io/gh/expatfile/next-runtime-env/branch/main/graph/badge.svg?token=mbGgsweFuP
[cov-url]: https://codecov.io/gh/expatfile/next-runtime-env
[snyk-img]: https://snyk.io/test/github/expatfile/next-runtime-env/badge.svg
[snyk-url]: https://snyk.io/test/github/expatfile/next-runtime-env
[nextjs-env-vars-order]: https://nextjs.org/docs/basic-features/environment-variables#environment-variable-load-order
[build-once-deploy-many-link]: https://www.mikemcgarr.com/blog/build-once-deploy-many.html
[fundamental-principle-link]: https://cloud.redhat.com/blog/build-once-deploy-anywhere
[twelve-factor-link]: https://12factor.net
[static-generation-link]: https://nextjs.org/docs/basic-features/pages#static-generation
[pages-router-branch-link]: https://github.com/expatfile/next-runtime-env/tree/1.x
[nextjs-env-vars]: https://nextjs.org/docs/basic-features/environment-variables
[react-env-repo]: https://github.com/andrewmclagan/react-env
[expatfile-site]: https://expatfile.tax
39 changes: 39 additions & 0 deletions docs/EXPOSING_CUSTOM_ENV.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Exposing custom environment variables 🛠

You might not only want to expose environment variables that are prefixed with `NEXT_PUBLIC_`. In this case you can use the `EnvProvider` to expose custom environment variables to the context.

## Example

```tsx
// app/layout.tsx

import { EnvProvider } from 'next-runtime-env';

export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
<EnvProvider
env={{
NEXT_PUBLIC_: process.env.NEXT_PUBLIC_FOO,
BAR: process.env.BAR,
BAZ: process.env.BAZ,
notAnEnvVar: 'not-an-env-var',
}}
>
{children}
</EnvProvider>
</body>
</html>
);
}

// By default server components are statically generated at build-time. To make
// sure the env vars are actually loaded use, add the following line to server
// components that use [env]. 👇
export const dynamic = 'force-dynamic';
```
Loading

0 comments on commit db9f8bd

Please sign in to comment.