stacktape 3.5.8 → 3.6.0

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 (123) hide show
  1. package/.tsconfig.bun-build.json +1 -0
  2. package/ai-docs/cli-ref/aws-profile-create.md +22 -0
  3. package/ai-docs/cli-ref/aws-profile-delete.md +22 -0
  4. package/ai-docs/cli-ref/aws-profile-list.md +20 -0
  5. package/ai-docs/cli-ref/aws-profile-update.md +22 -0
  6. package/ai-docs/cli-ref/bastion-session.md +29 -0
  7. package/ai-docs/cli-ref/bastion-tunnel.md +30 -0
  8. package/ai-docs/cli-ref/bucket-sync.md +30 -0
  9. package/ai-docs/cli-ref/cf-module-update.md +26 -0
  10. package/ai-docs/cli-ref/cf-rollback.md +28 -0
  11. package/ai-docs/cli-ref/codebuild-deploy.md +34 -0
  12. package/ai-docs/cli-ref/compile-template.md +25 -0
  13. package/ai-docs/cli-ref/container-session.md +30 -0
  14. package/ai-docs/cli-ref/debug-alarms.md +28 -0
  15. package/ai-docs/cli-ref/debug-aws-sdk.md +33 -0
  16. package/ai-docs/cli-ref/debug-container-exec.md +36 -0
  17. package/ai-docs/cli-ref/debug-dynamodb.md +35 -0
  18. package/ai-docs/cli-ref/debug-logs.md +34 -0
  19. package/ai-docs/cli-ref/debug-metrics.md +33 -0
  20. package/ai-docs/cli-ref/debug-opensearch.md +35 -0
  21. package/ai-docs/cli-ref/debug-redis.md +36 -0
  22. package/ai-docs/cli-ref/debug-sql.md +35 -0
  23. package/ai-docs/cli-ref/defaults-configure.md +29 -0
  24. package/ai-docs/cli-ref/defaults-list.md +20 -0
  25. package/ai-docs/cli-ref/delete.md +24 -0
  26. package/ai-docs/cli-ref/deploy.md +25 -0
  27. package/ai-docs/cli-ref/deployment-script-run.md +28 -0
  28. package/ai-docs/cli-ref/dev-stop.md +26 -0
  29. package/ai-docs/cli-ref/dev.md +45 -0
  30. package/ai-docs/cli-ref/domain-add.md +26 -0
  31. package/ai-docs/cli-ref/help.md +18 -0
  32. package/ai-docs/cli-ref/info-operations.md +22 -0
  33. package/ai-docs/cli-ref/info-stack.md +30 -0
  34. package/ai-docs/cli-ref/info-stacks.md +26 -0
  35. package/ai-docs/cli-ref/info-whoami.md +22 -0
  36. package/ai-docs/cli-ref/init.md +30 -0
  37. package/ai-docs/cli-ref/login.md +20 -0
  38. package/ai-docs/cli-ref/logout.md +18 -0
  39. package/ai-docs/cli-ref/mcp-add.md +22 -0
  40. package/ai-docs/cli-ref/mcp.md +20 -0
  41. package/ai-docs/cli-ref/org-create.md +24 -0
  42. package/ai-docs/cli-ref/org-delete.md +24 -0
  43. package/ai-docs/cli-ref/org-list.md +22 -0
  44. package/ai-docs/cli-ref/package-workloads.md +25 -0
  45. package/ai-docs/cli-ref/param-get.md +26 -0
  46. package/ai-docs/cli-ref/preview-changes.md +23 -0
  47. package/ai-docs/cli-ref/project-create.md +22 -0
  48. package/ai-docs/cli-ref/projects-list.md +22 -0
  49. package/ai-docs/cli-ref/rollback.md +28 -0
  50. package/ai-docs/cli-ref/script-run.md +29 -0
  51. package/ai-docs/cli-ref/secret-create.md +28 -0
  52. package/ai-docs/cli-ref/secret-delete.md +26 -0
  53. package/ai-docs/cli-ref/secret-get.md +26 -0
  54. package/ai-docs/cli-ref/upgrade.md +20 -0
  55. package/ai-docs/cli-ref/version.md +18 -0
  56. package/ai-docs/concept/connecting-resources.md +369 -0
  57. package/ai-docs/concept/directives.md +371 -0
  58. package/ai-docs/concept/extending-cloudformation.md +315 -0
  59. package/ai-docs/concept/overrides-and-transforms.md +352 -0
  60. package/ai-docs/concept/stages-and-environments.md +347 -0
  61. package/ai-docs/concept/typescript-config.md +447 -0
  62. package/ai-docs/concept/yaml-config.md +338 -0
  63. package/ai-docs/config-ref/_root.md +131 -0
  64. package/ai-docs/config-ref/application-load-balancer.md +1109 -0
  65. package/ai-docs/config-ref/astro-web.md +115 -0
  66. package/ai-docs/config-ref/aws-cdk-construct.md +68 -0
  67. package/ai-docs/config-ref/bastion.md +93 -0
  68. package/ai-docs/config-ref/batch-job.md +179 -0
  69. package/ai-docs/config-ref/bucket.md +348 -0
  70. package/ai-docs/config-ref/cdn.md +496 -0
  71. package/ai-docs/config-ref/custom-resource.md +80 -0
  72. package/ai-docs/config-ref/deployment-script.md +79 -0
  73. package/ai-docs/config-ref/dynamo-db-table.md +202 -0
  74. package/ai-docs/config-ref/edge-lambda-function.md +87 -0
  75. package/ai-docs/config-ref/efs-filesystem.md +72 -0
  76. package/ai-docs/config-ref/event-bus.md +63 -0
  77. package/ai-docs/config-ref/function.md +409 -0
  78. package/ai-docs/config-ref/hosting-bucket.md +171 -0
  79. package/ai-docs/config-ref/http-api-gateway.md +149 -0
  80. package/ai-docs/config-ref/http-endpoint.md +92 -0
  81. package/ai-docs/config-ref/kinesis-stream.md +97 -0
  82. package/ai-docs/config-ref/mongo-db-atlas-cluster.md +254 -0
  83. package/ai-docs/config-ref/multi-container-workload.md +399 -0
  84. package/ai-docs/config-ref/network-load-balancer.md +118 -0
  85. package/ai-docs/config-ref/nextjs-web.md +147 -0
  86. package/ai-docs/config-ref/nuxt-web.md +81 -0
  87. package/ai-docs/config-ref/open-search.md +206 -0
  88. package/ai-docs/config-ref/private-service.md +75 -0
  89. package/ai-docs/config-ref/redis-cluster.md +223 -0
  90. package/ai-docs/config-ref/relational-database.md +525 -0
  91. package/ai-docs/config-ref/remix-web.md +74 -0
  92. package/ai-docs/config-ref/sns-topic.md +69 -0
  93. package/ai-docs/config-ref/solidstart-web.md +75 -0
  94. package/ai-docs/config-ref/sqs-queue-not-empty.md +414 -0
  95. package/ai-docs/config-ref/sqs-queue.md +232 -0
  96. package/ai-docs/config-ref/state-machine.md +235 -0
  97. package/ai-docs/config-ref/sveltekit-web.md +81 -0
  98. package/ai-docs/config-ref/tanstack-web.md +75 -0
  99. package/ai-docs/config-ref/upstash-redis.md +59 -0
  100. package/ai-docs/config-ref/user-auth-pool.md +876 -0
  101. package/ai-docs/config-ref/web-app-firewall.md +212 -0
  102. package/ai-docs/config-ref/web-service.md +178 -0
  103. package/ai-docs/config-ref/worker-service.md +41 -0
  104. package/ai-docs/getting-started/console.md +232 -0
  105. package/ai-docs/getting-started/deployment.md +434 -0
  106. package/ai-docs/getting-started/dev-mode.md +118 -0
  107. package/ai-docs/getting-started/how-it-works.md +119 -0
  108. package/ai-docs/getting-started/intro.md +157 -0
  109. package/ai-docs/getting-started/using-with-ai.md +228 -0
  110. package/ai-docs/getting-started/workflow.md +197 -0
  111. package/ai-docs/index.json +1514 -0
  112. package/ai-docs/recipe/background-jobs.md +183 -0
  113. package/ai-docs/recipe/database-migrations.md +240 -0
  114. package/ai-docs/recipe/graphql-api.md +211 -0
  115. package/ai-docs/recipe/monorepo-setup.md +183 -0
  116. package/ai-docs/recipe/nextjs-full-stack.md +188 -0
  117. package/ai-docs/recipe/rest-api-with-database.md +156 -0
  118. package/ai-docs/recipe/scheduled-tasks.md +186 -0
  119. package/ai-docs/recipe/static-website.md +241 -0
  120. package/ai-docs/troubleshooting/cloudformation-stack-states.md +189 -0
  121. package/bin/stacktape.js +0 -12
  122. package/package.json +1 -1
  123. package/plain.d.ts +372 -111
@@ -0,0 +1,183 @@
1
+ ---
2
+ docType: recipe
3
+ title: Background Jobs
4
+ tags:
5
+ - background
6
+ - jobs
7
+ - recipe
8
+ source: docs/_curated-docs/recipes/background-jobs.mdx
9
+ priority: 1
10
+ ---
11
+
12
+ # Background Job Processing
13
+
14
+ Process jobs asynchronously using SQS queues and Lambda functions.
15
+
16
+ ## Configuration
17
+
18
+ ```typescript
19
+ import { defineConfig, LambdaFunction, SqsQueue, HttpApiGateway } from 'stacktape';
20
+
21
+ export default defineConfig(() => {
22
+ // Job queue
23
+ const jobQueue = new SqsQueue({
24
+ visibilityTimeoutSeconds: 300, // 5 minutes to process
25
+ messageRetentionPeriodSeconds: 1209600 // Keep for 14 days
26
+ });
27
+
28
+ // Job processor
29
+ const jobProcessor = new LambdaFunction({
30
+ packaging: {
31
+ type: 'stacktape-lambda-buildpack',
32
+ properties: {
33
+ entryfilePath: './src/processor.ts'
34
+ }
35
+ },
36
+ timeout: 300, // 5 minute timeout
37
+ memory: 1024,
38
+ events: [
39
+ {
40
+ type: 'sqs',
41
+ properties: {
42
+ sqsQueueName: 'jobQueue',
43
+ batchSize: 10
44
+ }
45
+ }
46
+ ]
47
+ });
48
+
49
+ // API to enqueue jobs
50
+ const api = new LambdaFunction({
51
+ packaging: {
52
+ type: 'stacktape-lambda-buildpack',
53
+ properties: {
54
+ entryfilePath: './src/api.ts'
55
+ }
56
+ },
57
+ connectTo: [jobQueue]
58
+ });
59
+
60
+ const gateway = new HttpApiGateway({
61
+ routes: [{ path: '/jobs', method: 'POST', integration: { type: 'function', properties: { function: api } } }]
62
+ });
63
+
64
+ return {
65
+ resources: { jobQueue, jobProcessor, api, gateway }
66
+ };
67
+ });
68
+ ```
69
+
70
+ ## API Handler (Enqueue Jobs)
71
+
72
+ ```typescript
73
+ // src/api.ts
74
+ import { SQSClient, SendMessageCommand } from '@aws-sdk/client-sqs';
75
+ import { APIGatewayProxyHandlerV2 } from 'aws-lambda';
76
+
77
+ const sqs = new SQSClient({});
78
+
79
+ export const handler: APIGatewayProxyHandlerV2 = async (event) => {
80
+ const body = JSON.parse(event.body || '{}');
81
+
82
+ await sqs.send(
83
+ new SendMessageCommand({
84
+ QueueUrl: process.env.STP_JOBQUEUE_QUEUE_URL,
85
+ MessageBody: JSON.stringify({
86
+ type: body.type,
87
+ payload: body.payload,
88
+ createdAt: new Date().toISOString()
89
+ })
90
+ })
91
+ );
92
+
93
+ return {
94
+ statusCode: 202,
95
+ body: JSON.stringify({ message: 'Job queued' })
96
+ };
97
+ };
98
+ ```
99
+
100
+ ## Job Processor
101
+
102
+ ```typescript
103
+ // src/processor.ts
104
+ import { SQSHandler } from 'aws-lambda';
105
+
106
+ export const handler: SQSHandler = async (event) => {
107
+ for (const record of event.Records) {
108
+ const job = JSON.parse(record.body);
109
+
110
+ console.log(`Processing job: ${job.type}`);
111
+
112
+ switch (job.type) {
113
+ case 'email':
114
+ await sendEmail(job.payload);
115
+ break;
116
+ case 'resize-image':
117
+ await resizeImage(job.payload);
118
+ break;
119
+ case 'generate-report':
120
+ await generateReport(job.payload);
121
+ break;
122
+ default:
123
+ console.warn(`Unknown job type: ${job.type}`);
124
+ }
125
+ }
126
+ };
127
+
128
+ async function sendEmail(payload: any) {
129
+ // Email sending logic
130
+ }
131
+
132
+ async function resizeImage(payload: any) {
133
+ // Image processing logic
134
+ }
135
+
136
+ async function generateReport(payload: any) {
137
+ // Report generation logic
138
+ }
139
+ ```
140
+
141
+ ## Dead Letter Queue
142
+
143
+ Handle failed jobs:
144
+
145
+ ```typescript
146
+ // Failed jobs go here after 3 retries
147
+ const deadLetterQueue = new SqsQueue({
148
+ messageRetentionPeriodSeconds: 1209600
149
+ });
150
+
151
+ const jobQueue = new SqsQueue({
152
+ visibilityTimeoutSeconds: 300,
153
+ redrivePolicy: {
154
+ targetSqsQueueName: 'deadLetterQueue',
155
+ maxReceiveCount: 3 // Retry 3 times before DLQ
156
+ }
157
+ });
158
+
159
+ // Alert on DLQ messages
160
+ const dlqProcessor = new LambdaFunction({
161
+ packaging: {
162
+ type: 'stacktape-lambda-buildpack',
163
+ properties: {
164
+ entryfilePath: './src/dlq-alert.ts'
165
+ }
166
+ },
167
+ events: [
168
+ {
169
+ type: 'sqs',
170
+ properties: { sqsQueueName: 'deadLetterQueue' }
171
+ }
172
+ ]
173
+ });
174
+ ```
175
+
176
+ ## Usage
177
+
178
+ ```bash
179
+ # Enqueue a job
180
+ curl -X POST https://your-api.execute-api.us-east-1.amazonaws.com/jobs \
181
+ -H "Content-Type: application/json" \
182
+ -d '{"type": "email", "payload": {"to": "user@example.com", "subject": "Hello"}}'
183
+ ```
@@ -0,0 +1,240 @@
1
+ ---
2
+ docType: recipe
3
+ title: Database Migrations
4
+ tags:
5
+ - database
6
+ - migrations
7
+ - recipe
8
+ source: docs/_curated-docs/recipes/database-migrations.mdx
9
+ priority: 1
10
+ ---
11
+
12
+ # Database Migrations
13
+
14
+ Run database migrations automatically during deployment.
15
+
16
+ ## Using Prisma
17
+
18
+ ### Configuration
19
+
20
+ ```typescript
21
+ import {
22
+ defineConfig,
23
+ LambdaFunction,
24
+ RelationalDatabase,
25
+ RdsEnginePostgres,
26
+ HttpApiGateway,
27
+ $Secret,
28
+ $ResourceParam
29
+ } from 'stacktape';
30
+
31
+ export default defineConfig(({ stage }) => {
32
+ const database = new RelationalDatabase({
33
+ engine: new RdsEnginePostgres({ version: '16' }),
34
+ credentials: {
35
+ masterUserPassword: $Secret(`db-password-${stage}`)
36
+ }
37
+ });
38
+
39
+ const api = new LambdaFunction({
40
+ packaging: {
41
+ type: 'stacktape-lambda-buildpack',
42
+ properties: {
43
+ entryfilePath: './src/handler.ts'
44
+ }
45
+ },
46
+ connectTo: [database]
47
+ });
48
+
49
+ const gateway = new HttpApiGateway({
50
+ routes: [{ path: '/{proxy+}', method: '*', integration: { type: 'function', properties: { function: api } } }]
51
+ });
52
+
53
+ return {
54
+ hooks: {
55
+ afterDeploy: [{ scriptName: 'migrate' }]
56
+ },
57
+ scripts: {
58
+ migrate: {
59
+ executeCommand: 'npx prisma migrate deploy',
60
+ environment: {
61
+ DATABASE_URL: $ResourceParam('database', 'connectionString')
62
+ }
63
+ },
64
+ // Optional: seed script for development
65
+ seed: {
66
+ executeCommand: 'npx prisma db seed',
67
+ environment: {
68
+ DATABASE_URL: $ResourceParam('database', 'connectionString')
69
+ }
70
+ }
71
+ },
72
+ resources: { database, api, gateway }
73
+ };
74
+ });
75
+ ```
76
+
77
+ ### Prisma Schema
78
+
79
+ ```prisma
80
+ // prisma/schema.prisma
81
+ generator client {
82
+ provider = "prisma-client-js"
83
+ }
84
+
85
+ datasource db {
86
+ provider = "postgresql"
87
+ url = env("DATABASE_URL")
88
+ }
89
+
90
+ model User {
91
+ id Int @id @default(autoincrement())
92
+ email String @unique
93
+ name String?
94
+ posts Post[]
95
+ createdAt DateTime @default(now())
96
+ }
97
+
98
+ model Post {
99
+ id Int @id @default(autoincrement())
100
+ title String
101
+ content String?
102
+ author User @relation(fields: [authorId], references: [id])
103
+ authorId Int
104
+ createdAt DateTime @default(now())
105
+ }
106
+ ```
107
+
108
+ ### Creating Migrations
109
+
110
+ ```bash
111
+ # Development: create a new migration
112
+ npx prisma migrate dev --name add_users_table
113
+
114
+ # This creates: prisma/migrations/20240115_add_users_table/migration.sql
115
+ ```
116
+
117
+ ## Using Drizzle
118
+
119
+ ### Configuration
120
+
121
+ ```typescript
122
+ export default defineConfig(({ stage }) => {
123
+ // ... resources ...
124
+
125
+ return {
126
+ hooks: {
127
+ afterDeploy: [{ scriptName: 'migrate' }]
128
+ },
129
+ scripts: {
130
+ migrate: {
131
+ executeCommand: 'npx drizzle-kit push',
132
+ environment: {
133
+ DATABASE_URL: $ResourceParam('database', 'connectionString')
134
+ }
135
+ }
136
+ },
137
+ resources: { database, api, gateway }
138
+ };
139
+ });
140
+ ```
141
+
142
+ ## Using Raw SQL
143
+
144
+ ### Migration Script
145
+
146
+ ```typescript
147
+ // scripts/migrate.ts
148
+ import { Pool } from 'pg';
149
+ import { readdir, readFile } from 'fs/promises';
150
+ import { join } from 'path';
151
+
152
+ const pool = new Pool({
153
+ connectionString: process.env.DATABASE_URL
154
+ });
155
+
156
+ async function migrate() {
157
+ // Create migrations table if not exists
158
+ await pool.query(`
159
+ CREATE TABLE IF NOT EXISTS _migrations (
160
+ id SERIAL PRIMARY KEY,
161
+ name VARCHAR(255) NOT NULL UNIQUE,
162
+ applied_at TIMESTAMP DEFAULT NOW()
163
+ )
164
+ `);
165
+
166
+ // Get applied migrations
167
+ const { rows: applied } = await pool.query('SELECT name FROM _migrations');
168
+ const appliedNames = new Set(applied.map((r) => r.name));
169
+
170
+ // Get migration files
171
+ const migrationsDir = join(__dirname, '../migrations');
172
+ const files = await readdir(migrationsDir);
173
+ const pending = files
174
+ .filter((f) => f.endsWith('.sql'))
175
+ .filter((f) => !appliedNames.has(f))
176
+ .sort();
177
+
178
+ // Apply pending migrations
179
+ for (const file of pending) {
180
+ console.log(`Applying: ${file}`);
181
+ const sql = await readFile(join(migrationsDir, file), 'utf-8');
182
+
183
+ await pool.query('BEGIN');
184
+ try {
185
+ await pool.query(sql);
186
+ await pool.query('INSERT INTO _migrations (name) VALUES ($1)', [file]);
187
+ await pool.query('COMMIT');
188
+ console.log(`Applied: ${file}`);
189
+ } catch (error) {
190
+ await pool.query('ROLLBACK');
191
+ throw error;
192
+ }
193
+ }
194
+
195
+ console.log('Migrations complete');
196
+ }
197
+
198
+ migrate()
199
+ .catch(console.error)
200
+ .finally(() => pool.end());
201
+ ```
202
+
203
+ ### Stacktape Script
204
+
205
+ ```typescript
206
+ scripts: {
207
+ migrate: {
208
+ executeCommand: 'npx ts-node scripts/migrate.ts',
209
+ environment: {
210
+ DATABASE_URL: $ResourceParam('database', 'connectionString')
211
+ }
212
+ }
213
+ }
214
+ ```
215
+
216
+ ## Migration Best Practices
217
+
218
+ 1. **Always test migrations locally first**
219
+
220
+ ```bash
221
+ stacktape dev --stage dev --region us-east-1
222
+ # Then run migrations against local emulated database
223
+ ```
224
+
225
+ 2. **Make migrations idempotent when possible**
226
+
227
+ ```sql
228
+ CREATE TABLE IF NOT EXISTS users (...);
229
+ CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
230
+ ```
231
+
232
+ 3. **Use transactions for safety**
233
+ - Prisma and Drizzle handle this automatically
234
+ - For raw SQL, wrap in BEGIN/COMMIT
235
+
236
+ 4. **Back up production before migrating**
237
+ ```bash
238
+ # Create a snapshot before deploying
239
+ aws rds create-db-snapshot --db-instance-identifier my-db --db-snapshot-identifier pre-migration-backup
240
+ ```
@@ -0,0 +1,211 @@
1
+ ---
2
+ docType: recipe
3
+ title: GraphQL API
4
+ tags:
5
+ - graphql
6
+ - api
7
+ - recipe
8
+ source: docs/_curated-docs/recipes/graphql-api.mdx
9
+ priority: 1
10
+ ---
11
+
12
+ # GraphQL API
13
+
14
+ Deploy a GraphQL API using Apollo Server.
15
+
16
+ ## Lambda-based (Serverless)
17
+
18
+ ```typescript
19
+ import {
20
+ defineConfig,
21
+ LambdaFunction,
22
+ RelationalDatabase,
23
+ RdsEnginePostgres,
24
+ HttpApiGateway,
25
+ $Secret
26
+ } from 'stacktape';
27
+
28
+ export default defineConfig(({ stage }) => {
29
+ const database = new RelationalDatabase({
30
+ engine: new RdsEnginePostgres({ version: '16' }),
31
+ credentials: {
32
+ masterUserPassword: $Secret(`db-password-${stage}`)
33
+ }
34
+ });
35
+
36
+ const graphql = new LambdaFunction({
37
+ packaging: {
38
+ type: 'stacktape-lambda-buildpack',
39
+ properties: {
40
+ entryfilePath: './src/graphql.ts'
41
+ }
42
+ },
43
+ timeout: 30,
44
+ memory: 1024,
45
+ connectTo: [database]
46
+ });
47
+
48
+ const gateway = new HttpApiGateway({
49
+ routes: [{ path: '/graphql', method: '*', integration: { type: 'function', properties: { function: graphql } } }]
50
+ });
51
+
52
+ return {
53
+ resources: { database, graphql, gateway }
54
+ };
55
+ });
56
+ ```
57
+
58
+ ## Apollo Server Handler
59
+
60
+ ```typescript
61
+ // src/graphql.ts
62
+ import { ApolloServer } from '@apollo/server';
63
+ import { startServerAndCreateLambdaHandler, handlers } from '@as-integrations/aws-lambda';
64
+ import { PrismaClient } from '@prisma/client';
65
+
66
+ const prisma = new PrismaClient();
67
+
68
+ const typeDefs = `#graphql
69
+ type User {
70
+ id: ID!
71
+ email: String!
72
+ name: String
73
+ posts: [Post!]!
74
+ }
75
+
76
+ type Post {
77
+ id: ID!
78
+ title: String!
79
+ content: String
80
+ author: User!
81
+ }
82
+
83
+ type Query {
84
+ users: [User!]!
85
+ user(id: ID!): User
86
+ posts: [Post!]!
87
+ }
88
+
89
+ type Mutation {
90
+ createUser(email: String!, name: String): User!
91
+ createPost(title: String!, content: String, authorId: ID!): Post!
92
+ }
93
+ `;
94
+
95
+ const resolvers = {
96
+ Query: {
97
+ users: () => prisma.user.findMany(),
98
+ user: (_: any, { id }: { id: string }) => prisma.user.findUnique({ where: { id: parseInt(id) } }),
99
+ posts: () => prisma.post.findMany()
100
+ },
101
+ Mutation: {
102
+ createUser: (_: any, { email, name }: { email: string; name?: string }) =>
103
+ prisma.user.create({ data: { email, name } }),
104
+ createPost: (_: any, { title, content, authorId }: { title: string; content?: string; authorId: string }) =>
105
+ prisma.post.create({ data: { title, content, authorId: parseInt(authorId) } })
106
+ },
107
+ User: {
108
+ posts: (user: any) => prisma.post.findMany({ where: { authorId: user.id } })
109
+ },
110
+ Post: {
111
+ author: (post: any) => prisma.user.findUnique({ where: { id: post.authorId } })
112
+ }
113
+ };
114
+
115
+ const server = new ApolloServer({
116
+ typeDefs,
117
+ resolvers,
118
+ introspection: process.env.NODE_ENV !== 'production'
119
+ });
120
+
121
+ export const handler = startServerAndCreateLambdaHandler(server, handlers.createAPIGatewayProxyEventV2RequestHandler());
122
+ ```
123
+
124
+ ## Container-based (For Complex APIs)
125
+
126
+ ```typescript
127
+ import { defineConfig, WebService, RelationalDatabase, RdsEnginePostgres, $Secret } from 'stacktape';
128
+
129
+ export default defineConfig(({ stage }) => {
130
+ const database = new RelationalDatabase({
131
+ engine: new RdsEnginePostgres({ version: '16' }),
132
+ credentials: {
133
+ masterUserPassword: $Secret(`db-password-${stage}`)
134
+ }
135
+ });
136
+
137
+ const graphql = new WebService({
138
+ packaging: {
139
+ type: 'stacktape-image-buildpack',
140
+ properties: {
141
+ entryfilePath: './src/server.ts'
142
+ }
143
+ },
144
+ resources: {
145
+ cpu: 0.5,
146
+ memory: 1024
147
+ },
148
+ connectTo: [database],
149
+ scaling: {
150
+ minInstances: 1,
151
+ maxInstances: 10
152
+ }
153
+ });
154
+
155
+ return {
156
+ resources: { database, graphql }
157
+ };
158
+ });
159
+ ```
160
+
161
+ ## Container Server
162
+
163
+ ```typescript
164
+ // src/server.ts
165
+ import { ApolloServer } from '@apollo/server';
166
+ import { expressMiddleware } from '@apollo/server/express4';
167
+ import express from 'express';
168
+ import cors from 'cors';
169
+
170
+ const app = express();
171
+
172
+ const server = new ApolloServer({
173
+ typeDefs,
174
+ resolvers
175
+ });
176
+
177
+ await server.start();
178
+
179
+ app.use('/graphql', cors(), express.json(), expressMiddleware(server));
180
+
181
+ app.listen(process.env.PORT || 80, () => {
182
+ console.log('GraphQL server running');
183
+ });
184
+ ```
185
+
186
+ ## Dependencies
187
+
188
+ ```json
189
+ {
190
+ "dependencies": {
191
+ "@apollo/server": "^4.0.0",
192
+ "@as-integrations/aws-lambda": "^3.0.0",
193
+ "@prisma/client": "^5.0.0",
194
+ "graphql": "^16.0.0"
195
+ }
196
+ }
197
+ ```
198
+
199
+ ## Testing
200
+
201
+ ```bash
202
+ # Query
203
+ curl -X POST https://your-api.execute-api.us-east-1.amazonaws.com/graphql \
204
+ -H "Content-Type: application/json" \
205
+ -d '{"query": "{ users { id email name } }"}'
206
+
207
+ # Mutation
208
+ curl -X POST https://your-api.execute-api.us-east-1.amazonaws.com/graphql \
209
+ -H "Content-Type: application/json" \
210
+ -d '{"query": "mutation { createUser(email: \"test@example.com\", name: \"Test\") { id } }"}'
211
+ ```