Typing env variables in Next.js

Serhii Aksonov

While creating my site with Next.js and TypeScript, I set up some environment variables. But when writing the code, I noticed that VSCode autocomplete shows only 2 explicitly defined variables:

  • TZ?: string; in @types/node
  • NODE_ENV: 'development' | 'production' | 'test' in next

For all other user-defined variables, there's a common type [key: string]: string | undefined. So I wanted to add types for the variables that my project uses and potentially use better types than just string | undefined.

My initial way to implement this was to create type definitions file env.d.ts in a root folder of a project and define all needed variables there. Given .env file that looks like this:

1NEXT_PUBLIC_RECAPTCHA_KEY=key 2NEXT_PUBLIC_SKIP_RECAPTCHA=false 3RECAPTCHA_SECRET=

I created env.d.ts with the next content:

1namespace NodeJS { 2 interface ProcessEnv { 3 NEXT_PUBLIC_RECAPTCHA_KEY: string 4 NEXT_PUBLIC_SKIP_RECAPTCHA: 'true' | 'false' 5 RECAPTCHA_SECRET?: string 6 } 7}

Then I had to add this file into tsconfig.json:

1{ 2 ... 3 "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "env.d.ts"] 4}

Auto-generating env.d.ts file

While this file works fine, it's not very convenient to add a new line there each time I introduce a new variable. So I came up with a solution to auto-generate this file, inspired by graphql-codegen. The idea for a script is simple - read the .env file in the root folder and generate types file from it.

My final script (bin/generate-env.js) looks like this:

1const fs = require('fs') 2 3const envFile = fs.readFileSync('.env').toString().trim() 4 5const types = envFile.split('\n').map((line) => { 6 const [name, value] = line.trim().split('=') 7 const type = ['true', 'false'].includes(value) ? "'true' | 'false'" : 'string' 8 return ` ${name}${value ? '' : '?'}: ${type}` 9}) 10 11const result = `// Auto-generated. Do not edit. 12declare namespace NodeJS { 13 interface ProcessEnv { 14${types.join('\n')} 15 } 16} 17` 18 19fs.writeFileSync('env.d.ts', result)

One last thing to do is to add it to the scripts section in a package.json file. Also, optionally, it's possible to run it automatically before running the dev server by adding a predev script:

1{ 2 ... 3 "scripts": { 4 ... 5 "generate-env": "node bin/generate-env.js", 6 "predev": "npm run generate-env" 7 } 8}

#TypeScript #Next.js