@fy-stack/fullstack-construct 0.0.147-alpha.303 → 0.0.147-alpha.304

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +881 -57
  2. package/package.json +14 -11
package/README.md CHANGED
@@ -1,75 +1,899 @@
1
- # Fullstack Construct Documentation
1
+ # @fy-stack/fullstack-construct
2
2
 
3
- ## Overview
3
+ A high-level AWS CDK construct that wires together the full suite of `@fy-stack` constructs into a single, unified infrastructure definition. It handles auth, storage, databases, compute (Lambda & ECS), static hosting, CDN, API Gateway, event routing, and secrets — all from one props object.
4
4
 
5
- The Fullstack Construct is a high-level AWS CDK construct that enables deploying full-stack applications to AWS infrastructure. It provides an integrated solution for deploying various types of applications with their associated infrastructure.
5
+ ## Table of Contents
6
6
 
7
- ## Supported Application Types
7
+ - [Installation](#installation)
8
+ - [Quick Start](#quick-start)
9
+ - [Props Reference](#props-reference)
10
+ - [name](#name)
11
+ - [environment](#environment)
12
+ - [vpcId](#vpcid)
13
+ - [ownerArn](#ownerarn)
14
+ - [outputs](#outputs)
15
+ - [auth](#auth)
16
+ - [storage](#storage)
17
+ - [database](#database)
18
+ - [lambda](#lambda)
19
+ - [ecs](#ecs)
20
+ - [static](#static)
21
+ - [event](#event)
22
+ - [cdn](#cdn)
23
+ - [api](#api)
24
+ - [secret](#secret)
25
+ - [Application Types](#application-types)
26
+ - [Grants & Attachments](#grants--attachments)
27
+ - [AppGrant](#appgrant)
28
+ - [AppAttachment](#appattachment)
29
+ - [ResourceRef](#resourceref)
30
+ - [Public Properties](#public-properties)
31
+ - [CloudFormation Outputs](#cloudformation-outputs)
32
+ - [Examples](#examples)
8
33
 
9
- ### 1. Static Applications
10
- - **Static Website** (`AppType.STATIC_WEBSITE`)
11
- - Simple static websites with HTML, CSS, and JavaScript
12
- - Deployed to S3 and served via CloudFront
34
+ ---
13
35
 
14
- ### 2. Next.js Applications
15
- - **Next.js Pages Export** (`AppType.NEXT_PAGE_EXPORT`)
16
- - Static export mode of Next.js applications
17
- - Optimized for static site generation
18
- - **Next.js App Router** (`AppType.NEXT_APP_ROUTER`)
19
- - Support for the newer App Router architecture
20
- - Includes handling for server components
36
+ ## Installation
21
37
 
22
- ### 3. Node.js Applications
23
- - **Node App** (`AppType.NODE_APP`)
24
- - General purpose Node.js applications
25
- - Deployable as containers or Lambda functions
26
- - **Node API** (`AppType.NODE_API`)
27
- - Specialized for NestJS API applications
28
- - Includes API Gateway integration
38
+ ```bash
39
+ npm install @fy-stack/fullstack-construct
40
+ ```
29
41
 
30
- ### 4. Container Applications
31
- - **Image App** (`AppType.IMAGE_APP`)
32
- - Docker container-based applications
33
- - Deployable to ECS with Fargate
42
+ The package requires `aws-cdk-lib` and `constructs` as peer dependencies.
34
43
 
35
- ## Deployment Options
44
+ ---
36
45
 
37
- ### 1. Lambda Deployment
38
- - Suitable for serverless applications
39
- - Configurable memory and timeout settings
40
- - Environment variable support
41
- - Example configuration:
46
+ ## Quick Start
42
47
 
43
48
  ```typescript
44
- {
45
- type: AppType.NODE_API,
46
- buildParams: {
47
- memorySize: 512,
48
- timeout: 30,
49
- environment: {
50
- NODE_ENV: 'production'
51
- }
52
- }
49
+ import { App, Stack } from 'aws-cdk-lib';
50
+ import { FullStackConstruct } from '@fy-stack/fullstack-construct';
51
+ import { AppType, AppGrant } from '@fy-stack/types';
52
+
53
+ const app = new App();
54
+ const stack = new Stack(app, 'MyStack');
55
+
56
+ new FullStackConstruct(stack, 'MyApp', {
57
+ name: 'my-app',
58
+ environment: 'production',
59
+ outputs: true,
60
+
61
+ auth: {
62
+ groups: ['admin', 'users'],
63
+ },
64
+
65
+ storage: {
66
+ retainOnDelete: true,
67
+ },
68
+
69
+ lambda: {
70
+ api: {
71
+ type: AppType.NODE_API,
72
+ output: 'dist/apps/api',
73
+ grants: [AppGrant.AUTH, AppGrant.STORAGE],
74
+ attachment: { auth: true, storage: true, secret: true },
75
+ },
76
+ },
77
+
78
+ static: {
79
+ web: {
80
+ type: AppType.NEXT_PAGE_EXPORT,
81
+ output: 'dist/apps/web/exported',
82
+ },
83
+ },
84
+
85
+ cdn: {
86
+ routes: {
87
+ '/api/*': { $resource: 'api' },
88
+ '/*': { $resource: 'web' },
89
+ },
90
+ },
91
+ });
92
+ ```
93
+
94
+ ---
95
+
96
+ ## Props Reference
97
+
98
+ ### `name`
99
+
100
+ **Type:** `string` — **Required**
101
+
102
+ The application name. Used as a prefix for resource names and as the value of the `App` tag applied to all resources.
103
+
104
+ ```typescript
105
+ name: 'my-app'
106
+ ```
107
+
108
+ ---
109
+
110
+ ### `environment`
111
+
112
+ **Type:** `string` — **Required**
113
+
114
+ The deployment environment (e.g. `"production"`, `"staging"`, `"dev"`). Used in resource names, SSM parameter paths, and as the value of the `Environment` tag.
115
+
116
+ ```typescript
117
+ environment: 'production'
118
+ ```
119
+
120
+ ---
121
+
122
+ ### `vpcId`
123
+
124
+ **Type:** `string` — **Optional**
125
+
126
+ The ID of an existing VPC to use. If omitted, the account's default VPC is used. The VPC is lazily resolved — it is only looked up when a construct actually requires it (database, ECS, or Lambda with VPC).
127
+
128
+ ```typescript
129
+ vpcId: 'vpc-0abc1234def56789'
130
+ ```
131
+
132
+ ---
133
+
134
+ ### `ownerArn`
135
+
136
+ **Type:** `string` — **Optional**
137
+
138
+ The ARN of an IAM user or role to grant ownership permissions over all tagged resources. The owner receives:
139
+
140
+ - A wildcard (`*`) policy on all resources tagged with the app name and environment.
141
+ - Full S3 access on the storage bucket (if storage is configured).
142
+
143
+ The ARN resource type is parsed automatically — both `arn:aws:iam::123456789012:user/alice` and `arn:aws:iam::123456789012:role/deploy-role` are supported.
144
+
145
+ ```typescript
146
+ ownerArn: 'arn:aws:iam::123456789012:role/deploy-role'
147
+ ```
148
+
149
+ ---
150
+
151
+ ### `outputs`
152
+
153
+ **Type:** `boolean` — **Optional**
154
+
155
+ When `true`, CloudFormation outputs are exported for key resource identifiers. See [CloudFormation Outputs](#cloudformation-outputs) for the full list.
156
+
157
+ ```typescript
158
+ outputs: true
159
+ ```
160
+
161
+ ---
162
+
163
+ ### `auth`
164
+
165
+ **Type:** `{ groups?: string[] }` — **Optional**
166
+
167
+ Provisions an [Amazon Cognito](https://aws.amazon.com/cognito/) user pool with a client and optional domain. When omitted, no authentication infrastructure is created.
168
+
169
+ | Field | Type | Description |
170
+ |-------|------|-------------|
171
+ | `groups` | `string[]` | Names of Cognito user pool groups to create |
172
+
173
+ ```typescript
174
+ auth: {
175
+ groups: ['admin', 'moderators', 'users'],
176
+ }
177
+ ```
178
+
179
+ **Defaults:**
180
+ - Self sign-up is enabled.
181
+ - Deletion protection is enabled.
182
+ - Sign-in is case-insensitive.
183
+ - Auth flows: `userPassword`, `userSrp`, `adminUserPassword`.
184
+ - Access token validity: 24 hours.
185
+ - Refresh token validity: 720 hours (30 days).
186
+
187
+ ---
188
+
189
+ ### `storage`
190
+
191
+ **Type:** `StorageConstructProps` — **Optional**
192
+
193
+ Provisions an S3 bucket with CORS enabled for `GET`, `PUT`, and `DELETE` requests. All public access is blocked by default.
194
+
195
+ | Field | Type | Default | Description |
196
+ |-------|------|---------|-------------|
197
+ | `retainOnDelete` | `boolean` | `false` | Keep the bucket when the stack is deleted |
198
+ | `logTable` | `boolean` | `false` | Create a DynamoDB table for access logging |
199
+ | `keys` | `string[]` | — | CloudFront key pair IDs for signed URL support. Creates a CloudFront `KeyGroup` when provided |
200
+
201
+ ```typescript
202
+ storage: {
203
+ retainOnDelete: true,
204
+ logTable: true,
205
+ keys: ['K2JCJMDEHXQW5F'],
206
+ }
207
+ ```
208
+
209
+ ---
210
+
211
+ ### `database`
212
+
213
+ **Type:** `DatabaseConstructProps` — **Optional**
214
+
215
+ Provisions an RDS database instance inside the VPC. Extends the CDK `DatabaseInstanceProps` with convenience overrides.
216
+
217
+ | Field | Type | Default | Description |
218
+ |-------|------|---------|-------------|
219
+ | `engine` | `IInstanceEngine` | — | RDS engine (e.g. `DatabaseInstanceEngine.postgres(...)`) |
220
+ | `instance` | `{ class: InstanceClass; size: InstanceSize }` | — | EC2 instance type for the database |
221
+ | `public` | `boolean` | `false` | Whether the instance is publicly accessible |
222
+ | `useDefault` | `boolean` | — | Use the default database configuration |
223
+
224
+ All other `DatabaseInstanceProps` fields (except `instanceType`, `databaseName`, `publiclyAccessible`, `engine`, and `vpc` — which are managed internally) are passed through.
225
+
226
+ ```typescript
227
+ import { DatabaseInstanceEngine, PostgresEngineVersion } from 'aws-cdk-lib/aws-rds';
228
+ import { InstanceClass, InstanceSize } from 'aws-cdk-lib/aws-ec2';
229
+
230
+ database: {
231
+ engine: DatabaseInstanceEngine.postgres({
232
+ version: PostgresEngineVersion.VER_16,
233
+ }),
234
+ instance: {
235
+ class: InstanceClass.T3,
236
+ size: InstanceSize.MICRO,
237
+ },
53
238
  }
54
239
  ```
55
240
 
241
+ ---
242
+
243
+ ### `lambda`
244
+
245
+ **Type:** `Record<string, LambdaApp>` — **Optional**
246
+
247
+ A map of named Lambda functions to deploy. Each key becomes the function's logical name and is used as the resource identifier in `cdn.routes` and `api.routes`.
56
248
 
57
- ### 2. ECS Deployment
58
- - Container-based deployment with Fargate
59
- - Supports both service and task patterns
60
- - Load balancer integration
61
- - Example configuration:
249
+ Each entry extends the base `App` type with two additional fields:
250
+
251
+ | Field | Type | Description |
252
+ |-------|------|-------------|
253
+ | `type` | `AppType` | One of `NODE_APP`, `NODE_API`, `IMAGE_APP`, `NEXT_APP_ROUTER` |
254
+ | `output` | `string` | Path to the built application directory |
255
+ | `env` | `Record<string, string>` | Static environment variables injected at deploy time |
256
+ | `timeout` | `number` | Function timeout in seconds |
257
+ | `queue` | `{ batchSize?: number } & QueueProps` | Attach an SQS queue as an event source |
258
+ | `buildParams` | `Partial<FunctionProps>` | CDK `FunctionProps` overrides (e.g. `memorySize`, `reservedConcurrentExecutions`) |
259
+ | `grants` | `AppGrant[]` | Permissions to grant to the function — see [AppGrant](#appgrant) |
260
+ | `attachment` | `AppAttachment` | Resources to attach as environment variables — see [AppAttachment](#appattachment) |
62
261
 
63
262
  ```typescript
64
- {
65
- server: {
263
+ lambda: {
264
+ api: {
265
+ type: AppType.NODE_API,
266
+ output: 'dist/apps/api',
267
+ timeout: 30,
268
+ buildParams: {
269
+ memorySize: 512,
270
+ },
271
+ env: {
272
+ NODE_ENV: 'production',
273
+ },
274
+ grants: [AppGrant.AUTH, AppGrant.STORAGE, AppGrant.SECRET],
275
+ attachment: {
276
+ auth: true,
277
+ storage: true,
278
+ secret: true,
279
+ },
280
+ },
281
+ worker: {
282
+ type: AppType.NODE_APP,
283
+ output: 'dist/apps/worker',
284
+ queue: { batchSize: 5 },
285
+ grants: [AppGrant.DATABASE, AppGrant.SECRET],
286
+ attachment: { database: true, secret: true },
287
+ },
288
+ }
289
+ ```
290
+
291
+ ---
292
+
293
+ ### `ecs`
294
+
295
+ **Type:** `EcsConfig` — **Optional**
296
+
297
+ Provisions an ECS cluster with Fargate. Supports long-running services behind an Application Load Balancer (`server`) and one-off background task definitions (`tasks`).
298
+
299
+ #### `ecs.server`
300
+
301
+ A single Fargate service with one or more containers behind an ALB.
302
+
303
+ | Field | Type | Description |
304
+ |-------|------|-------------|
305
+ | `apps` | `Record<string, ServerApp>` | Container apps to run in the service |
306
+ | `loadBalancer` | `{ arn, priorityRange } \| ApplicationLoadBalancerProps` | Reuse an existing ALB by ARN or create a new one |
307
+ | `definition` | `FargateTaskDefinitionProps` | CPU/memory for the task definition |
308
+ | `grants` | `AppGrant[]` | Permissions to grant the server's task role |
309
+ | `assignPublicIp` | `boolean` | Assign a public IP to the Fargate service (default: `false`) |
310
+
311
+ Each `ServerApp` entry:
312
+
313
+ | Field | Type | Description |
314
+ |-------|------|-------------|
315
+ | `type` | `AppType.NEXT_APP_ROUTER \| AppType.IMAGE_APP` | Application type |
316
+ | `output` | `string` | Path to the built application or Dockerfile directory |
317
+ | `port` | `number` | Container port to expose |
318
+ | `env` | `Record<string, string>` | Static environment variables |
319
+ | `attachment` | `AppAttachment` | Resources to attach to this specific container |
320
+ | `container` | `ContainerDefinitionOptions` | Additional CDK container options |
321
+
322
+ #### `ecs.tasks`
323
+
324
+ A map of Fargate task definitions for background processing (not behind a load balancer).
325
+
326
+ | Field | Type | Description |
327
+ |-------|------|-------------|
328
+ | `type` | `AppType.IMAGE_APP` | Must be `IMAGE_APP` |
329
+ | `output` | `string` | Path to the Dockerfile directory |
330
+ | `env` | `Record<string, string>` | Static environment variables |
331
+ | `grants` | `AppGrant[]` | Permissions to grant the task role |
332
+ | `attachment` | `AppAttachment` | Resources to attach to this task |
333
+ | `container` | `ContainerDefinitionOptions` | Additional CDK container options |
334
+
335
+ ```typescript
336
+ ecs: {
337
+ server: {
338
+ definition: {
339
+ cpu: 512,
340
+ memoryLimitMiB: 1024,
341
+ },
342
+ loadBalancer: {
343
+ // Create a new ALB:
344
+ internetFacing: true,
345
+ },
346
+ // Or reuse an existing ALB:
347
+ // loadBalancer: {
348
+ // arn: 'arn:aws:elasticloadbalancing:...',
349
+ // priorityRange: [1, 50000],
350
+ // },
351
+ grants: [AppGrant.SECRET, AppGrant.STORAGE],
66
352
  apps: {
67
- api: {
68
- type: AppType.NODE_API,
69
- // ECS-specific configuration
70
- }
71
- },
72
- loadBalancer: {
73
- // Load balancer configuration
74
- } }, tasks: { // Background task configurations } }
75
- ```
353
+ api: {
354
+ type: AppType.IMAGE_APP,
355
+ output: 'apps/api',
356
+ port: 3000,
357
+ attachment: { storage: true, secret: true },
358
+ },
359
+ },
360
+ },
361
+ tasks: {
362
+ migrate: {
363
+ type: AppType.IMAGE_APP,
364
+ output: 'apps/api',
365
+ grants: [AppGrant.DATABASE, AppGrant.SECRET],
366
+ attachment: { database: true, secret: true },
367
+ },
368
+ },
369
+ }
370
+ ```
371
+
372
+ ---
373
+
374
+ ### `static`
375
+
376
+ **Type:** `Record<string, StaticApp>` — **Optional**
377
+
378
+ A map of named static site deployments. Each entry is uploaded to S3 and served through CloudFront.
379
+
380
+ | Field | Type | Description |
381
+ |-------|------|-------------|
382
+ | `type` | `AppType.NEXT_PAGE_EXPORT \| AppType.STATIC_WEBSITE` | Static app type |
383
+ | `output` | `string` | Path to the built static output directory |
384
+ | `buildParams` | `Record<string, unknown>` | Additional build-time parameters |
385
+
386
+ ```typescript
387
+ static: {
388
+ web: {
389
+ type: AppType.NEXT_PAGE_EXPORT,
390
+ output: 'dist/apps/web/exported',
391
+ },
392
+ marketing: {
393
+ type: AppType.STATIC_WEBSITE,
394
+ output: 'dist/apps/marketing',
395
+ },
396
+ }
397
+ ```
398
+
399
+ ---
400
+
401
+ ### `event`
402
+
403
+ **Type:** `EventConfig` — **Optional**
404
+
405
+ Sets up SNS-based message routing between resources and supports scheduled (cron) invocations. Eligible targets are Lambda functions and ECS tasks.
406
+
407
+ #### `event.handlers`
408
+
409
+ A list of message handler mappings — which resource should process which message types:
410
+
411
+ | Field | Type | Description |
412
+ |-------|------|-------------|
413
+ | `$resource` | `string` | Key of the Lambda or ECS task that handles the messages |
414
+ | `messages` | `string[]` | List of message type strings this resource subscribes to |
415
+
416
+ #### `event.schedule`
417
+
418
+ A list of cron-based schedules that publish messages to subscribed resources:
419
+
420
+ | Field | Type | Description |
421
+ |-------|------|-------------|
422
+ | `messages` | `string[]` | Message types to publish on schedule |
423
+ | `cron` | `CronOptions` | AWS Events cron expression |
424
+ | `rate` | `Duration` | Fixed rate interval (alternative to `cron`) |
425
+ | `expression` | `string` | Raw cron expression string |
426
+
427
+ ```typescript
428
+ event: {
429
+ handlers: [
430
+ { $resource: 'worker', messages: ['user.created', 'order.placed'] },
431
+ { $resource: 'migrate', messages: ['db.migrate'] },
432
+ ],
433
+ schedule: [
434
+ {
435
+ messages: ['db.migrate'],
436
+ cron: { minute: '0', hour: '2', weekDay: 'MON' },
437
+ },
438
+ {
439
+ messages: ['reports.generate'],
440
+ rate: Duration.hours(24),
441
+ },
442
+ ],
443
+ }
444
+ ```
445
+
446
+ ---
447
+
448
+ ### `cdn`
449
+
450
+ **Type:** `CDNConfig` — **Optional**
451
+
452
+ Provisions a CloudFront distribution that routes requests to your Lambda functions, ECS services, static apps, and storage bucket.
453
+
454
+ #### `cdn.routes`
455
+
456
+ A map of URL path patterns to resource references. Each key is a CloudFront path pattern (e.g. `/api/*`) and the value is a `ResourceRef` pointing to a named resource.
457
+
458
+ | Field | Type | Description |
459
+ |-------|------|-------------|
460
+ | `$resource` | `string` | Key of the Lambda, ECS server app, static app, or `"storage"` |
461
+ | `public` | `boolean` | Whether the route is publicly accessible (default: `true`) |
462
+ | `keys` | `string[]` | CloudFront key pair IDs required to access signed routes |
463
+
464
+ #### `cdn.domains`
465
+
466
+ Optional custom domain configuration:
467
+
468
+ | Field | Type | Description |
469
+ |-------|------|-------------|
470
+ | `domain` | `string` | The root domain name |
471
+ | `records` | `string[]` | Subdomains to map (use `"*"` for the default/catch-all) |
472
+
473
+ ```typescript
474
+ cdn: {
475
+ routes: {
476
+ '/api/*': { $resource: 'api' },
477
+ '/assets/*': { $resource: 'storage' },
478
+ '/*': { $resource: 'web' },
479
+ },
480
+ domains: [
481
+ {
482
+ domain: 'example.com',
483
+ records: ['www', '*'],
484
+ },
485
+ ],
486
+ }
487
+ ```
488
+
489
+ When both `storage` and `cdn` are configured, a CloudFront bucket policy is automatically generated and exported as the `storageBucketCDNPolicy` CloudFormation output.
490
+
491
+ ---
492
+
493
+ ### `api`
494
+
495
+ **Type:** `{ routes: Record<string, ResourceRef> }` — **Optional**
496
+
497
+ Provisions an HTTP API Gateway and routes requests to Lambda functions or ECS services.
498
+
499
+ | Field | Type | Description |
500
+ |-------|------|-------------|
501
+ | `routes` | `Record<string, ResourceRef>` | Map of route keys to resource references |
502
+
503
+ The route key is passed directly to the underlying `ApiResource.api(path)` method of the referenced construct, so the exact path format depends on the application type.
504
+
505
+ ```typescript
506
+ api: {
507
+ routes: {
508
+ api: { $resource: 'api' },
509
+ },
510
+ }
511
+ ```
512
+
513
+ ---
514
+
515
+ ### `secret`
516
+
517
+ **Type:** `Record<string, string | undefined>` — **Optional**
518
+
519
+ Additional key/value pairs to include in the Secrets Manager secret. The following keys are always included automatically:
520
+
521
+ - `REGION` — AWS region
522
+ - `ENVIRONMENT` — the `environment` prop value
523
+
524
+ All constructs (auth, database, storage, event, cdn, api) also contribute their own connection values to the same secret automatically.
525
+
526
+ ```typescript
527
+ secret: {
528
+ STRIPE_SECRET_KEY: process.env.STRIPE_SECRET_KEY,
529
+ SENDGRID_API_KEY: process.env.SENDGRID_API_KEY,
530
+ }
531
+ ```
532
+
533
+ ---
534
+
535
+ ## Application Types
536
+
537
+ The `AppType` constant (from `@fy-stack/types`) defines all supported application types:
538
+
539
+ | Constant | Value | Runtimes | Description |
540
+ |----------|-------|----------|-------------|
541
+ | `AppType.NODE_APP` | `"nodeApp"` | Lambda, ECS | General-purpose Node.js application |
542
+ | `AppType.NODE_API` | `"nestApi"` | Lambda, ECS | NestJS API application with API Gateway integration |
543
+ | `AppType.IMAGE_APP` | `"imageApp"` | Lambda, ECS | Docker image-based application |
544
+ | `AppType.NEXT_APP_ROUTER` | `"nextAppRouter"` | Lambda, ECS | Next.js with App Router (server-rendered) |
545
+ | `AppType.NEXT_PAGE_EXPORT` | `"nextPageExport"` | Static | Next.js with static export (`next export`) |
546
+ | `AppType.STATIC_WEBSITE` | `"staticWebsite"` | Static | Plain HTML/CSS/JS static site |
547
+
548
+ ```typescript
549
+ import { AppType } from '@fy-stack/types';
550
+ ```
551
+
552
+ ---
553
+
554
+ ## Grants & Attachments
555
+
556
+ Two mechanisms control how compute resources (Lambda, ECS) interact with infrastructure resources (Auth, Storage, Database, Secrets).
557
+
558
+ ### AppGrant
559
+
560
+ `AppGrant` (from `@fy-stack/types`) grants IAM permissions to a Lambda function or ECS task role. The construct automatically resolves the grant to the correct resource.
561
+
562
+ | Constant | Value | Grants Access To |
563
+ |----------|-------|-----------------|
564
+ | `AppGrant.AUTH` | `"auth"` | Cognito user pool (read/write operations) |
565
+ | `AppGrant.STORAGE` | `"storage"` | S3 bucket (read/write) |
566
+ | `AppGrant.DATABASE` | `"database"` | RDS database credentials and connection |
567
+ | `AppGrant.SECRET` | `"secret"` | Secrets Manager secret (read) |
568
+ | `AppGrant.EVENT` | `"event"` | SNS topic (publish) |
569
+
570
+ ```typescript
571
+ import { AppGrant } from '@fy-stack/types';
572
+
573
+ lambda: {
574
+ api: {
575
+ type: AppType.NODE_API,
576
+ output: 'dist/apps/api',
577
+ grants: [AppGrant.AUTH, AppGrant.STORAGE, AppGrant.SECRET],
578
+ },
579
+ }
580
+ ```
581
+
582
+ ### AppAttachment
583
+
584
+ `AppAttachment` injects configuration values from a resource into a compute resource's environment variables at deploy time. Set a key to `true` to attach that resource's `attachable()` values.
585
+
586
+ | Field | Type | Injects |
587
+ |-------|------|---------|
588
+ | `auth` | `boolean` | Cognito user pool ID, client ID, and domain |
589
+ | `storage` | `boolean` | S3 bucket name and region |
590
+ | `database` | `boolean` | Database host, port, name, and credentials secret ARN |
591
+ | `secret` | `boolean` | Secrets Manager secret name and ARN |
592
+
593
+ ```typescript
594
+ lambda: {
595
+ api: {
596
+ type: AppType.NODE_API,
597
+ output: 'dist/apps/api',
598
+ attachment: {
599
+ auth: true,
600
+ storage: true,
601
+ secret: true,
602
+ },
603
+ },
604
+ }
605
+ ```
606
+
607
+ > **Note:** Granting permissions (`grants`) and attaching configuration (`attachment`) are independent. You typically need both — a grant for IAM access and an attachment for the runtime configuration values.
608
+
609
+ ---
610
+
611
+ ## ResourceRef
612
+
613
+ A `ResourceRef` (from `@fy-stack/types`) is a pointer to a named resource used in `cdn.routes` and `api.routes`. It has a single field:
614
+
615
+ | Field | Type | Description |
616
+ |-------|------|-------------|
617
+ | `$resource` | `string` | The key of the resource to reference |
618
+
619
+ Valid values for `$resource` are:
620
+
621
+ - Any key in `lambda` (Lambda function)
622
+ - Any key in `ecs.server.apps` (ECS container app)
623
+ - Any key in `static` (static website)
624
+ - `"storage"` (the S3 storage bucket — CDN only)
625
+
626
+ ```typescript
627
+ cdn: {
628
+ routes: {
629
+ '/api/*': { $resource: 'api' }, // references lambda.api
630
+ '/*': { $resource: 'web' }, // references static.web
631
+ },
632
+ }
633
+ ```
634
+
635
+ ---
636
+
637
+ ## Public Properties
638
+
639
+ After instantiation, `FullStackConstruct` exposes the following properties for downstream use:
640
+
641
+ | Property | Type | Description |
642
+ |----------|------|-------------|
643
+ | `vpc` | `ec2.IVpc \| undefined` | The VPC (resolved lazily) |
644
+ | `owner` | `iam.IUser \| iam.IRole \| undefined` | The owner IAM principal |
645
+ | `auth` | `AuthConstruct \| undefined` | Cognito construct |
646
+ | `storage` | `StorageConstruct \| undefined` | S3 storage construct |
647
+ | `storagePolicy` | `string \| undefined` | CloudFront bucket policy JSON string |
648
+ | `database` | `DatabaseConstruct \| undefined` | RDS database construct |
649
+ | `event` | `EventConstruct \| undefined` | Event/SNS construct |
650
+ | `ecs` | `EcsConstruct \| undefined` | ECS cluster construct |
651
+ | `lambda` | `LambdaConstruct \| undefined` | Lambda functions construct |
652
+ | `static` | `StaticConstruct \| undefined` | Static sites construct |
653
+ | `cdn` | `CDNConstruct \| undefined` | CloudFront distribution construct |
654
+ | `api` | `ApiGatewayConstruct \| undefined` | API Gateway construct |
655
+ | `secret` | `SecretsConstruct` | Secrets Manager construct (always present) |
656
+
657
+ ```typescript
658
+ const fullstack = new FullStackConstruct(stack, 'App', props);
659
+
660
+ // Access the CloudFront distribution
661
+ const distributionId = fullstack.cdn?.distribution.distributionId;
662
+
663
+ // Access the Secrets Manager secret
664
+ const secretArn = fullstack.secret.secrets.secretArn;
665
+ ```
666
+
667
+ ---
668
+
669
+ ## CloudFormation Outputs
670
+
671
+ When `outputs: true` is set, the following CloudFormation outputs are exported:
672
+
673
+ | Key | Condition | Value |
674
+ |-----|-----------|-------|
675
+ | `cdnURl` | `cdn` configured | `https://<cloudfront-domain>` |
676
+ | `apiUrl` | `api` configured | API Gateway invoke URL |
677
+ | `appSecrets` | Always | Secrets Manager secret name |
678
+ | `storageBucketCDNPolicy` | `storage` + `cdn` configured | CloudFront bucket policy JSON |
679
+
680
+ A CloudWatch log group is always created at `{name}-{environment}-logs` with a one-week retention and destroy removal policy.
681
+
682
+ ---
683
+
684
+ ## Examples
685
+
686
+ ### Serverless API + Static Frontend
687
+
688
+ A typical setup with a NestJS Lambda API, a Next.js static frontend, Cognito auth, S3 storage, and a CloudFront CDN:
689
+
690
+ ```typescript
691
+ import { App, Stack } from 'aws-cdk-lib';
692
+ import { FullStackConstruct } from '@fy-stack/fullstack-construct';
693
+ import { AppType, AppGrant } from '@fy-stack/types';
694
+
695
+ const app = new App();
696
+ const stack = new Stack(app, 'MyAppStack', {
697
+ env: { account: '123456789012', region: 'us-east-1' },
698
+ });
699
+
700
+ new FullStackConstruct(stack, 'MyApp', {
701
+ name: 'my-app',
702
+ environment: 'production',
703
+ outputs: true,
704
+
705
+ auth: {
706
+ groups: ['admin', 'users'],
707
+ },
708
+
709
+ storage: {
710
+ retainOnDelete: true,
711
+ },
712
+
713
+ lambda: {
714
+ api: {
715
+ type: AppType.NODE_API,
716
+ output: 'dist/apps/api',
717
+ timeout: 30,
718
+ buildParams: {
719
+ memorySize: 512,
720
+ },
721
+ grants: [AppGrant.AUTH, AppGrant.STORAGE, AppGrant.SECRET],
722
+ attachment: {
723
+ auth: true,
724
+ storage: true,
725
+ secret: true,
726
+ },
727
+ },
728
+ },
729
+
730
+ static: {
731
+ web: {
732
+ type: AppType.NEXT_PAGE_EXPORT,
733
+ output: 'dist/apps/web/exported',
734
+ },
735
+ },
736
+
737
+ cdn: {
738
+ routes: {
739
+ '/api/*': { $resource: 'api' },
740
+ '/assets/*': { $resource: 'storage' },
741
+ '/*': { $resource: 'web' },
742
+ },
743
+ },
744
+
745
+ secret: {
746
+ STRIPE_SECRET_KEY: process.env.STRIPE_SECRET_KEY,
747
+ },
748
+ });
749
+ ```
750
+
751
+ ---
752
+
753
+ ### ECS Deployment with Background Tasks
754
+
755
+ A containerised API on ECS Fargate with a background migration task and SNS-based event scheduling:
756
+
757
+ ```typescript
758
+ import { App, Stack } from 'aws-cdk-lib';
759
+ import { FullStackConstruct } from '@fy-stack/fullstack-construct';
760
+ import { AppType, AppGrant } from '@fy-stack/types';
761
+ import { DatabaseInstanceEngine, PostgresEngineVersion } from 'aws-cdk-lib/aws-rds';
762
+ import { InstanceClass, InstanceSize } from 'aws-cdk-lib/aws-ec2';
763
+
764
+ const app = new App();
765
+ const stack = new Stack(app, 'MyStack', {
766
+ env: { account: '123456789012', region: 'us-east-1' },
767
+ });
768
+
769
+ new FullStackConstruct(stack, 'MyApp', {
770
+ name: 'my-app',
771
+ environment: 'production',
772
+ outputs: true,
773
+
774
+ auth: {
775
+ groups: ['admin'],
776
+ },
777
+
778
+ storage: {},
779
+
780
+ database: {
781
+ engine: DatabaseInstanceEngine.postgres({
782
+ version: PostgresEngineVersion.VER_16,
783
+ }),
784
+ instance: {
785
+ class: InstanceClass.T3,
786
+ size: InstanceSize.MICRO,
787
+ },
788
+ },
789
+
790
+ ecs: {
791
+ server: {
792
+ definition: {
793
+ cpu: 1024,
794
+ memoryLimitMiB: 2048,
795
+ },
796
+ loadBalancer: {
797
+ internetFacing: true,
798
+ },
799
+ grants: [AppGrant.AUTH, AppGrant.STORAGE, AppGrant.SECRET],
800
+ apps: {
801
+ api: {
802
+ type: AppType.IMAGE_APP,
803
+ output: 'apps/api',
804
+ port: 3000,
805
+ attachment: {
806
+ auth: true,
807
+ storage: true,
808
+ database: true,
809
+ secret: true,
810
+ },
811
+ },
812
+ },
813
+ },
814
+ tasks: {
815
+ migrate: {
816
+ type: AppType.IMAGE_APP,
817
+ output: 'apps/api',
818
+ grants: [AppGrant.DATABASE, AppGrant.SECRET],
819
+ attachment: { database: true, secret: true },
820
+ },
821
+ },
822
+ },
823
+
824
+ event: {
825
+ handlers: [
826
+ { $resource: 'migrate', messages: ['db.migrate'] },
827
+ ],
828
+ schedule: [
829
+ {
830
+ messages: ['db.migrate'],
831
+ cron: { minute: '0', hour: '3', weekDay: 'MON' },
832
+ },
833
+ ],
834
+ },
835
+
836
+ cdn: {
837
+ routes: {
838
+ '/api/*': { $resource: 'api' },
839
+ '/assets/*': { $resource: 'storage' },
840
+ },
841
+ },
842
+ });
843
+ ```
844
+
845
+ ---
846
+
847
+ ### Reusing an Existing Load Balancer
848
+
849
+ When sharing an ALB across multiple stacks, provide the ARN and a priority range:
850
+
851
+ ```typescript
852
+ ecs: {
853
+ server: {
854
+ loadBalancer: {
855
+ arn: 'arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/shared-alb/abc123',
856
+ priorityRange: [100, 200],
857
+ },
858
+ apps: {
859
+ api: {
860
+ type: AppType.IMAGE_APP,
861
+ output: 'apps/api',
862
+ port: 3000,
863
+ },
864
+ },
865
+ },
866
+ tasks: {},
867
+ }
868
+ ```
869
+
870
+ ---
871
+
872
+ ### Minimal Static Website
873
+
874
+ ```typescript
875
+ new FullStackConstruct(stack, 'Site', {
876
+ name: 'my-site',
877
+ environment: 'production',
878
+ outputs: true,
879
+
880
+ static: {
881
+ web: {
882
+ type: AppType.STATIC_WEBSITE,
883
+ output: 'dist/web',
884
+ },
885
+ },
886
+
887
+ cdn: {
888
+ routes: {
889
+ '/*': { $resource: 'web' },
890
+ },
891
+ domains: [
892
+ {
893
+ domain: 'example.com',
894
+ records: ['www', '*'],
895
+ },
896
+ ],
897
+ },
898
+ });
899
+ ```
package/package.json CHANGED
@@ -1,20 +1,23 @@
1
1
  {
2
2
  "name": "@fy-stack/fullstack-construct",
3
- "version": "0.0.147-alpha.303",
3
+ "version": "0.0.147-alpha.304",
4
4
  "repository": "https://github.com/festusyuma/fy-stack",
5
5
  "dependencies": {
6
- "@fy-stack/apigateway-construct": "0.0.147-alpha.303",
7
- "@fy-stack/app-construct": "0.0.147-alpha.303",
8
- "@fy-stack/auth-construct": "0.0.147-alpha.303",
9
- "@fy-stack/cdn-construct": "0.0.147-alpha.303",
10
- "@fy-stack/database-construct": "0.0.147-alpha.303",
11
- "@fy-stack/event-construct": "0.0.147-alpha.303",
12
- "@fy-stack/secret-construct": "0.0.147-alpha.303",
13
- "@fy-stack/storage-construct": "0.0.147-alpha.303",
6
+ "@fy-stack/apigateway-construct": "0.0.147-alpha.304",
7
+ "@fy-stack/app-construct": "0.0.147-alpha.304",
8
+ "@fy-stack/auth-construct": "0.0.147-alpha.304",
9
+ "@fy-stack/cdn-construct": "0.0.147-alpha.304",
10
+ "@fy-stack/database-construct": "0.0.147-alpha.304",
11
+ "@fy-stack/event-construct": "0.0.147-alpha.304",
12
+ "@fy-stack/secret-construct": "0.0.147-alpha.304",
13
+ "@fy-stack/storage-construct": "0.0.147-alpha.304",
14
14
  "tslib": "^2.3.0",
15
- "@fy-stack/types": "0.0.147-alpha.303"
15
+ "@fy-stack/types": "0.0.147-alpha.304"
16
+ },
17
+ "peerDependencies": {
18
+ "aws-cdk-lib": "^2.235.0",
19
+ "constructs": "^10.4.4"
16
20
  },
17
- "peerDependencies": {},
18
21
  "type": "commonjs",
19
22
  "main": "./dist/index.js",
20
23
  "typings": "./dist/index.d.ts",