@@ -23,26 +23,27 @@ import { Schema, Effect } from "effect"
2323
2424// 1. Define environment schema
2525const EnvironmentSchema = Schema .Struct ({
26- DATABASE_URL: Schema .String .pipe (
27- Schema .description (" PostgreSQL connection string" )
28- ),
29- API_KEY: Schema .String .pipe (
30- Schema .minLength (32 ),
31- Schema .description (" API authentication key (min 32 chars)" )
32- ),
33- PORT: Schema .pipe (
34- Schema .String ,
35- Schema .parseNumber (),
36- Schema .int (),
37- Schema .between (1024 , 65535 ),
38- Schema .description (" Server port (1024-65535)" )
39- ),
40- LOG_LEVEL: Schema .Literal (" debug" , " info" , " warn" , " error" ).pipe (
41- Schema .description (" Logging level" )
42- ),
43- NODE_ENV: Schema .Literal (" development" , " staging" , " production" ).pipe (
44- Schema .description (" Deployment environment" )
45- ),
26+ DATABASE_URL: Schema .String .pipe (
27+ Schema .annotations ({ description: ' PostgreSQL connection string' }),
28+ ),
29+ API_KEY: Schema .String .pipe (
30+ Schema .minLength (32 ),
31+ Schema .annotations ({
32+ description: ' API authentication key (min 32 chars)' ,
33+ }),
34+ ),
35+ PORT: Schema .String .pipe (
36+ Schema .parseNumber ,
37+ Schema .int (),
38+ Schema .between (1024 , 65535 ),
39+ Schema .annotations ({ description: ' Server port (1024-65535)' }),
40+ ),
41+ LOG_LEVEL: Schema .Literal (' debug' , ' info' , ' warn' , ' error' ).pipe (
42+ Schema .annotations ({ description: ' Logging level' }),
43+ ),
44+ NODE_ENV: Schema .Literal (' development' , ' staging' , ' production' ).pipe (
45+ Schema .annotations ({ description: ' Deployment environment' }),
46+ ),
4647})
4748
4849type Environment = typeof EnvironmentSchema .Type
@@ -51,56 +52,52 @@ type Environment = typeof EnvironmentSchema.Type
5152const validateEnv = Schema .decodeUnknown (EnvironmentSchema )
5253
5354// 3. Load and validate environment
54- const loadEnvironment = (): Effect .Effect <Environment , Error > =>
55- Effect .gen (function * () {
56- const envVars = process .env
57-
58- const validated = yield * Effect .tryPromise ({
59- try : () => validateEnv (envVars ),
60- catch : (error ) => {
61- const errorMsg =
62- error instanceof Error ? error .message : String (error )
63- return new Error (` Environment validation failed: ${errorMsg } ` )
64- },
65- })
66-
67- console .log (` ✅ Environment loaded: NODE_ENV=${validated .NODE_ENV } ` )
68- return validated
69- })
55+ const loadEnvironment = Effect .fn (function * () {
56+ const validated = yield * validateEnv (process .env )
7057
71- // 4. Create service to provide environment
72- class EnvironmentService {
73- constructor ( readonly env : Environment ) {}
58+ console . log ( ` ✅ Environment loaded: NODE_ENV=${ validated . NODE_ENV } ` )
59+ return validated
60+ })
7461
75- isDev = () => this .env .NODE_ENV === " development"
76- isProd = () => this .env .NODE_ENV === " production"
77- isStaging = () => this .env .NODE_ENV === " staging"
62+ // 4. Create service to provide environment
63+ export class EnvironmentService extends Context .Tag (' @app/EnvironmentService' )<
64+ EnvironmentService ,
65+ Environment & {
66+ isDev: () => boolean
67+ isStaging: () => boolean
68+ isProd: () => boolean
69+ }
70+ >() {
71+ static layer = Layer .effect (
72+ this ,
73+ Effect .gen (function * () {
74+ const env = yield * loadEnvironment ()
75+ return {
76+ ... env ,
77+ isDev : () => env .NODE_ENV === ' development' ,
78+ isStaging : () => env .NODE_ENV === ' staging' ,
79+ isProd : () => env .NODE_ENV === ' production' ,
80+ }
81+ }),
82+ )
7883}
7984
80- // 5. Provide environment as a service
81- const EnvironmentServiceLive = Effect .gen (function * () {
82- const env = yield * loadEnvironment ()
83- return new EnvironmentService (env )
84- }).pipe (Effect .layer )
85-
8685// Usage
87- const appLogic = Effect .gen (function * () {
88- const envService = yield * Effect . service ( EnvironmentService )
86+ const program = Effect .gen (function * () {
87+ const envService = yield * EnvironmentService
8988
90- console .log (` Database: ${envService . env .DATABASE_URL } ` )
91- console .log (` Port: ${envService . env .PORT } ` )
92- console .log (` Log level: ${envService . env .LOG_LEVEL } ` )
93- console .log (` Is production: ${envService .isProd ()} ` )
89+ console .log (` Database: ${envService .DATABASE_URL } ` )
90+ console .log (` Port: ${envService .PORT } ` )
91+ console .log (` Log level: ${envService .LOG_LEVEL } ` )
92+ console .log (` Is production: ${envService .isProd ()} ` )
9493
95- return envService . env .PORT
94+ return envService .PORT
9695})
9796
9897// Run with environment layer
99- Effect .runPromise (
100- appLogic .pipe (Effect .provide (EnvironmentServiceLive ))
101- )
102- .then ((port ) => console .log (` Server starting on port ${port } ` ))
103- .catch ((error ) => console .error (` Failed to start: ${error .message } ` ))
98+ Effect .runPromise (program .pipe (Effect .provide (EnvironmentService .layer )))
99+ .then ((port ) => console .log (` Server starting on port ${port } ` ))
100+ .catch ((error ) => console .error (` Failed to start: ${error .message } ` ))
104101```
105102
106103# Why This Works
0 commit comments