Skip to content

Commit 0508e61

Browse files
committed
✨ Add environment-variables example
1 parent b17ee4c commit 0508e61

16 files changed

+346
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# available at server & client (public)
2+
# context.project.name
3+
NULLSTACK_PROJECT_NAME="[dev] env-example"
4+
# context.settings.publicKey
5+
NULLSTACK_SETTINGS_PUBLIC_KEY="a_public_key"
6+
7+
# available only at server (secret)
8+
# context.server.port
9+
NULLSTACK_SERVER_PORT="3000"
10+
# context.secrets.password
11+
NULLSTACK_SECRETS_PASSWORD="right_password"
12+
# process.env.COMMON_VARIABLE
13+
COMMON_VARIABLE="value"
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# available at server & client (public)
2+
# context.project.name
3+
NULLSTACK_PROJECT_NAME="[test] env-example"
4+
# context.settings.publicKey
5+
NULLSTACK_SETTINGS_PUBLIC_KEY="a_public_key"
6+
7+
# available only at server (secret)
8+
# context.server.port
9+
NULLSTACK_SERVER_PORT="3000"
10+
# process.env.COMMON_VARIABLE
11+
COMMON_VARIABLE="value"
12+
13+
# available only at this environment (i.e. `nullstack start --name=test`)
14+
NULLSTACK_SETTINGS_DISABLE_COUNT="true"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
module.exports = {
2+
extends: 'plugin:nullstack/recommended',
3+
rules: {
4+
'nullstack/prettier': [
5+
'warn',
6+
{
7+
// More options at https://prettier.io/docs/en/options
8+
trailingComma: 'all',
9+
tabWidth: 2,
10+
semi: false,
11+
singleQuote: true,
12+
printWidth: 80,
13+
},
14+
{
15+
usePrettierrc: false,
16+
},
17+
],
18+
},
19+
}
+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.js
7+
package-lock.json
8+
yarn.lock
9+
pnpm-lock.yaml
10+
11+
# testing
12+
/coverage
13+
14+
# misc
15+
.DS_Store
16+
*.pem
17+
18+
# debug
19+
npm-debug.log*
20+
yarn-debug.log*
21+
yarn-error.log*
22+
.pnpm-debug.log*
23+
24+
# local env files
25+
.env
26+
27+
# vercel
28+
.vercel
29+
30+
# bundle folders
31+
.development/
32+
.production/
+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Environment Variables Example
2+
3+
This example shows how to use environment variables with Nullstack.
4+
5+
Start by renaming the **[.env.example](./.env.example)** file to **.env** (recommended to be kept in [**.gitignore**](./.gitignore)).
6+
7+
Then take a look at it's contents and comments describing what every key become after Nullstack consumes it.
8+
9+
> 💡 By default Nullstack consumes the **.env** file with exception of when a `-n`/`--name` flag is passed, e.g. `nullstack start --name=test`, in this case it searches for a **.env.test**
10+
11+
[Application](./src/Application.tsx) component is the main code and logs values only available at server brought from **.env**: `NULLSTACK_SERVER_PORT` and `COMMON_VARIABLE`.
12+
13+
[KeysTable](./src/KeysTable.tsx) is a [stateless component](https://nullstack.app/stateless-components) that just illustrates how all keys from **.env** are or not available to the browser.
14+
15+
[AccessCount](./src/AccessCount.tsx) component brings a more functional use of **.env** keys featuring a counter to API ([server functions](https://nullstack.app/server-functions)) access.
16+
17+
- The API is only accessed passing the right password as stored at **.env**'s `NULLSTACK_SECRETS_PASSWORD`.
18+
- It was written as when running the app with the `test` script (e.g. `npm run test`) `--name=test` will be passed, making Nullstack use the **.env.test** which sets `NULLSTACK_SETTINGS_DISABLE_COUNT` (`settings.disableCount`) used to fully disable API access and count at all.
19+
20+
> 💡🏷️ [**src/environment.d.ts**](./src/environment.d.ts) declares types for **.env** keys adding autocomplete for them at both `process.env` and context keys `settings`/`server`
21+
22+
## Deploy your own
23+
24+
Deploy it now with [Vercel](https://vercel.com) or preview on [StackBlitz](https://stackblitz.com):
25+
26+
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/GuiDevloper/nullstack-examples/tree/main/examples/environment-variables&project-name=environment-variables&repo-name=environment-variables&demo-title=Nullstack+EnvironmentVariables&demo-description=Nullstack+example+of+environment+variables&demo-url=https://github.com/GuiDevloper/nullstack-examples/tree/main/examples/environment-variables&demo-image=https://nullstack.app/image-1200x630.png)
27+
[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/fork/github/GuiDevloper/nullstack-examples/tree/main/examples/environment-variables?title=Nullstack+EnvironmentVariables)
28+
29+
## How to use
30+
31+
Execute [`nulla create`](https://github.com/GuiDevloper/nulla) with [npm](https://docs.npmjs.com/cli/init):
32+
33+
```bash
34+
npx nulla create --example environment-variables environment-variables-app
35+
```
36+
37+
Then deploy it with [Vercel](https://github.com/GuiDevloper/nulla/blob/main/docs/en-US/deploy-vercel.md), [Heroku](https://github.com/GuiDevloper/nulla/blob/main/docs/en-US/deploy-heroku.md) or [Netlify](https://github.com/GuiDevloper/nulla/blob/main/docs/en-US/deploy-netlify.md).
+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import Nullstack from 'nullstack'
2+
3+
import Application from './src/Application'
4+
5+
export default Nullstack.start(Application)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"private": true,
3+
"devDependencies": {
4+
"@types/node": "^20.4.2",
5+
"nullstack": "0.20.0"
6+
},
7+
"scripts": {
8+
"start": "npx nullstack start",
9+
"build": "npx nullstack build",
10+
"test": "npx nullstack start --name=test",
11+
"vercel-build": "npm run build && npx nullstack-adapt-vercel"
12+
},
13+
"stackblitz": {
14+
"installDependencies": false,
15+
"startCommand": "npm i [email protected] -D && nullstack-adapt-babel && npm start",
16+
"compileTrigger": "save"
17+
}
18+
}
Loading
+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import Nullstack from 'nullstack'
2+
3+
import Application from './src/Application'
4+
5+
export default Nullstack.start(Application)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/* eslint-disable no-console */
2+
import Nullstack, { NullstackServerContext } from 'nullstack'
3+
4+
type ContextPassword = {
5+
passwordFromBrowser: string
6+
}
7+
8+
class AccessCount extends Nullstack {
9+
10+
apiResult: string
11+
static serverCount = 0
12+
13+
static async accessAPI(context: ContextPassword) {
14+
const { secrets, settings, passwordFromBrowser } =
15+
context as NullstackServerContext<ContextPassword>
16+
if (settings.disableCount) {
17+
return 'Test Mode! ViewCount disabled!'
18+
}
19+
if (passwordFromBrowser !== secrets.password) {
20+
return 'Unauthorized access! Use the right password!'
21+
}
22+
return `Authorized! API Access count: ${++AccessCount.serverCount}`
23+
}
24+
25+
async callAPI({ passwordFromBrowser }: ContextPassword) {
26+
this.apiResult = await AccessCount.accessAPI({ passwordFromBrowser })
27+
}
28+
29+
render() {
30+
return (
31+
<main>
32+
<button class="button" onclick={this.callAPI}>
33+
Try access with wrong password
34+
</button>
35+
<button
36+
class="button"
37+
onclick={() =>
38+
this.callAPI({ passwordFromBrowser: 'right_password' })
39+
}
40+
>
41+
Try access with right password
42+
</button>
43+
{this.apiResult && <p>{this.apiResult}</p>}
44+
</main>
45+
)
46+
}
47+
48+
}
49+
50+
export default AccessCount
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
* {
2+
padding: 0;
3+
margin: 0;
4+
text-decoration: none;
5+
list-style: none;
6+
box-sizing: border-box;
7+
}
8+
9+
body {
10+
background-color: #111827;
11+
color: #fff;
12+
font-family: sans-serif;
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/* eslint-disable no-console */
2+
import './Application.css'
3+
import Nullstack, {
4+
NullstackClientContext,
5+
NullstackServerContext,
6+
} from 'nullstack'
7+
8+
import AccessCount from './AccessCount'
9+
import KeysTable from './KeysTable'
10+
11+
class Application extends Nullstack {
12+
13+
static async prepareServer(context?: any) {
14+
const { server } = context as NullstackServerContext
15+
console.log(`Server port set from .env: ${server.port}`)
16+
console.log(`process.env.COMMON_VARIABLE: ${process.env.COMMON_VARIABLE}`)
17+
}
18+
19+
async prepare({ page, project }: NullstackClientContext) {
20+
page.title = project.name
21+
await Application.prepareServer()
22+
}
23+
24+
render({ settings }: NullstackClientContext) {
25+
const headerTitle = `${
26+
settings.disableCount !== 'true' ? 'Enabled' : 'Disabled'
27+
} Count`
28+
29+
return (
30+
<main>
31+
<KeysTable />
32+
<h2>{headerTitle}</h2>
33+
<AccessCount />
34+
</main>
35+
)
36+
}
37+
38+
}
39+
40+
export default Application
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
.table {
2+
border-collapse: collapse;
3+
margin: 1rem 0;
4+
}
5+
6+
.table th,
7+
.table td {
8+
border: 1px solid #eaeaea;
9+
}
10+
11+
.table th {
12+
padding: 0.7rem;
13+
}
14+
15+
.table td {
16+
font-size: 0.8rem;
17+
padding: 0.7rem;
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import './KeysTable.css'
2+
import {
3+
NullstackClientContext,
4+
NullstackFunctionalComponent,
5+
NullstackServerContext,
6+
} from 'nullstack'
7+
8+
// Wrongly enforcing type to example values always undefined at browser
9+
type FakeServer = NullstackServerContext
10+
11+
function KeysTable({
12+
project,
13+
settings,
14+
secrets,
15+
server,
16+
}: NullstackClientContext<FakeServer>) {
17+
return (
18+
<table class="table">
19+
<thead>
20+
<tr>
21+
<th>Variable Name</th>
22+
<th>Value</th>
23+
</tr>
24+
</thead>
25+
<tbody>
26+
<tr>
27+
<td>NULLSTACK_PROJECT_NAME</td>
28+
<td>{project.name}</td>
29+
</tr>
30+
<tr>
31+
<td>NULLSTACK_SETTINGS_PUBLIC_KEY</td>
32+
<td>{settings?.publicKey}</td>
33+
</tr>
34+
<tr>
35+
<td>NULLSTACK_SERVER_PORT</td>
36+
<td>{server?.port || 'undefined in browser :)'}</td>
37+
</tr>
38+
<tr>
39+
<td>NULLSTACK_SECRETS_PASSWORD</td>
40+
<td>{secrets?.password || 'undefined in browser :)'}</td>
41+
</tr>
42+
<tr>
43+
<td>COMMON_VARIABLE</td>
44+
<td>
45+
<code>process.env.COMMON_VARIABLE</code> can be accessed only at
46+
server!
47+
</td>
48+
</tr>
49+
</tbody>
50+
</table>
51+
)
52+
}
53+
54+
export default KeysTable as NullstackFunctionalComponent<any>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
declare global {
2+
namespace NodeJS {
3+
export interface ProcessEnv {
4+
readonly COMMON_VARIABLE: string
5+
}
6+
}
7+
}
8+
9+
// eslint-disable-next-line nullstack/no-unused-imports
10+
import * as Nullstack from 'nullstack'
11+
12+
declare module 'nullstack' {
13+
export interface NullstackSettings {
14+
publicKey: string | undefined
15+
disableCount?: string | undefined
16+
}
17+
export interface NullstackSecrets {
18+
password: string | undefined
19+
}
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"compilerOptions": {
3+
"strict": false,
4+
"noImplicitAny": false,
5+
"jsx": "preserve",
6+
"lib": ["ES2020", "DOM"]
7+
}
8+
}

0 commit comments

Comments
 (0)