Skip to content

Commit 0b635e0

Browse files
committed
refactor environment variables
1 parent 83e7c81 commit 0b635e0

File tree

12 files changed

+140
-91
lines changed

12 files changed

+140
-91
lines changed

src/app/Providers.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use client'
22

33
import { PropsWithChildren } from 'react'
4-
import { configureLogger, logger } from '@navikt/next-logger'
4+
import { configureLogger } from '@navikt/next-logger'
55
import { BASE_PATH } from '../../next.config'
66

77
configureLogger({

src/app/api/flexjar/route.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { NextRequest, NextResponse } from 'next/server'
22
import { logger } from '@navikt/next-logger'
3-
import { getServerEnv, isLocalOrDemo } from '@/constants/envs'
43
import { verifyUserLoggedIn } from '@/auth/rsc'
54
import { requestOboToken } from '@navikt/oasis'
65
import { FlexJarTransportPayload } from '@navikt/flexjar-widget'
6+
import { isLocalOrDemo } from '@/env-variables/envHelpers'
7+
import { getServerEnv } from '@/env-variables/serverEnv'
78

89
async function exchangeToken(idportenToken: string): Promise<string> {
910
const { FLEXJAR_BACKEND_CLIENT_ID } = getServerEnv()

src/app/layout.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import '@/app/globals.css'
2-
import "@navikt/flexjar-widget/styles.css";
2+
import '@navikt/flexjar-widget/styles.css'
33

44
import type { Metadata } from 'next'
55
import Script from 'next/script'
@@ -8,9 +8,10 @@ import { fetchDecoratorReact } from '@navikt/nav-dekoratoren-moduler/ssr'
88

99
import { Page } from '@navikt/ds-react'
1010
import { PageBlock } from '@navikt/ds-react/Page'
11-
import { isDemo, publicEnv } from '@/constants/envs'
1211
import Providers from '@/app/Providers'
1312
import DemoAlert from '@/components/DemoAlert'
13+
import { isDemo } from '@/env-variables/envHelpers'
14+
import { publicEnv } from '@/env-variables/publicEnv'
1415

1516
export const metadata: Metadata = {
1617
title: 'Kartlegging av din situasjon',

src/app/preload.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
'use client'
2+
3+
import ReactDOM from 'react-dom'
4+
5+
function Preload(): null {
6+
ReactDOM.preload('https://cdn.nav.no/aksel/fonts/SourceSans3-normal.woff2', {
7+
as: 'font',
8+
type: 'font/woff2',
9+
crossOrigin: 'anonymous',
10+
})
11+
12+
return null
13+
}
14+
15+
export default Preload

src/auth/rsc.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import { logger } from '@navikt/next-logger'
44
import { headers } from 'next/headers'
55
import { redirect } from 'next/navigation'
66
import { getToken, validateToken } from '@navikt/oasis'
7-
import { isLocalOrDemo } from '@/constants/envs'
87
import { BASE_PATH } from '../../next.config'
8+
import { isLocalOrDemo } from '@/env-variables/envHelpers'
99

1010
export async function verifyUserLoggedIn(): Promise<string> {
1111
if (isLocalOrDemo) {

src/auth/tokenUtils.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
'use server'
22

33
import { requestOboToken } from '@navikt/oasis'
4-
5-
import { getServerEnv } from '@/constants/envs'
4+
import { getServerEnv } from '@/env-variables/serverEnv'
65

76
export async function exchangeIdportenTokenForMeroppfolgingBackendTokenx(
87
idportenToken: string | null,

src/constants/envs.ts

Lines changed: 0 additions & 82 deletions
This file was deleted.

src/env-variables/envHelpers.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { z } from 'zod/v4'
2+
3+
export function throwEnvSchemaParsingError(e: unknown): never {
4+
if (e instanceof z.ZodError) {
5+
throw new Error(
6+
`The following envs are missing: ${
7+
e.issues
8+
.filter((it) => it.code === 'invalid_type' && it.message.includes('received undefined'))
9+
.map((it) => it.path.join('.'))
10+
.join(', ') || 'None are missing, but zod is not happy. Look at cause'
11+
}`,
12+
{ cause: e },
13+
)
14+
} else {
15+
throw e
16+
}
17+
}
18+
export const isDemo = process.env.NEXT_PUBLIC_RUNTIME_ENVIRONMENT === 'demo'
19+
20+
export const isLocalOrDemo = process.env.NEXT_PUBLIC_RUNTIME_ENVIRONMENT === 'local' || isDemo

src/env-variables/publicEnv.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { z } from 'zod'
2+
import { throwEnvSchemaParsingError } from './envHelpers'
3+
4+
export type PublicEnv = z.infer<typeof publicEnvSchema>
5+
export const publicEnvSchema = z.object({
6+
NEXT_PUBLIC_RUNTIME_ENVIRONMENT: z.union([
7+
z.literal('local'),
8+
z.literal('test'),
9+
z.literal('demo'),
10+
z.literal('dev'),
11+
z.literal('prod'),
12+
]),
13+
NEXT_PUBLIC_ASSET_PREFIX: z.string().optional(),
14+
NEXT_PUBLIC_TELEMETRY_URL: z.string().optional(),
15+
NEXT_PUBLIC_BASE_PATH: z.string(),
16+
})
17+
18+
/**
19+
* These envs are available in the browser. They are replaced during the bundling step by NextJS.
20+
*/
21+
export const rawPublicEnv = {
22+
NEXT_PUBLIC_RUNTIME_ENVIRONMENT: process.env.NEXT_PUBLIC_RUNTIME_ENVIRONMENT,
23+
NEXT_PUBLIC_ASSET_PREFIX: process.env.NEXT_PUBLIC_ASSET_PREFIX,
24+
NEXT_PUBLIC_TELEMETRY_URL: process.env.NEXT_PUBLIC_TELEMETRY_URL,
25+
NEXT_PUBLIC_BASE_PATH: process.env.NEXT_PUBLIC_BASE_PATH,
26+
} satisfies Record<keyof PublicEnv, string | undefined>
27+
28+
// Evaluate once at module load time with an IIFE.
29+
export const publicEnv: Readonly<PublicEnv> = (() => {
30+
try {
31+
return Object.freeze(publicEnvSchema.parse(rawPublicEnv))
32+
} catch (err) {
33+
throwEnvSchemaParsingError(err)
34+
}
35+
})()

src/env-variables/serverEnv.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import 'server-only'
2+
3+
import { z } from 'zod'
4+
import { PublicEnv, publicEnvSchema, rawPublicEnv } from './publicEnv'
5+
import { throwEnvSchemaParsingError } from './envHelpers'
6+
7+
export type ServerEnv = z.infer<typeof serverEnvSchema>
8+
export const serverEnvSchema = z.object({
9+
// Provided by nais-*.yaml
10+
MEROPPFOLGING_BACKEND_URL: z.string(),
11+
FLEXJAR_BACKEND_HOST: z.string(),
12+
FLEXJAR_BACKEND_CLIENT_ID: z.string(),
13+
// Provided by nais
14+
TOKEN_X_WELL_KNOWN_URL: z.string(),
15+
TOKEN_X_CLIENT_ID: z.string(),
16+
TOKEN_X_PRIVATE_JWK: z.string(),
17+
IDPORTEN_WELL_KNOWN_URL: z.string(),
18+
IDPORTEN_CLIENT_ID: z.string(),
19+
NAIS_CLUSTER_NAME: z.string(),
20+
})
21+
22+
const rawServerEnv = {
23+
// Provided by nais-*.yml
24+
MEROPPFOLGING_BACKEND_URL: process.env.MEROPPFOLGING_BACKEND_URL,
25+
FLEXJAR_BACKEND_HOST: process.env.FLEXJAR_BACKEND_HOST,
26+
FLEXJAR_BACKEND_CLIENT_ID: process.env.FLEXJAR_BACKEND_CLIENT_ID,
27+
28+
// Provided by nais
29+
TOKEN_X_WELL_KNOWN_URL: process.env.TOKEN_X_WELL_KNOWN_URL,
30+
TOKEN_X_CLIENT_ID: process.env.TOKEN_X_CLIENT_ID,
31+
TOKEN_X_PRIVATE_JWK: process.env.TOKEN_X_PRIVATE_JWK,
32+
IDPORTEN_WELL_KNOWN_URL: process.env.IDPORTEN_WELL_KNOWN_URL,
33+
IDPORTEN_CLIENT_ID: process.env.IDPORTEN_CLIENT_ID,
34+
NAIS_CLUSTER_NAME: process.env.NAIS_CLUSTER_NAME,
35+
} satisfies Record<keyof ServerEnv, string | undefined>
36+
37+
let cachedServerEnv: Readonly<ServerEnv & PublicEnv> | undefined
38+
39+
/**
40+
* Validates server and public environment variables against zod schemas that define the
41+
* expected variables, and returns an object containing all variables.
42+
* Caches the result after the first invocation.
43+
* Server environment variables don't need to be set in local or demo environment,
44+
* as long as we don't call this function if isLocalOrDemo is true.
45+
*/
46+
export function getServerEnv(): Readonly<ServerEnv & PublicEnv> {
47+
if (!cachedServerEnv) {
48+
try {
49+
cachedServerEnv = Object.freeze({
50+
...serverEnvSchema.parse(rawServerEnv),
51+
...publicEnvSchema.parse(rawPublicEnv),
52+
})
53+
} catch (e) {
54+
throwEnvSchemaParsingError(e)
55+
}
56+
}
57+
return cachedServerEnv
58+
}

0 commit comments

Comments
 (0)