Skip to content

Add a more straightforward method of opting dynamic explicit environment variables out of build-time validation #16195

Description

@cofl

Describe the problem

With the current method of defining environment variables, dynamic variables are not validated at build time. This is common and expected behavior across a wide variety of frameworks going back decades.

The new "explicit environment variables" has gone a different route, stating that variables must have a value at build time. The logic here seems to be that if a dynamic variable is used in a static capacity, it will need a value. This is not unintuitive -- what is the point of differentiating static and dynamic variables if dynamic variables will be treated just like static variables apart from inlining?

The official workaround is to dynamically make your variables optional just at build time, but this requires sometimes substantial extra code or that potentially undesired dependencies be installed.

Describe the proposed solution

Add a flag prender to EnvVarConfig that controls if variables are validated for prerendering, defaulting to true. Setting this flag to false would prevent a variable with value not known until deploy time from being built.

Alternatives considered

  1. One solution could be writing your own optional validators that only take effect at build time. This seems very verbose and counter-intuitive.

    import { defineEnvVars } from '@sveltejs/kit/hooks'
    import { building } from '$app/env'
    
    export const variables = defineEnvVars({
        SECRET: /** @type {import("@sveltejs/kit").EnvVarConfig<string>} */ {
            schema: building
                ? {
                      ["~standard"]: {
                          version: 1,
                          vendor: "",
                          validate: (value) =>
                              typeof value === "string" ? { value } :
                              typeof value === "undefined" ? { value: "" } :
                            { issues: [{ message: "Value was not string or undefined." }]}
                      },
                  }
                : undefined,
        },
    })
  2. Another solution would be to not use explicit environment variables for dynamic variables that may not be available at build time. "Don't use the feature" is not a good way to use the feature, though...

    import { building } from '$app/env'
    function runtimeOnly(fn){
        if(building) return undefined
        return fn()
    }
    const SECRET = runtimeOnly(() => process.env.SECRET)
  3. Another solution would be to require schema validators, thus leading users to install libraries where the official workaround is less verbose. A pure-SvelteKit solution would still be desireable.

    import { defineEnvVars } from '@sveltejs/kit/hooks'
    import { building } from '$app/env'
    import * as v from "valibot"
    
    function runtimeOnly(schema, dummyValue){
        if(building) return v.optional(schema, dummyValue)
        return schema
    }
    
    const variables = defineEnvVars({
        SECRET: { schema: runtimeOnly(v.string()) }
    })
  4. A fourth solution would be to provide a wrapper schema that does the above automatically.

    import { defineEnvVars, runtimeOnlyEnvVar } from '@sveltejs/kit/hooks'
    import * as v from "valibot"
    
    const variables = defineEnvVars({
        SECRET: { schema: runtimeOnlyEnvVar() },
        OTHER: { schema: runtimeOnlyEnvVar(v.pipe(v.string(), v.url())) },
    })
  5. A fifth solution would be to change the default behavior to not validate dynamic variables at build time, and instead require users to use static variables instead.

  6. Use dummy values. This is unfriendly to new developers on projects, who will see unexpected errors due to incorrect values that have not been overriden.

Some less serious alternatives:

  1. Commit your secrets to git so they're available in CI pipelines.

Importance

would make my life easier

Additional Information

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions