Type-safe environment variables in your Remix application using t3-env
Quickly and easily add type-safety and runtime validation to your environment variables using the t3-env package.
There are many was you can get type-safe variables in your projects, I’ve tried a few but recently found the t3-env package which makes it even easier and comes with some nice features as well. I will quickly walk through how to set this up in a Remix project.
Install t3-env
As of writing this, currently you can’t just bring in any validation library so you will need to install zod
as well.
npm install @t3-oss/env-core zod
Create a new env.server.ts
file
Inside of the /app
directory, create a file named env.server.ts
and add the following code:
// app/env.server.ts
import { createEnv } from "@t3-oss/env-core";
import { z } from "zod";
export const env = createEnv({
server: {
DATABASE_URL: z.string().url(),
// whatever else you may need
},
});
Then, to access the environment variables in your loaders or actions or anywhere you might need them, you would just need to import the env
object and use it like this:
import { env } from "~/env.server";
export async function loader() {
const dbUrl = env.server.DATABASE_URL;
// do something with the dbUrl
}
This is enough to get type-safety for your environment variables, but we can go even further.
If you want to add runtime validation you just need to add a single property:
export const env = createEnv({
// ...
runtimeEnv: process.env,
});
and then you’ll need to import the env.server.ts
file into the entry.server.tsx
file like so:
// app/entry.server.tsx
import { PassThrough } from "node:stream";
import type { AppLoadContext, EntryContext } from "@remix-run/node";
import { createReadableStreamFromReadable } from "@remix-run/node";
import { RemixServer } from "@remix-run/react";
import { isbot } from "isbot";
import { renderToPipeableStream } from "react-dom/server";
+ import "~/env.server.ts";
On top of that, if you want to override the default error handler, you can do so like this:
export const env = createEnv({
onValidationError: (error) => {
throw new Error(
`Invalid environment configuration, missing the following variables: ${error.errors.map((error) => error.path[0]).join(", ")}`,
);
},
// ...
});
Putting that all together you should end up with a file that looks like this:
// app/env.server.ts
import { createEnv } from "@t3-oss/env-core";
import { z } from "zod";
export const env = createEnv({
onValidationError: (error) => {
throw new Error(
`Invalid environment configuration, missing the following variables: ${error.errors.map((error) => error.path[0]).join(", ")}`,
);
},
server: {
DATABASE_URL: z.string().url(),
},
runtimeEnv: process.env,
});
If you want to see all of the features available in the t3-env package, I recommend checking out the official documentation.