Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Environment variables should be included in generated types #725

Open
Ernxst opened this issue Jul 19, 2024 · 5 comments
Open

Environment variables should be included in generated types #725

Ernxst opened this issue Jul 19, 2024 · 5 comments
Assignees

Comments

@Ernxst
Copy link
Contributor

Ernxst commented Jul 19, 2024

Given that environment variables are linked to Resource (v0.1.1) - I can access them in the app - it also makes sense they should be included in the generated types.

/// <reference path="./.sst/platform/config.d.ts" />

export default $config({
	app(input) {
		return {
			name: "ion",
			removal: input.stage === "production" ? "retain" : "remove",
			home: "cloudflare",
		};
	},
	async run() {
		const worker = new sst.cloudflare.Worker("MyFunction", {
			handler: "./functions/worker.ts",
			environment: {
				WorkerName: 'MyFunction'
			}
		});

		return {
			url: worker.url,
		};
	},
});

The above config produces the following sst-env.d.ts

/* tslint:disable */
/* eslint-disable */
import "sst"
declare module "sst" {
  export interface Resource {
    MyFunction: {
      type: "sst.cloudflare.Worker"
    }
  }
}
export {}

But I can still access Resource.WorkerName in my worker as well as it being listed as a variable in the cloudflare dashboard:

image

I should add, I'm not sure how the types should be generated if I reference the same variable in multiple functions

Copy link
Contributor

thdxr commented Jul 19, 2024

We can't generate types for environment variables because they are not globally unique - they're scoped to the specific target they're attached to.

it's a bit challenging to try and collect all the environment variables from everywhere to produce a unique list of variables that may be available

a work around is to manually define them the same way sst-env.d.ts defines them

@thdxr thdxr self-assigned this Jul 19, 2024
@Ernxst
Copy link
Contributor Author

Ernxst commented Jul 19, 2024

That's fair, then again you can access a resource on the type level even if it's not scoped to the target you're currently in and it will throw at runtime. I guess the same can be true here, but I agree it only really works if you're using the same env vars in all your functions (which I currently am).

Copy link
Contributor

thdxr commented Jul 19, 2024

this might not be what you really want but you could do new sst.Secret("Name", value) and link that to all your functions if you want the typesafety

@Ernxst
Copy link
Contributor Author

Ernxst commented Jul 19, 2024

The secrets approach works until two functions need the same env var but with different values (I have a sentry helper function which grabs the dsn from the env instead of being passed as an argument). I was using define in the esbuild config as build-time vars, but I also needed to do the same when bundling the workers for testing so defining it in the sst config seemed best. Given my use-case, the following works well since all my workers will need the same env vars:

/// <reference path="./../../sst-env.d.ts" />

declare module "sst" {
	export interface Resource {
		SentryRelease: string;
		SentryDsn: string;
		AppName: string;
		FunctionOrigin: string;
	}
}

export type {};

@Ernxst
Copy link
Contributor Author

Ernxst commented Jul 19, 2024

Just had a play around and have come up with a solutio to achieve better scoping of env vars in a monorepo setup. My setup is an infra package with the following package.json exports:

	"exports": {
		".": "./src/index.d.ts",
		"./env/cloudflare": "./src/env/cloudflare.d.ts",
		"./env/sentry": "./src/env/sentry.d.ts",
	},

My ./src/index.d.ts is simply:

/// <reference path="./../../sst-env.d.ts" />

declare module "sst" {
	export interface Resource {}
}

export type {};

Which just gives me the original generated types, then in src/env/sentry.d.ts, I only include sentry specific env vars:

/// <reference path="./../../sst-env.d.ts" />

declare module "sst" {
	export interface Resource {
		SentryRelease: string;
		SentryDsn: string;
	}
}

export type {};

This way, in packages that have a target which will use these env vars, you only need to include the specific export in your tsconfig:

{
	"compilerOptions": {
		"types": [
			"@repo/infra",
			"@repo/infra/env/sentry"
		]
	},
}

This way, you can scope env vars only to where they will be defined. Keep in mind, this approach falls apart if you have multiple targets in the same package who do not share all env vars. I think I prefer this approach rather than requiring sst to generate them

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants