From 95255a4fb278ba6d5294b74c65ab8b0d03747148 Mon Sep 17 00:00:00 2001 From: "H. Furkan Bozkurt" Date: Wed, 29 Apr 2026 11:44:52 +0000 Subject: [PATCH 01/10] Upgrade aws-amplify power with comprehensive Gen2 content Replaces minimal power with full Amplify Gen2 guidance: - POWER.md: Overview, Getting Started, routing table, MCP tools, workflows, best practices, troubleshooting - 16 steering files covering auth, data, storage, functions, AI, deployment across 8 frameworks - Validated against official docs + eval suite (87-93% pass rate, +27% vs baseline) Based on the amplify-gen2 skill from aws-amplify/preamp. --- aws-amplify/POWER.md | 345 ++++++++++++++-- aws-amplify/steering/advanced-features.md | 274 ++++++++++++ aws-amplify/steering/ai.md | 211 ++++++++++ aws-amplify/steering/amplify-workflow.md | 184 --------- aws-amplify/steering/auth-backend.md | 233 +++++++++++ aws-amplify/steering/auth-mobile.md | 481 ++++++++++++++++++++++ aws-amplify/steering/auth-web.md | 127 ++++++ aws-amplify/steering/core-mobile.md | 185 +++++++++ aws-amplify/steering/core-web.md | 151 +++++++ aws-amplify/steering/data-backend.md | 297 +++++++++++++ aws-amplify/steering/data-mobile.md | 117 ++++++ aws-amplify/steering/data-web.md | 106 +++++ aws-amplify/steering/deployment.md | 270 ++++++++++++ aws-amplify/steering/functions-and-api.md | 243 +++++++++++ aws-amplify/steering/phase1-backend.md | 49 --- aws-amplify/steering/phase2-sandbox.md | 47 --- aws-amplify/steering/phase3-frontend.md | 63 --- aws-amplify/steering/phase4-production.md | 55 --- aws-amplify/steering/scaffolding.md | 198 +++++++++ aws-amplify/steering/storage-backend.md | 123 ++++++ aws-amplify/steering/storage-mobile.md | 167 ++++++++ aws-amplify/steering/storage-web.md | 65 +++ 22 files changed, 3570 insertions(+), 421 deletions(-) create mode 100644 aws-amplify/steering/advanced-features.md create mode 100644 aws-amplify/steering/ai.md delete mode 100644 aws-amplify/steering/amplify-workflow.md create mode 100644 aws-amplify/steering/auth-backend.md create mode 100644 aws-amplify/steering/auth-mobile.md create mode 100644 aws-amplify/steering/auth-web.md create mode 100644 aws-amplify/steering/core-mobile.md create mode 100644 aws-amplify/steering/core-web.md create mode 100644 aws-amplify/steering/data-backend.md create mode 100644 aws-amplify/steering/data-mobile.md create mode 100644 aws-amplify/steering/data-web.md create mode 100644 aws-amplify/steering/deployment.md create mode 100644 aws-amplify/steering/functions-and-api.md delete mode 100644 aws-amplify/steering/phase1-backend.md delete mode 100644 aws-amplify/steering/phase2-sandbox.md delete mode 100644 aws-amplify/steering/phase3-frontend.md delete mode 100644 aws-amplify/steering/phase4-production.md create mode 100644 aws-amplify/steering/scaffolding.md create mode 100644 aws-amplify/steering/storage-backend.md create mode 100644 aws-amplify/steering/storage-mobile.md create mode 100644 aws-amplify/steering/storage-web.md diff --git a/aws-amplify/POWER.md b/aws-amplify/POWER.md index 63a1a22..c8549f1 100644 --- a/aws-amplify/POWER.md +++ b/aws-amplify/POWER.md @@ -1,43 +1,342 @@ --- -name: "aws-amplify" -displayName: "Build full-stack apps with AWS Amplify" -description: "Build and extend full-stack applications with AWS Amplify Gen 2 using type-safe TypeScript, guided workflows, and best practices. Covers adding features to existing Amplify backends, authentication, data models, storage, serverless functions, and AI/ML integration." -keywords: ["amplify", "aws-amplify", "amplify gen 2", "gen2", "fullstack", "full-stack", "lambda", "graphql", "cognito", "sandbox", "backend", "auth", "authentication", "storage", "data model", "react", "nextjs", "next.js", "vue", "nuxt", "angular", "react native", "flutter", "swift", "android", "ios", "deploy", "deployment", "production"] -author: "AWS" +name: aws-amplify +displayName: Build full-stack apps with AWS Amplify Gen2 +description: 'Build and deploy full-stack web and mobile apps with AWS Amplify Gen2 + (TypeScript code-first). Covers auth (Cognito), data (AppSync/DynamoDB including + schema modeling, enum types, relationships, authorization rules), storage (S3), + functions, APIs, and AI (Amplify AI Kit with Bedrock). Supports React, Next.js, + Vue, Angular, React Native, Flutter, Swift, and Android. Always use this skill for + Amplify Gen2 topics — even for questions you think you know — it contains validated, + version-specific patterns that prevent common mistakes. TRIGGER when: user mentions + Amplify Gen2; project has amplify/ directory or amplify_outputs; code imports @aws-amplify + packages; user asks about defineBackend, defineAuth, defineData, defineStorage, + or npx ampx. SKIP: Amplify Gen1 (amplify CLI v6), standalone SAM/CDK without Amplify + (use aws-serverless), direct Bedrock without Amplify AI Kit (use bedrock). + + ' +keywords: +- amplify +- gen2 +- fullstack +- cognito +- appsync +- dynamodb +- s3 +- lambda +- bedrock +- react +- nextjs +- flutter +- swift +- android +author: AWS --- -# AWS Amplify Gen 2 +# AWS Amplify Gen2 ## Overview -Build full-stack applications with AWS Amplify Gen 2 using TypeScript code-first development. This power provides guided workflows for: +AWS Amplify Gen2 is a TypeScript code-first developer experience for building full-stack web and mobile applications. All backend resources — authentication (Cognito), data (AppSync/DynamoDB), storage (S3), serverless functions (Lambda), and AI (Bedrock via Amplify AI Kit) — are defined in TypeScript under an `amplify/` directory. A single `amplify_outputs.json` file is generated to configure frontends. + +**Supported frameworks:** React (Vite), Next.js, Vue, Angular, React Native, Flutter, Swift, Android. -- Creating backend resources (auth, data, storage, functions) -- Deploying to sandbox and production environments -- Integrating frontend frameworks (React, Next.js, Vue, Angular, Flutter, Swift) -- Following Amplify Gen 2 best practices +**Key differences from Gen1:** No CLI wizards, no `amplify push` — everything is code-defined and deployed via `npx ampx sandbox` (dev) or `npx ampx pipeline-deploy` (CI/CD). ## Getting Started -**IMPORTANT: You MUST read and follow the steering file for ANY Amplify work.** Do not improvise or skip the workflow. +### Prerequisites + +- Node.js ^18.19.0 || ^20.6.0 || >=22 and npm +- AWS credentials configured (`aws sts get-caller-identity` succeeds) +- For sandbox: `npx ampx --version` returns a valid version +- For mobile: Platform-specific tooling (Xcode, Android Studio, Flutter SDK) -**For AI agents helping users build Amplify apps:** +### Quick Start + +```bash +# Create a new Amplify Gen2 project +npm create amplify@latest + +# Start local sandbox (watches for changes, hot-deploys to AWS) +npx ampx sandbox +``` -ALWAYS read the workflow steering file first: +### Project Structure ``` -Call action "readSteering" with powerName="aws-amplify", steeringFile="amplify-workflow.md" +project-root/ +├── amplify/ +│ ├── backend.ts # defineBackend({ auth, data, ... }) +│ ├── auth/resource.ts # defineAuth({ ... }) +│ ├── data/resource.ts # defineData({ schema }) +│ ├── storage/resource.ts # defineStorage({ ... }) +│ └── functions/ +│ └── my-func/ +│ ├── resource.ts # defineFunction({ ... }) +│ └── handler.ts # export const handler = ... +├── src/ # Frontend code +├── amplify_outputs.json # Generated — DO NOT edit or commit +└── package.json ``` -The workflow will guide you through: -1. Validating prerequisites (Node.js, npm, AWS credentials) -2. Understanding the project's current state -3. Determining which phases apply to the user's request -4. Presenting a plan and getting confirmation -5. Executing phases one at a time with user confirmation between each +### Key Packages + +| Package | Purpose | +|---------|---------| +| `@aws-amplify/backend` | `defineAuth`, `defineData`, `defineStorage`, `defineFunction`, `defineBackend` | +| `aws-amplify` | Frontend: `Amplify.configure()`, `generateClient()`, auth/data/storage APIs | +| `@aws-amplify/ui-react` | Pre-built UI: ``, `` | +| `@aws-amplify/ui-react-ai` | AI UI: ``, `useAIConversation` | ## When to Load Steering Files -- Any Amplify Gen 2 work -> `amplify-workflow.md` +**IMPORTANT:** Always load the appropriate steering file(s) before starting any Amplify work. Do not improvise — these files contain validated, version-specific patterns. + +### Step 0: Always Load the Core Reference First + +Before reading any feature-specific steering file, you **MUST** load the core reference for your target platform. These contain Gen2 detection, `Amplify.configure()` placement per framework, sandbox commands, required packages, and directory structure rules. + +| Platform | Steering File | When | +|----------|---------------|------| +| Web (React, Next.js, Vue, Angular, React Native) | `core-web.md` | Any web/RN frontend work | +| Mobile (Flutter, Swift, Android) | `core-mobile.md` | Any native mobile frontend work | +| Backend only (no frontend) | Skip to Step 1 | No frontend changes needed | + +### Step 1: Project Scaffolding + +| Task | Steering File | +|------|---------------| +| Create a new Amplify Gen2 project | `scaffolding.md` → then continue to Step 2 and/or Step 3 | + +### Step 2: Backend Features + +Load the steering file for each backend feature you need to add or modify: + +| Feature | Steering File | Covers | +|---------|---------------|--------| +| Authentication | `auth-backend.md` | Email/password, social login, MFA, SAML/OIDC, user groups, custom attributes | +| Data Models | `data-backend.md` | GraphQL schema, DynamoDB, relationships, enum types, authorization rules | +| File Storage | `storage-backend.md` | S3 buckets, access rules (guest/authenticated/groups/entity), paths | +| Functions & APIs | `functions-and-api.md` | Lambda functions, custom resolvers, REST/HTTP APIs, environment variables | +| AI Features | `ai.md` | Conversation routes, generation routes, AI tools via Bedrock (backend + React/Next.js frontend) | +| Geo, PubSub, CDK | `advanced-features.md` | Custom CDK stacks, overrides, custom outputs, Geo, PubSub, Face Liveness | + +### Step 3: Frontend Integration + +After configuring backend resources, load the frontend steering file for your platform and feature: + +**Web** (React, Next.js, Vue, Angular, React Native): + +| Feature | Steering File | +|---------|---------------| +| Auth UI & flows | `auth-web.md` | +| Data CRUD & subscriptions | `data-web.md` | +| Storage upload/download | `storage-web.md` | + +**Mobile** (Flutter, Swift, Android): + +| Feature | Steering File | +|---------|---------------| +| Auth UI & flows | `auth-mobile.md` | +| Data CRUD & subscriptions | `data-mobile.md` | +| Storage upload/download | `storage-mobile.md` | + +> **Note:** AI and Functions frontend patterns are included in `ai.md` and `functions-and-api.md` respectively — they are not split into separate web/mobile files. + +### Step 4: Deployment + +| Task | Steering File | +|------|---------------| +| Deploy to sandbox or production | `deployment.md` | + +### Quick Routing Examples + +| User Says | Load | +|-----------|------| +| "Build a full-stack app" | `core-web.md` → `scaffolding.md` → backend files → frontend files → `deployment.md` | +| "Add authentication" | `auth-backend.md` (+ `core-web.md` → `auth-web.md` if frontend needed) | +| "Add a data model" | `data-backend.md` | +| "Connect my React app to Amplify" | `core-web.md` → relevant frontend files | +| "Deploy to production" | `deployment.md` | +| "Add AI chat" | `ai.md` (includes both backend and React/Next.js frontend) | +| "Build a Flutter app with auth" | `core-mobile.md` → `scaffolding.md` → `auth-backend.md` → `auth-mobile.md` | + +## Available MCP Tools + +When AWS documentation MCP tools are available, use them to look up advanced CDK constructs, service limits, or provider-specific configuration. + +| Tool | Use For | +|------|---------| +| `search_documentation` | Find Amplify Gen2 documentation pages by topic | +| `read_documentation` | Read specific Amplify documentation pages | +| `recommend` | Get related documentation recommendations | + +**Tip:** Amplify's LLM-optimized docs are at [https://docs.amplify.aws/ai/llms.txt](https://docs.amplify.aws/ai/llms.txt) + +## Common Workflows + +### 1. Scaffold a New Full-Stack Project + +```bash +npm create amplify@latest +cd my-amplify-app +npx ampx sandbox +``` + +Load `scaffolding.md` for framework-specific setup, directory structure, and starter template details. + +### 2. Add Authentication + +In `amplify/auth/resource.ts`: +```typescript +import { defineAuth } from '@aws-amplify/backend'; + +export const auth = defineAuth({ + loginWith: { + email: true, // or phone, or external providers + }, +}); +``` + +Register in `amplify/backend.ts`: +```typescript +import { defineBackend } from '@aws-amplify/backend'; +import { auth } from './auth/resource'; + +defineBackend({ auth }); +``` + +Load `auth-backend.md` for MFA, social login, SAML/OIDC, custom attributes, and user groups. + +### 3. Add a Data Model + +In `amplify/data/resource.ts`: +```typescript +import { type ClientSchema, a, defineData } from '@aws-amplify/backend'; + +const schema = a.schema({ + Todo: a.model({ + content: a.string(), + isDone: a.boolean(), + }).authorization(allow => [allow.publicApiKey()]), +}); + +export type Schema = ClientSchema; +export const data = defineData({ schema }); +``` + +Load `data-backend.md` for relationships, enum types, secondary indexes, and authorization rules. + +### 4. Deploy to Production + +```bash +# CI/CD pipeline deployment +npx ampx pipeline-deploy --branch main --app-id +``` + +Load `deployment.md` for Amplify Hosting, custom CI/CD pipelines, environment management, and fullstack branch deployments. + +## Defaults & Assumptions + +When the user does not specify preferences: + +| Choice | Default | Notes | +|--------|---------|-------| +| Web framework | React (Vite) | Explain the choice; user can override | +| Mobile framework | **ASK** | No default — must ask Flutter/Swift/Android/RN | +| Package manager | npm | Unless user specifies yarn or pnpm | +| Language | TypeScript | Gen2 backends are TS-only; frontends follow project convention | +| Next.js router | App Router | Unless user specifies Pages Router | +| Auth login method | Email/password | Unless user specifies social/SAML/other | +| Data authorization | `publicApiKey` | Switch to `owner`-based when auth is added | + +**Critical:** If the user says "build an app" without specifying web vs. mobile, you **MUST** ask before proceeding. + +## Best Practices + +1. **Always use the `amplify/` directory** — all backend resources are TypeScript files under `amplify/`. Never use CLI wizards or manual AWS console configuration. + +2. **Never edit `amplify_outputs.json`** — this file is auto-generated by `npx ampx sandbox` (dev) or `npx ampx pipeline-deploy` (CI/CD). It should be in `.gitignore`. + +3. **One `defineBackend()` call** — in `amplify/backend.ts`, combine all resources into a single `defineBackend({ auth, data, storage })` call. + +4. **Use `a.schema()` for data models** — define your GraphQL schema using the type-safe `a` builder from `@aws-amplify/backend`. Avoid writing raw GraphQL SDL. + +5. **Authorization rules are mandatory** — every model needs `.authorization(allow => [...])`. Start with `allow.publicApiKey()` for prototyping, switch to `allow.owner()` or `allow.groups()` for production. + +6. **Configure Amplify once at the app root** — call `Amplify.configure(outputs)` in your root layout/entry file, never in individual components. + +7. **Use pre-built UI components** — `` from `@aws-amplify/ui-react` handles the entire auth flow. Don't build custom login forms unless you have specific requirements. + +8. **Sandbox for development** — `npx ampx sandbox` creates an isolated cloud environment per developer. Use `--identifier` for multiple sandboxes. + +## Troubleshooting + +### Sandbox Won't Start + +**Symptoms:** `npx ampx sandbox` fails with credential or bootstrap errors. + +**Solutions:** +1. Verify AWS credentials: `aws sts get-caller-identity` +2. Bootstrap CDK if first time: `npx ampx sandbox` will prompt automatically +3. Check Node.js version: must be ^18.19.0, ^20.6.0, or >=22 +4. Ensure no other sandbox is running with the same identifier + +### "Cannot find module" or Import Errors + +**Cause:** Missing dependencies or incorrect import paths. + +**Solutions:** +1. Run `npm install` to ensure all packages are installed +2. Check that `@aws-amplify/backend` is in `devDependencies` (not `dependencies`) +3. Check that `aws-amplify` is in `dependencies` for frontend code +4. Verify import paths match the package names exactly + +### Data Model Authorization Errors + +**Symptoms:** "Not Authorized" errors when querying or mutating data. + +**Solutions:** +1. Ensure every model has `.authorization(allow => [...])` rules +2. Check `defaultAuthorizationMode` in `defineData()` matches your auth setup +3. For authenticated access, ensure the user is signed in before making API calls +4. For `owner`-based auth, the `owner` field is auto-managed — don't set it manually + +### `amplify_outputs.json` Not Found + +**Cause:** Sandbox hasn't been started, or the file is gitignored (expected). + +**Solutions:** +1. Run `npx ampx sandbox` to generate the file locally +2. For CI/CD, `npx ampx pipeline-deploy` generates it during build +3. Ensure your `.gitignore` includes `amplify_outputs.json` — it should NOT be committed + +### Next.js SSR/SSG Issues + +**Symptoms:** `Amplify.configure()` errors in server components, hydration mismatches. + +**Solutions:** +1. Call `Amplify.configure()` in a client-side wrapper component (`'use client'`) +2. For server-side data access, use `generateServerClientUsingCookies()` from `aws-amplify/adapter-nextjs` +3. Never import `aws-amplify` in server components directly + +## Resources + +- [Amplify Gen2 Documentation](https://docs.amplify.aws/react/) +- [Amplify Docs for LLMs](https://docs.amplify.aws/ai/llms.txt) +- [Getting Started Guide](https://docs.amplify.aws/react/start/) +- [Quickstart Tutorial](https://docs.amplify.aws/react/start/quickstart/) +- [Account Setup](https://docs.amplify.aws/react/start/account-setup/) +- [How Amplify Works](https://docs.amplify.aws/react/how-amplify-works/) +- [Build a Backend](https://docs.amplify.aws/react/build-a-backend/) +- [Deploy and Host](https://docs.amplify.aws/react/deploy-and-host/) +- [CLI Reference](https://docs.amplify.aws/react/reference/cli-commands/) +- [Project Structure Reference](https://docs.amplify.aws/react/reference/project-structure/) +- [Amplify UI Components](https://ui.docs.amplify.aws/) +- [Amplify GitHub](https://github.com/aws-amplify) + +> All documentation links use `react` as the default platform slug. Replace `/react/` with `/nextjs/`, `/vue/`, `/angular/`, `/react-native/`, `/flutter/`, `/swift/`, or `/android/` for other frameworks. + +--- -**Do NOT load phase steering files directly.** The orchestrator (`amplify-workflow.md`) determines which phases apply and loads them in sequence. Phase files (`phase1-backend.md`, `phase2-sandbox.md`, `phase3-frontend.md`, `phase4-production.md`) are internal and should only be loaded when the orchestrator or a previous phase instructs you to. +This power integrates with [`awslabs.aws-documentation-mcp-server@latest`](https://github.com/awslabs/mcp) for documentation search and retrieval. diff --git a/aws-amplify/steering/advanced-features.md b/aws-amplify/steering/advanced-features.md new file mode 100644 index 0000000..7aebc9a --- /dev/null +++ b/aws-amplify/steering/advanced-features.md @@ -0,0 +1,274 @@ +# Advanced Features + +## Geo (Location) — Backend + Frontend + +Add map display and location search using CDK constructs in +`amplify/backend.ts`: + +```typescript +import { defineBackend } from '@aws-amplify/backend'; +import * as geo from 'aws-cdk-lib/aws-location'; + +const backend = defineBackend({ auth }); +const geoStack = backend.createStack('GeoStack'); + +const placeIndex = new geo.CfnPlaceIndex(geoStack, 'PlaceIndex', { + dataSource: 'Esri', + indexName: 'myPlaceIndex', +}); + +const map = new geo.CfnMap(geoStack, 'Map', { + mapName: 'myMap', + configuration: { style: 'VectorEsriNavigation' }, +}); + +backend.addOutput({ + geo: { + aws_region: geoStack.region, + maps: { items: { [map.mapName]: { style: 'VectorEsriNavigation' } } }, + search_indices: { items: [placeIndex.indexName] }, + }, +}); +``` + +Grant authenticated users access via IAM policy on the geo resources. + +**Frontend:** Install `@aws-amplify/geo` and `maplibre-gl-js-amplify`. +Use `Amplify.Geo.searchByText()` for search and `AmplifyMapLibreRequest` +for rendering maps. See +[AWS Amplify Geo docs](https://docs.amplify.aws/react/build-a-backend/add-aws-services/geo/) +for full client setup. + +## PubSub — Backend + Frontend + +Real-time messaging via AWS IoT Core. Configure an IoT endpoint and +attach an IAM policy for authenticated users in `amplify/backend.ts`: + +```typescript +import * as iot from 'aws-cdk-lib/aws-iot'; +import * as iam from 'aws-cdk-lib/aws-iam'; + +const pubsubStack = backend.createStack('PubSubStack'); + +backend.auth.resources.authenticatedUserIamRole.addToPrincipalPolicy( + new iam.PolicyStatement({ + actions: ['iot:Connect', 'iot:Subscribe', 'iot:Publish', 'iot:Receive'], + resources: ['*'], + }) +); + +backend.addOutput({ + custom: { iotEndpoint: 'your-iot-endpoint.iot.region.amazonaws.com' }, +}); +``` + +**Frontend** — subscribe and publish: + +```typescript +import { PubSub } from '@aws-amplify/pubsub'; + +const sub = PubSub.subscribe({ topics: ['myTopic'] }).subscribe({ + next: (data) => console.log('Message:', data), + error: (err) => console.error(err), +}); + +await PubSub.publish({ topics: ['myTopic'], message: { msg: 'hello' } }); +sub.unsubscribe(); // MUST unsubscribe to prevent leaks +``` + +When using subscriptions in React, **MUST** wrap in `useEffect` and return +cleanup function to call `.unsubscribe()`. + +Retrieve the IoT endpoint programmatically: +`aws iot describe-endpoint --endpoint-type iot:Data-ATS`. +See [AWS Amplify PubSub docs](https://docs.amplify.aws/react/build-a-backend/add-aws-services/pubsub/) +for connection configuration. + +## Custom CDK Stacks — Backend Only + +Create additional CloudFormation stacks for resources not natively +supported by Amplify: + +```typescript +const backend = defineBackend({ auth, data }); +const customStack = backend.createStack('AnalyticsStack'); + +// Use any CDK construct in the custom stack +import * as sns from 'aws-cdk-lib/aws-sns'; +const topic = new sns.Topic(customStack, 'NotificationTopic'); + +// Access Amplify resources from custom stack +const userPool = backend.auth.resources.userPool; +``` + +Stack names **MUST** be unique within the backend — duplicate names cause +deployment failures. Use descriptive names like `'EmailStack'`, +`'AnalyticsStack'`. + +## Backend Overrides — Backend Only + +Access and modify underlying CloudFormation resources when Amplify's +high-level API does not expose a needed property: + +```typescript +const backend = defineBackend({ auth, data }); + +// Auth override: Access the underlying CFN user pool resource +const cfnUserPool = backend.auth.resources.cfnResources.cfnUserPool; +cfnUserPool.policies = { + passwordPolicy: { + minimumLength: 12, + requireLowercase: true, + requireUppercase: true, + requireNumbers: true, + requireSymbols: true, + }, +}; + +// DynamoDB override: Access underlying CFN resources +const { cfnResources } = backend.data.resources; + +// Enable point-in-time recovery +cfnResources.amplifyDynamoDbTables['Todo'].pointInTimeRecoveryEnabled = true; + +// Change billing mode +cfnResources.amplifyDynamoDbTables['Todo'].billingMode = 'PAY_PER_REQUEST'; + +// Set TTL +cfnResources.amplifyDynamoDbTables['Todo'].timeToLiveAttribute = { + attributeName: 'ttl', + enabled: true, +}; +``` + +The entry point for DynamoDB table overrides is +`backend.data.resources.cfnResources.amplifyDynamoDbTables['ModelName']`, +which exposes L1 CFN properties directly. + +To add a **Global Secondary Index**, use `.secondaryIndexes()` in the +schema definition (the Amplify-native approach) rather than CDK overrides: + +```typescript +const schema = a.schema({ + Todo: a.model({ + content: a.string(), + status: a.string(), + createdAt: a.datetime(), + }) + .secondaryIndexes(index => [ + index('status').sortKeys(['createdAt']), + ]) + .authorization(allow => [allow.owner()]), +}); +``` + +See +[AWS Amplify Override docs](https://docs.amplify.aws/react/build-a-backend/add-aws-services/overriding-resources/) +for the full override API. + +## Custom Outputs — Backend Only + +Expose custom resource values to the frontend via `amplify_outputs.json`: + +```typescript +backend.addOutput({ + custom: { + analyticsTopicArn: topic.topicArn, + apiEndpoint: 'https://api.example.com', + }, +}); +``` + +Values appear under the `custom` key in `amplify_outputs.json`. Frontend +reads them from the Amplify configuration after `Amplify.configure()`. + +## Face Liveness — Backend + Frontend + +Verify user identity with Amazon Rekognition Face Liveness. Add IAM +permissions in `amplify/backend.ts`: + +```typescript +import * as iam from 'aws-cdk-lib/aws-iam'; + +backend.auth.resources.authenticatedUserIamRole.addToPrincipalPolicy( + new iam.PolicyStatement({ + actions: [ + 'rekognition:CreateFaceLivenessSession', + 'rekognition:StartFaceLivenessSession', + 'rekognition:GetFaceLivenessSessionResults', + ], + resources: ['*'], + }) +); +``` + +**Frontend (React):** + +```bash +npm install @aws-amplify/ui-react-liveness +``` + +```tsx +import { FaceLivenessDetector } from '@aws-amplify/ui-react-liveness'; + + { /* fetch results */ }} +/> +``` + +Create the session server-side via Rekognition SDK or a Lambda function, +then pass the `sessionId` to the component. See +[AWS Amplify Liveness docs](https://ui.docs.amplify.aws/react/connected-components/liveness) +for the full integration guide. + +**Frontend (Swift — iOS 14+):** + +Requires the `amplify-ui-swift-liveness` package and camera permission +(`NSCameraUsageDescription` in `Info.plist`). Add the package via Xcode SPM: +`https://github.com/aws-amplify/amplify-ui-swift-liveness`. + +The backend must include a Cognito Identity Pool with an IAM role that +grants `rekognition:StartFaceLivenessSession` and +`rekognition:GetFaceLivenessSessionResults`. + +See [Swift Liveness docs](https://ui.docs.amplify.aws/swift/connected-components/liveness) +for the full SwiftUI integration guide. + +**Frontend (Android — API 24+):** + +Add the dependency to `app/build.gradle.kts`: + +```kotlin +dependencies { + implementation("com.amplifyframework.ui:liveness:1.+") +} +``` + +Requires Jetpack Compose. The backend must include a Cognito Identity Pool +with an IAM role that grants `rekognition:StartFaceLivenessSession` and +`rekognition:GetFaceLivenessSessionResults`. + +See [Android Liveness docs](https://ui.docs.amplify.aws/android/connected-components/liveness) +for the full Compose integration guide. + +## Pitfalls + +- **Duplicate stack names:** `backend.createStack()` names **MUST** be + unique across the entire backend — reusing a name silently overwrites. +- **Missing IAM permissions:** Geo, PubSub, and Face Liveness all require + explicit IAM policies — Amplify does not auto-grant access to these + services. +- **Geo CDK setup:** Geo (maps, place search, geofencing) requires CDK + constructs — there is no `defineGeo()` in Amplify Gen2. Use + `aws-cdk-lib/aws-location` directly as shown above. +- **PubSub endpoint:** You **MUST** configure the correct IoT endpoint for + your region; using the wrong endpoint type causes silent connection + failures. + +## Links + +- [Add AWS Services](https://docs.amplify.aws/react/build-a-backend/add-aws-services/) +- [Custom Resources](https://docs.amplify.aws/react/build-a-backend/add-aws-services/custom-resources/) +- [Overriding Resources](https://docs.amplify.aws/react/build-a-backend/add-aws-services/overriding-resources/) diff --git a/aws-amplify/steering/ai.md b/aws-amplify/steering/ai.md new file mode 100644 index 0000000..015a98e --- /dev/null +++ b/aws-amplify/steering/ai.md @@ -0,0 +1,211 @@ +# AI + +## Model Selection + +Use `a.ai.model()` to select an AI model in both `a.conversation()` and `a.generation()` routes. Pass a human-readable model name string: + +```typescript +aiModel: a.ai.model('Claude 3.5 Sonnet v2') +``` + +`a.ai.model()` accepts any supported model name: +- **Anthropic**: `'Claude 3 Haiku'`, `'Claude 3 Sonnet'`, `'Claude 3 Opus'`, `'Claude 3.5 Haiku'`, `'Claude 3.5 Sonnet'`, `'Claude 3.5 Sonnet v2'`, `'Claude 3.7 Sonnet'`, `'Claude Opus 4'`, `'Claude Sonnet 4'`, `'Claude Haiku 4.5'`, `'Claude Sonnet 4.5'`, `'Claude Opus 4.5'`, `'Claude Sonnet 4.6'`, `'Claude Opus 4.6'` +- **Amazon**: `'Amazon Nova Pro'`, `'Amazon Nova Lite'`, `'Amazon Nova Micro'` +- **Meta**: `'Llama 3.1 405B Instruct'`, `'Llama 3.1 70B Instruct'`, `'Llama 3.1 8B Instruct'` +- **Cohere**: `'Cohere Command R+'`, `'Cohere Command R'` +- **Mistral**: `'Mistral Large 2'`, `'Mistral Large'`, `'Mistral Small'` + +For models not in the supported list, use the raw escape hatch: `aiModel: { resourcePath: '' }`. + +Availability depends on the AWS region and Bedrock model access enablement. + +> **Note:** `a.generation()` routes only support Anthropic (Claude) models. `a.conversation()` routes work with any supported model. + +## Backend: Conversation Routes + +Define multi-turn conversation routes in your data schema using +`a.conversation()`: + +```typescript +// amplify/data/resource.ts +import { a, type ClientSchema } from '@aws-amplify/backend'; + +const schema = a.schema({ + chat: a.conversation({ + aiModel: a.ai.model('Claude 3.5 Sonnet v2'), + systemPrompt: 'You are a helpful assistant.', + }) + .authorization(allow => allow.owner()), +}); +``` + +## Backend: Generation Routes + +Use `a.generation()` for single-turn (stateless) inference. + +> **MUST:** Only Anthropic (Claude) models support `a.generation()` routes. Non-Anthropic models (Amazon Nova, Meta Llama, Cohere, Mistral) work with `a.conversation()` only. + +```typescript +const schema = a.schema({ + summarize: a.generation({ + aiModel: a.ai.model('Claude 3.5 Sonnet v2'), + systemPrompt: 'Summarize the provided text concisely.', + inferenceConfiguration: { maxTokens: 500, temperature: 0.3 }, + }) + .arguments({ text: a.string().required() }) + .returns(a.customType({ summary: a.string() })) + .authorization(allow => allow.authenticated()), +}); +``` + +**CRITICAL — Authorization Constraints:** +- **Conversation routes** (`a.conversation()`) **MUST** use `allow.owner()` authorization — `allow.authenticated()` and other non-owner strategies throw a TypeError at CDK assembly time (before deployment even begins). +- **Generation routes** (`a.generation()`) **MUST** use non-owner authorization (`allow.authenticated()`, `allow.guest()`, `allow.group()`, or `allow.publicApiKey()`) — `allow.owner()` throws a TypeError at CDK assembly time (before deployment even begins). + +These constraints are asymmetric and frequently confused. Getting them wrong +causes the CDK synthesis to fail with a non-obvious TypeError. + +### Backend Integration + +AI conversation and generation routes are part of your data schema. Import into `amplify/backend.ts`: + +```typescript +import { defineBackend } from '@aws-amplify/backend'; +import { data } from './data/resource'; + +defineBackend({ data }); // AI routes live inside the data schema +``` + +## Backend: AI Tools + +Attach Lambda functions as tools to conversation routes so the AI model +can invoke them: + +```typescript +import { myToolFunc } from '../functions/my-tool/resource'; + +const schema = a.schema({ + chat: a.conversation({ + aiModel: a.ai.model('Claude 3.5 Sonnet v2'), + systemPrompt: 'You are a helpful assistant with tool access.', + tools: [ + { + name: 'getWeather', + query: a.ref('getWeather'), + description: 'Get current weather for a city', + }, + ], + }) + .authorization(allow => allow.owner()), + + getWeather: a.query() + .arguments({ city: a.string().required() }) + .returns(a.customType({ temp: a.float(), condition: a.string() })) + .handler(a.handler.function(myToolFunc)) + .authorization(allow => allow.authenticated()), +}); +``` + +Define the tool function with `defineFunction` (see +[functions-and-api.md](functions-and-api.md)). + +## Frontend: React AI UI + +Install the AI UI package: + +```bash +npm install @aws-amplify/ui-react-ai +``` + +Set up hooks and render the conversation component: + +```tsx +import { generateClient } from 'aws-amplify/api'; +import { createAIHooks, AIConversation } from '@aws-amplify/ui-react-ai'; +import type { Schema } from '../amplify/data/resource'; + +const client = generateClient(); +const { useAIConversation } = createAIHooks(client); + +export default function Chat() { + const [ + { data: { messages }, isLoading }, + handleSendMessage, + ] = useAIConversation('chat'); + + return ( + + ); +} +``` + +## Frontend: Manual Client + +For programmatic access without the pre-built UI: + +```typescript +const client = generateClient(); + +// List conversations +const { data: conversations } = await client.conversations.chat.list(); + +// Create a new conversation +const { data: conversation } = await client.conversations.chat.create(); + +// Send a message +const { data: message } = await conversation.sendMessage({ + content: [{ text: 'Hello!' }], +}); +``` + +Pagination: use `limit` and `nextToken` parameters on `.list()`. + +## Streaming + +Subscribe to streaming responses for real-time token delivery: + +In React, **MUST** wrap in `useEffect` and return the cleanup function: + +```tsx +useEffect(() => { + const sub = conversation.onStreamEvent({ + next: (event) => console.log(event), + error: (err) => console.error(err), + }); + return () => sub.unsubscribe(); +}, [conversation]); +``` + +> **UI note:** Amplify AI Kit provides pre-built UI components for React and +> React Native only. Flutter, Swift, and Android apps can invoke AI +> conversation/generation routes via manual GraphQL client calls — see +> [data-mobile.md](data-mobile.md) patterns for the equivalent approach. + +## Pitfalls + +- **Conversation auth MUST be `allow.owner()`:** Using + `allow.authenticated()` or any other non-owner strategy on + `a.conversation()` throws a TypeError at CDK assembly time. +- **Generation auth MUST NOT be `allow.owner()`:** Using + `allow.owner()` on `a.generation()` throws a TypeError at CDK assembly + time. Use `allow.authenticated()`, `allow.guest()`, or `allow.group()`. +- **Missing AI route in data schema:** The conversation or generation + route **MUST** be defined in your `a.schema()` — without it, the + frontend client has no AI endpoint to call. +- **Model availability:** Not all Bedrock models are enabled by default — + you **MUST** enable model access in the AWS console (Bedrock → Model + access) before using a model in `a.ai.model()`. +- **Message content structure:** Both `sendMessage('Hello')` (string) and + `sendMessage({ content: [{ text: 'Hello' }] })` (object) are valid. Use + the object form when sending images or tool results. + +## Links + +- [AI Overview](https://docs.amplify.aws/react/ai/) +- [Set Up AI](https://docs.amplify.aws/react/ai/set-up-ai/) +- [Conversation UI](https://docs.amplify.aws/react/frontend/ai/conversation/) +- [Generation UI](https://docs.amplify.aws/react/frontend/ai/generation/) diff --git a/aws-amplify/steering/amplify-workflow.md b/aws-amplify/steering/amplify-workflow.md deleted file mode 100644 index 0f32d9c..0000000 --- a/aws-amplify/steering/amplify-workflow.md +++ /dev/null @@ -1,184 +0,0 @@ -# Amplify Workflow - -Orchestrated workflow for AWS Amplify Gen 2 development. - -## When to Use This Workflow - -Use for any Amplify Gen 2 work: -- Building a new full-stack application -- Adding features to an existing backend -- Connecting frontend to backend -- Deploying to sandbox or production - -The workflow determines which phases apply based on your request. - ---- - -## Step 1: Validate Prerequisites - -Run these checks before proceeding: - -1. **Node.js 18.x or later** - - ```bash - node --version - ``` - -2. **npm available** - - ```bash - npm --version - ``` - -3. **AWS credentials configured** (CRITICAL) - - ```bash - AWS_PAGER="" aws sts get-caller-identity - ``` - -If the AWS credentials check fails, **STOP** and present this message to the user: - -``` -## AWS Credentials Required - -I can't proceed without AWS credentials configured. Please set up your credentials first: - -**Setup Guide:** https://docs.amplify.aws/react/start/account-setup/ - -**Quick options:** -- Run `aws configure` to set up access keys -- Run `aws sso login` if using AWS IAM Identity Center - -Once your credentials are configured, **come back and start a new conversation** to continue building with Amplify. -``` - -**Do NOT proceed with Amplify work until credentials are configured.** The user must restart the conversation after setting up credentials. - ---- - -## Step 2: Understand the Project - -Once all prerequisites pass: - -1. Read all necessary project files (e.g., `amplify/`, `package.json`, existing code) to understand the current state -2. If unsure about Amplify capabilities or best practices, use documentation tools to search and read AWS Amplify docs - -Do this BEFORE proposing a plan. - ---- - -## Step 3: Determine Applicable Phases - -Based on the user's request and project state, determine which phases apply: - -| Phase | Applies when | Steering file | -| ------------------ | -------------------------------------------------------- | -------------------- | -| 1: Backend | User needs to create or modify Amplify backend resources | `phase1-backend.md` | -| 2: Sandbox | Deploy to sandbox for testing | `phase2-sandbox.md` | -| 3: Frontend & Test | Frontend needs to connect to Amplify backend | `phase3-frontend.md` | -| 4: Production | Deploy to production | `phase4-production.md` | - -Common patterns: -- **New full-stack app:** 1 -> 2 -> 3 -> 4 -- **Backend only (no frontend):** 1 -> 2 -- **Add feature to existing backend:** 1 -> 2 -- **Redeploy after changes:** 2 only -- **Connect existing frontend:** 3 only -- **Deploy to production:** 4 only - -**IMPORTANT: Only include phases that the user actually needs.** If the user asks for backend work only (e.g., "add auth", "create a data model", "add storage"), do NOT include Phase 3 (Frontend & Test). Frontend phases should only be included when the user explicitly asks for frontend work, a full-stack app, or to connect a frontend to Amplify. - ---- - -## Step 4: Present Plan and Confirm - -Present to the user: - -``` -## Plan - -### What I understood -- [Brief summary of what the user wants] - -### Features -[list features if applicable] - -### Framework -[framework if known] - -### Phases I'll execute -1. [Phase name] - [one-line description] -> SOP: [sop-name] -2. [Phase name] - [one-line description] -> SOP: [sop-name] -... -(Include SOP name for phases 1 and 3. Phases 2 and 4 use the amplify-deployment-guide SOP.) - -Ready to get started? -``` - -**WAIT for user confirmation before proceeding.** - -**Once the user approves the plan, you MUST stick to it. Do not deviate from the planned phases or SOPs unless the user explicitly asks for changes.** - ---- - -## Step 5: Execute Phases - -After the user confirms the plan, read **ONLY the first phase's steering file** using readSteering: - -``` -Call action "readSteering" with powerName="aws-amplify", steeringFile="" -``` - -Where `` is the steering file for the first phase in the plan (from the table in Step 3). - -**Do NOT read any other phase steering files yet.** - -### Resuming After a Phase Completes - -When a phase completes, it will summarize what it did and stop. The orchestrator takes over: - -1. Tell the user which phase just finished -2. If there are more phases in the plan, ask: - -``` -[Phase name] is complete. Ready to proceed to [next phase name]? -``` - -3. **WAIT for the user to confirm before proceeding.** -4. After the user confirms, read the next phase's steering file: - -``` -Call action "readSteering" with powerName="aws-amplify", steeringFile="" -``` - -**If there are no more phases in the plan, the workflow is complete.** Tell the user all phases are done. - -Do NOT re-run prerequisites or re-present the plan. Simply dispatch the next phase. - ---- - -## Critical Rules - -1. **Always follow SOPs completely** - Do not improvise or skip steps -2. **Never use Gen 1 patterns** - This power is for Amplify Gen 2 only (TypeScript code-first, `defineAuth`/`defineData`/`defineStorage`/`defineFunction`) -3. **One phase at a time** - Read only one phase steering file at a time. Do not read ahead. -4. **Wait for confirmation between phases** - After each phase completes, ask the user to confirm before dispatching the next phase. Do not proceed until the user confirms. -5. **If you encounter an error or get sidetracked:** - - Fix the immediate issue - - Return to the SOP and continue from where you left off - - Do NOT abandon the SOP or start improvising -6. **If you lose track of where you were in the SOP:** - - Use the SOP retrieval tool to get the SOP again - - Identify which step you completed last - - Continue from the next step - ---- - -## Troubleshooting - -If issues occur during any phase: -1. Check the SOP's troubleshooting section first -2. Use documentation tools to search AWS Amplify docs for the error message -3. Read the relevant documentation page - -**After resolving the issue, immediately return to the SOP and continue from where you left off. Do not abandon the workflow.** diff --git a/aws-amplify/steering/auth-backend.md b/aws-amplify/steering/auth-backend.md new file mode 100644 index 0000000..12431a3 --- /dev/null +++ b/aws-amplify/steering/auth-backend.md @@ -0,0 +1,233 @@ +# Auth — Backend + +## Basic Auth Setup + +Define authentication in `amplify/auth/resource.ts`: + +```typescript +import { defineAuth } from '@aws-amplify/backend'; + +export const auth = defineAuth({ + loginWith: { + email: true, + // phone: true, // SMS-based login + }, + userAttributes: { + preferredUsername: { required: false }, + }, +}); +``` + +Import into `amplify/backend.ts`: + +```typescript +import { defineBackend } from '@aws-amplify/backend'; +import { auth } from './auth/resource'; +defineBackend({ auth }); +``` + +## MFA Configuration + +```typescript +export const auth = defineAuth({ + loginWith: { email: true }, + multifactor: { + mode: 'REQUIRED', // or 'OPTIONAL' + totp: true, + sms: true, + email: true, + }, +}); +``` + +Set `mode: 'REQUIRED'` to enforce MFA for all users. `'OPTIONAL'` lets +users enable it themselves. + +> **Frontend impact:** When MFA is enabled, the Authenticator component handles all MFA steps automatically. For custom UI, see auth-web.md for signInStep handling. + +## Passwordless Authentication + +Passwordless login methods can coexist with traditional password-based auth. + +**Email OTP:** + +```typescript +export const auth = defineAuth({ + loginWith: { + email: { + otpLogin: true, + }, + }, +}); +``` + +**SMS OTP:** + +```typescript +export const auth = defineAuth({ + loginWith: { + phone: { + otpLogin: true, + }, + }, +}); +``` + +**WebAuthn / Passkeys:** + +```typescript +export const auth = defineAuth({ + loginWith: { + webAuthn: true, + }, +}); +``` + +These passwordless methods can be combined with each other and with +password-based login in the same `defineAuth` configuration. + +## Social Login + +You **MUST** use `secret()` for OAuth client secrets — never hardcode +credentials. + +```typescript +import { defineAuth, secret } from '@aws-amplify/backend'; + +export const auth = defineAuth({ + loginWith: { + email: true, + externalProviders: { + google: { + clientId: secret('GOOGLE_CLIENT_ID'), + clientSecret: secret('GOOGLE_CLIENT_SECRET'), + scopes: ['email', 'profile', 'openid'], + attributeMapping: { + email: 'email', // values are strings, NOT objects + fullname: 'name', + }, + }, + facebook: { clientId: secret('FB_CLIENT_ID'), clientSecret: secret('FB_CLIENT_SECRET') }, + signInWithApple: { + clientId: secret('APPLE_CLIENT_ID'), + teamId: secret('APPLE_TEAM_ID'), + keyId: secret('APPLE_KEY_ID'), + privateKey: secret('APPLE_PRIVATE_KEY'), + }, + loginWithAmazon: { clientId: secret('AMAZON_CLIENT_ID'), clientSecret: secret('AMAZON_CLIENT_SECRET') }, + callbackUrls: ['http://localhost:3000/', 'https://myapp.com/'], + logoutUrls: ['http://localhost:3000/', 'https://myapp.com/'], + }, + }, +}); +``` + +Set secrets via CLI: `echo "" | npx ampx sandbox secret set GOOGLE_CLIENT_ID`. +For provider-specific OAuth setup guides, **SHOULD** consult AWS +documentation via available tools; when unavailable, **MUST** use web +search or AWS CLI. + +## SAML / OIDC (Enterprise) + +OIDC providers are configured directly in `externalProviders`: + +```typescript +externalProviders: { + oidc: [{ + name: 'MyOIDC', + clientId: secret('OIDC_CLIENT_ID'), + clientSecret: secret('OIDC_CLIENT_SECRET'), + issuerUrl: 'https://idp.example.com', + attributeMapping: { email: 'email' }, + }], + callbackUrls: ['http://localhost:3000/'], + logoutUrls: ['http://localhost:3000/'], +} +``` +**SAML** is NOT supported in `defineAuth` — the `ExternalProviderSpecificFactoryProps` type has no `saml` property. The lower-level `auth-construct` package supports SAML, but it was never wired up to the high-level API. Use CDK escape hatches via `backend.auth.resources` to configure SAML providers: + +```typescript +// In backend.ts — SAML requires CDK-level configuration +const { cfnUserPool } = backend.auth.resources.cfnResources; +// Configure SAML identity provider via CfnUserPoolIdentityProvider +``` + +Consult AWS documentation for `CfnUserPoolIdentityProvider` SAML configuration properties. + +## Cognito Triggers + +```typescript +import { defineAuth } from '@aws-amplify/backend'; +import { preSignUp } from './pre-sign-up/resource'; +import { postConfirmation } from './post-confirmation/resource'; + +export const auth = defineAuth({ + loginWith: { email: true }, + triggers: { + preSignUp, + postConfirmation, + // Also: preAuthentication, postAuthentication, + // createAuthChallenge, defineAuthChallenge, verifyAuthChallengeResponse, + // preTokenGeneration, customMessage, userMigration + }, +}); +``` + +Define each trigger with `defineFunction`: + +```typescript +// amplify/auth/pre-sign-up/resource.ts +import { defineFunction } from '@aws-amplify/backend'; +export const preSignUp = defineFunction({ name: 'pre-sign-up' }); +``` + +### Guest (Unauthenticated) Access + +Guest access is **enabled by default** in Amplify Gen2 — the Cognito Identity Pool is created with `allowUnauthenticatedIdentities: true` automatically. + +To use guest access in your data models, set `defaultAuthorizationMode` to `'iam'`: + +```typescript +export const data = defineData({ + schema, + authorizationModes: { + defaultAuthorizationMode: 'iam', + }, +}); +``` + +To **disable** guest access, use a CDK override in `backend.ts`: + +```typescript +const { cfnIdentityPool } = backend.auth.resources.cfnResources; +cfnIdentityPool.allowUnauthenticatedIdentities = false; +``` + +## Pitfalls + +- **Trigger not registered (silent no-op):** Defining a trigger function + with `defineFunction` but NOT adding it to `triggers: {}` in `defineAuth` + causes a **silent no-op** — the function deploys but never fires. You + **MUST** both define AND register: `triggers: { preSignUp, postConfirmation }`. +- **Hardcoded secrets:** Using string literals instead of `secret()` for + OAuth credentials exposes them in source control. +- **Missing scopes:** Social providers default to minimal scopes — add + `'email'`, `'profile'` explicitly or user attributes won't populate. +- **Google attribute mapping:** The Google claim `name` maps to Cognito + `fullname` (NOT `name`). The `attributeMapping` values are plain strings, + NOT objects: `{ email: 'email', fullname: 'name' }`. +- **MFA method mismatch:** Enabling `sms: true` in MFA requires a phone + number attribute on the user pool — add `phone_number` to user attributes. + Similarly, `email: true` in MFA requires an email attribute on the user pool. +- **Secrets in CI/CD:** For branch environments, use: + `npx ampx secret set KEY_NAME --branch main --app-id APP_ID`. + +## Links + +- [Auth Overview](https://docs.amplify.aws/react/build-a-backend/auth/) +- [Set Up Auth](https://docs.amplify.aws/react/build-a-backend/auth/set-up-auth/) +- [External Identity Providers](https://docs.amplify.aws/react/build-a-backend/auth/concepts/external-identity-providers/) +- [Multi-Factor Authentication](https://docs.amplify.aws/react/build-a-backend/auth/concepts/multi-factor-authentication/) +- [Passwordless Authentication](https://docs.amplify.aws/react/build-a-backend/auth/concepts/passwordless/) +- [User Attributes](https://docs.amplify.aws/react/build-a-backend/auth/concepts/user-attributes/) +- [Grant Access to Auth Resources](https://docs.amplify.aws/react/build-a-backend/auth/grant-access-to-auth-resources/) diff --git a/aws-amplify/steering/auth-mobile.md b/aws-amplify/steering/auth-mobile.md new file mode 100644 index 0000000..59913f9 --- /dev/null +++ b/aws-amplify/steering/auth-mobile.md @@ -0,0 +1,481 @@ +# Auth — Mobile + +> **Backend required:** Auth must be defined in `amplify/auth/resource.ts` +> using `defineAuth` — see [auth-backend.md](auth-backend.md). + +## Authenticator Component (Recommended) + +All three mobile platforms provide a drop-in **Authenticator** component that +handles sign-in, sign-up, MFA, social login, passwordless, password reset, and +all intermediate auth states automatically. **Use it unless you need a fully +custom UI.** Zero manual `signInStep` handling is required. + +> **Passwordless:** The Authenticator component handles passwordless flows (email OTP, SMS OTP, and WebAuthn/passkey) automatically when configured in `defineAuth`. No custom UI code needed for passwordless authentication. On Swift/Android, use `authenticationFlow: .userChoice(preferredAuthFactor: .webAuthn)` to default to passkeys. Custom OTP/passkey flows require additional challenge handling. + +### Flutter + +**Dependencies** — add to `pubspec.yaml`: + +```yaml +dependencies: + amplify_flutter: ^2.0.0 + amplify_auth_cognito: ^2.0.0 + amplify_authenticator: ^2.0.0 +``` + +**Usage** — wrap your `MaterialApp` and set its `builder`: + +```dart +import 'package:amplify_auth_cognito/amplify_auth_cognito.dart'; +import 'package:amplify_authenticator/amplify_authenticator.dart'; +import 'package:amplify_flutter/amplify_flutter.dart'; +import 'package:flutter/material.dart'; + +import 'amplifyconfiguration.dart'; + +class MyApp extends StatefulWidget { + const MyApp({super.key}); + @override + State createState() => _MyAppState(); +} + +class _MyAppState extends State { + @override + void initState() { + super.initState(); + _configureAmplify(); + } + + void _configureAmplify() async { + try { + await Amplify.addPlugin(AmplifyAuthCognito()); + await Amplify.configure(amplifyconfig); + } on Exception catch (e) { + safePrint('Error configuring Amplify: $e'); + } + } + + @override + Widget build(BuildContext context) { + return Authenticator( + child: MaterialApp( + builder: Authenticator.builder(), + home: const Scaffold( + body: Center(child: Text('You are logged in!')), + ), + ), + ); + } +} +``` + +### Swift (Apple platforms) + +> Supports iOS 13+, macOS 12+, tvOS 13+, watchOS 9+, visionOS 1+ (preview). +> Passkeys require iOS 17.4+, macOS 13.5+, or visionOS 1.0+. + +**Dependencies** — add both SPM packages in Xcode (**File > Add Packages…**): + +| Package | URL | Libraries | +|---|---|---| +| Amplify Library for Swift | `https://github.com/aws-amplify/amplify-swift` | `Amplify`, `AWSCognitoAuthPlugin` | +| Amplify UI Swift Authenticator | `https://github.com/aws-amplify/amplify-ui-swift-authenticator` | `Authenticator` | + +> **SPM versioning:** For both packages, select **"Up to Next Major Version"** in Xcode's dependency rule. Do NOT pin to a specific branch (e.g., `main`) — use "Up to Next Major Version" to get compatible updates automatically. + +**Usage** — SwiftUI entry point: + +```swift +import Amplify +import Authenticator +import AWSCognitoAuthPlugin +import SwiftUI + +@main +struct MyApp: App { + init() { + do { + try Amplify.add(plugin: AWSCognitoAuthPlugin()) + try Amplify.configure() + } catch { + print("Unable to configure Amplify \(error)") + } + } + + var body: some Scene { + WindowGroup { + Authenticator { state in + VStack { + Text("Hello, \(state.user.username)") + Button("Sign out") { + Task { await state.signOut() } + } + } + } + } + } +} +``` + +**Passwordless / user-choice flow:** + +```swift +Authenticator(authenticationFlow: .userChoice( + preferredAuthFactor: .webAuthn +)) { state in + Text("Welcome \(state.user.username)!") +} +``` + +### Android (Kotlin) + +**Dependencies** — add to your app's `build.gradle`: + +```groovy +// Enable Jetpack Compose +android { + compileOptions { + coreLibraryDesugaringEnabled true + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + buildFeatures { compose true } + composeOptions { kotlinCompilerExtensionVersion '1.5.3' } +} + +dependencies { + implementation 'com.amplifyframework.ui:authenticator:1.4.0' + coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' +} +``` + +`INTERNET` permission is required in `AndroidManifest.xml`: +```xml + +``` + +**Configure** — in your `Application.onCreate()`: + +```kotlin +try { + Amplify.addPlugin(AWSCognitoAuthPlugin()) + Amplify.configure(applicationContext) +} catch (error: AmplifyException) { + Log.e("MyApp", "Could not initialize Amplify", error) +} +``` + +**Usage** — Jetpack Compose: + +```kotlin +import com.amplifyframework.ui.authenticator.ui.Authenticator +import com.amplifyframework.ui.authenticator.SignedInState + +class MainActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContent { + Authenticator { state -> + Column { + Text("Signed in as ${state.user.username}") + val scope = rememberCoroutineScope() + Button(onClick = { scope.launch { state.signOut() } }) { + Text("Sign Out") + } + } + } + } + } +} +``` + +**Passwordless / user-choice flow:** + +```kotlin +val authenticatorState = rememberAuthenticatorState( + authenticationFlow = AuthenticationFlow.UserChoice( + preferredAuthFactor = AuthFactor.WebAuthn + ) +) +Authenticator(state = authenticatorState) { state -> + Text("Welcome ${state.user.username}!") +} +``` + +## Custom UI + +Use the low-level Auth APIs when you need full control over the UI. Each +platform returns a `nextStep` from `signIn` / `signUp` — switch on it and +call `confirmSignIn` as needed. The Authenticator handles all these steps +automatically; the list below is for reference when building custom flows. + +### Flutter + +```dart +import 'package:amplify_flutter/amplify_flutter.dart'; +``` + +**Sign in:** +```dart +final result = await Amplify.Auth.signIn( + username: username, + password: password, +); +if (result.isSignedIn) { + safePrint('Sign in complete'); +} else { + // Handle result.nextStep.signInStep — e.g.: + // confirmSignInWithSmsMfaCode → prompt for SMS code, call confirmSignIn + // confirmSignInWithTotpMfaCode → prompt for TOTP code, call confirmSignIn + // confirmSignInWithNewPassword → prompt new password, call confirmSignIn + // done → authenticated +} +``` + +**Confirm sign-in** (for MFA / challenge steps): +```dart +final result = await Amplify.Auth.confirmSignIn( + confirmationValue: codeFromUser, +); +``` + +**Sign up:** +```dart +final result = await Amplify.Auth.signUp( + username: username, + password: password, + options: SignUpOptions( + userAttributes: {AuthUserAttributeKey.email: email}, + ), +); +if (result.nextStep.signUpStep == AuthSignUpStep.confirmSignUp) { + // Prompt for confirmation code +} +``` + +**Confirm sign-up:** +```dart +await Amplify.Auth.confirmSignUp( + username: username, + confirmationCode: code, +); +``` + +### Swift (Apple platforms) + +Uses async/await. + +```swift +import Amplify +``` + +**Sign in:** +```swift +do { + let result = try await Amplify.Auth.signIn( + username: username, + password: password + ) + switch result.nextStep { + case .done: + print("Sign in succeeded") + case .confirmSignInWithSMSMFACode(let details, _): + print("SMS code sent to \(details.destination)") + // Prompt user, then call confirmSignIn + case .confirmSignInWithTOTPCode: + // Prompt for TOTP code, then call confirmSignIn + default: + print("Next step: \(result.nextStep)") + } +} catch let error as AuthError { + print("Sign in failed: \(error)") +} +``` + +**Confirm sign-in:** +```swift +let result = try await Amplify.Auth.confirmSignIn( + challengeResponse: codeFromUser +) +``` + +**Sign up:** +```swift +let options = AuthSignUpRequest.Options( + userAttributes: [AuthUserAttribute(.email, value: email)] +) +let result = try await Amplify.Auth.signUp( + username: username, + password: password, + options: options +) +if case .confirmUser(let details, _, _) = result.nextStep { + print("Confirmation sent to \(String(describing: details))") +} +``` + +**Confirm sign-up:** +```swift +try await Amplify.Auth.confirmSignUp( + for: username, + confirmationCode: code +) +``` + +### Android (Kotlin) + +Android supports **both** Kotlin coroutines and callbacks. Coroutines are +recommended. + +```kotlin +import com.amplifyframework.core.Amplify +import com.amplifyframework.auth.AuthUserAttributeKey +import com.amplifyframework.auth.options.AuthSignUpOptions +``` + +**Sign in (coroutines — recommended):** +```kotlin +try { + val result = Amplify.Auth.signIn("username", "password") + if (result.isSignedIn) { + Log.i("Auth", "Sign in succeeded") + } else { + // Handle result.nextStep.signInStep — e.g.: + // CONFIRM_SIGN_IN_WITH_SMS_MFA_CODE → prompt SMS code + // CONFIRM_SIGN_IN_WITH_TOTP_CODE → prompt TOTP code + // DONE → authenticated + Log.i("Auth", "Next step: ${result.nextStep.signInStep}") + } +} catch (error: AuthException) { + Log.e("Auth", "Sign in failed", error) +} +``` + +**Sign in (callbacks — alternative):** +```kotlin +Amplify.Auth.signIn("username", "password", + { result -> Log.i("Auth", "Signed in: ${result.isSignedIn}") }, + { error -> Log.e("Auth", "Sign in failed", error) } +) +``` + +**Confirm sign-in (coroutines):** +```kotlin +try { + val result = Amplify.Auth.confirmSignIn("code from user") + Log.i("Auth", "Confirmed: $result") +} catch (error: AuthException) { + Log.e("Auth", "Confirm failed", error) +} +``` + +**Sign up (coroutines):** +```kotlin +val options = AuthSignUpOptions.builder() + .userAttributes(listOf( + AuthUserAttribute(AuthUserAttributeKey.email(), email) + )) + .build() +try { + val result = Amplify.Auth.signUp("username", "password", options) + Log.i("Auth", "Sign up step: ${result.nextStep.signUpStep}") +} catch (error: AuthException) { + Log.e("Auth", "Sign up failed", error) +} +``` + +**Confirm sign-up (coroutines):** +```kotlin +try { + Amplify.Auth.confirmSignUp("username", "123456") +} catch (error: AuthException) { + Log.e("Auth", "Confirm sign-up failed", error) +} +``` + +## Social Login on Mobile + +Social sign-in uses an OAuth web UI redirect. **Callback URLs must match** the +`callbackUrls` configured in your `defineAuth` backend resource. + +**Flutter:** +```dart +final result = await Amplify.Auth.signInWithWebUI( + provider: AuthProvider.google, +); +``` + +Platform setup for Flutter OAuth: +- **Android:** Add `` with your callback scheme to `MainActivity` in `AndroidManifest.xml`. +- **iOS:** No additional platform configuration required. +- **macOS:** Enable App Sandbox → "Incoming Connections (Server)" in Xcode. + +**Swift:** +```swift +let result = try await Amplify.Auth.signInWithWebUI( + for: .google, + presentationAnchor: window +) +``` + +Platform setup: Add callback URL scheme to `Info.plist` under `CFBundleURLSchemes`. + +**Android (coroutines):** +```kotlin +try { + val result = Amplify.Auth.signInWithSocialWebUI( + AuthProvider.google(), activity + ) + Log.i("Auth", "Social sign-in OK: $result") +} catch (error: AuthException) { + Log.e("Auth", "Social sign-in failed", error) +} +``` + +Platform setup: Add `HostedUIRedirectActivity` with your callback scheme to `AndroidManifest.xml`: +```xml + + + + + + + + +``` + +## Pitfalls + +- **Plugin order:** `addPlugin()` / `add(plugin:)` **MUST** be called + before `configure()` on all platforms — see [core-mobile.md](core-mobile.md). +- **Missing INTERNET permission (Android):** Without + `` in + `AndroidManifest.xml`, all auth calls fail with a network error. +- **Callback URL mismatch (social login):** OAuth redirect URLs configured + in the native app (Info.plist / AndroidManifest.xml / Flutter scheme) + **MUST** match the `callbackUrls` in your `defineAuth` backend resource. + A mismatch causes a silent redirect failure. +- **Unhandled auth steps (Custom UI only):** When building custom sign-in + flows, the `nextStep` returned from `signIn` must be handled. Ignoring + steps like MFA confirmation causes the auth flow to stall silently. The + Authenticator component handles all steps automatically. + +## Links + +- [Authenticator (Android)](https://ui.docs.amplify.aws/android/connected-components/authenticator) +- [Authenticator (Swift)](https://ui.docs.amplify.aws/swift/connected-components/authenticator) +- [Authenticator (Flutter)](https://ui.docs.amplify.aws/flutter/connected-components/authenticator) +- [Auth Overview (Android)](https://docs.amplify.aws/android/build-a-backend/auth/) +- [Sign In (Android)](https://docs.amplify.aws/android/frontend/auth/sign-in/) +- [External Identity Providers (Android)](https://docs.amplify.aws/android/build-a-backend/auth/concepts/external-identity-providers/) +- [Multi-Factor Authentication (Android)](https://docs.amplify.aws/android/build-a-backend/auth/concepts/multi-factor-authentication/) +- [Auth Overview (Swift)](https://docs.amplify.aws/swift/build-a-backend/auth/) +- [Sign In (Swift)](https://docs.amplify.aws/swift/frontend/auth/sign-in/) +- [External Identity Providers (Swift)](https://docs.amplify.aws/swift/build-a-backend/auth/concepts/external-identity-providers/) +- [Multi-Factor Authentication (Swift)](https://docs.amplify.aws/swift/build-a-backend/auth/concepts/multi-factor-authentication/) +- [Auth Overview (Flutter)](https://docs.amplify.aws/flutter/build-a-backend/auth/) +- [Sign In (Flutter)](https://docs.amplify.aws/flutter/frontend/auth/sign-in/) +- [External Identity Providers (Flutter)](https://docs.amplify.aws/flutter/build-a-backend/auth/concepts/external-identity-providers/) +- [Multi-Factor Authentication (Flutter)](https://docs.amplify.aws/flutter/build-a-backend/auth/concepts/multi-factor-authentication/) diff --git a/aws-amplify/steering/auth-web.md b/aws-amplify/steering/auth-web.md new file mode 100644 index 0000000..f8295f0 --- /dev/null +++ b/aws-amplify/steering/auth-web.md @@ -0,0 +1,127 @@ +# Auth — Web + +> **Backend required:** Auth must be defined in `amplify/auth/resource.ts` +> using `defineAuth` — see [auth-backend.md](auth-backend.md). + +## Authenticator Component + +| Framework | Package | Tag | CSS (MUST import) | +|---|---|---|---| +| React / Next.js | `@aws-amplify/ui-react` | `` | `@aws-amplify/ui-react/styles.css` | +| Vue | `@aws-amplify/ui-vue` | `` | `@aws-amplify/ui-vue/styles.css` | +| Angular | `@aws-amplify/ui-angular` | `` + `AmplifyAuthenticatorModule` | `@aws-amplify/ui-angular/theme.css` | + +Props: `loginMechanisms={['email']}`, `socialProviders={['google']}`. +Slot: `{({ signOut, user }) => ...}` — access `user?.signInDetails?.loginId`. +Next.js SSR: wrap layout in ``, use `useAuthenticator` hook. + +## Manual Auth Flows + +Imports from `aws-amplify/auth`: `signIn`, `signUp`, `confirmSignUp`, `confirmSignIn`, `signOut`, `resetPassword`. + +After `signIn()`, you **MUST** switch on `result.nextStep.signInStep`: + +| signInStep value | Action | +|---|---| +| `DONE` | Authenticated | +| `CONFIRM_SIGN_UP` | Call `confirmSignUp()` | +| `CONFIRM_SIGN_IN_WITH_TOTP_CODE` | Prompt TOTP, call `confirmSignIn({ challengeResponse })` | +| `CONFIRM_SIGN_IN_WITH_SMS_CODE` | Prompt SMS code, same | +| `CONFIRM_SIGN_IN_WITH_EMAIL_CODE` | Prompt email code, same | +| `CONTINUE_SIGN_IN_WITH_TOTP_SETUP` | Show QR URI, call `confirmSignIn()` | +| `CONTINUE_SIGN_IN_WITH_MFA_SELECTION` | `confirmSignIn({ challengeResponse: 'TOTP'\|'SMS'\|'EMAIL' })` | +| `RESET_PASSWORD` | Call `resetPassword()` | +| `CONFIRM_SIGN_IN_WITH_NEW_PASSWORD_REQUIRED` | `confirmSignIn({ challengeResponse: newPassword })` | +| `CONFIRM_SIGN_IN_WITH_CUSTOM_CHALLENGE` | `confirmSignIn({ challengeResponse })` | +| `CONFIRM_SIGN_IN_WITH_PASSWORD` | `confirmSignIn({ challengeResponse: password })` | +| `CONTINUE_SIGN_IN_WITH_MFA_SETUP_SELECTION` | `confirmSignIn({ challengeResponse: 'TOTP'\|'EMAIL' })` | +| `CONTINUE_SIGN_IN_WITH_EMAIL_SETUP` | Prompt email, call `confirmSignIn()` | +| `CONTINUE_SIGN_IN_WITH_FIRST_FACTOR_SELECTION` | `confirmSignIn({ challengeResponse: selectedFactor })` | + +OAuth/social: `signInWithRedirect({ provider: 'Google' })`. + +## Session Management + +| API (from `aws-amplify/auth`) | Returns | +|---|---| +| `getCurrentUser()` | `{ userId, username, signInDetails? }` | +| `fetchAuthSession()` | `{ tokens?, credentials?, identityId?, userSub? }` — access `.tokens?.idToken`, `.tokens?.accessToken` | +| `fetchUserAttributes()` | `{ email, phone_number, ... }` | + +Tokens refresh automatically. + +## Next.js Server-Side Auth + +For server components and route handlers, use cookie-based auth: + +```typescript +import { generateServerClientUsingCookies } from '@aws-amplify/adapter-nextjs/data'; +import { cookies } from 'next/headers'; +import { amplifyOutputs } from '@/amplify_outputs'; + +const client = generateServerClientUsingCookies({ config: amplifyOutputs, cookies }); +``` + +> **Critical:** `generateServerClientUsingCookies` from `@aws-amplify/adapter-nextjs/data` is the ONLY way to access authenticated data in Next.js server components. Do NOT use `generateClient()` on the server side — it has no access to the user's session cookies. + +For server actions and middleware, use `createServerRunner` from `@aws-amplify/adapter-nextjs`: + +```typescript +import { createServerRunner } from '@aws-amplify/adapter-nextjs'; +import { amplifyOutputs } from '@/amplify_outputs'; + +export const { runWithAmplifyServerContext } = createServerRunner({ config: amplifyOutputs }); +``` + +## React Native + +React Native uses the same `aws-amplify` auth APIs as web. All manual auth +flows (`signIn`, `signUp`, `confirmSignIn`, etc.) and session management +APIs work identically. + +### Setup + +**Import order matters:** `react-native-get-random-values` **MUST** be +the FIRST import in the entry file, `@aws-amplify/react-native` **MUST** +come before `aws-amplify`. See [core-web.md](core-web.md) for the +full required import order. + +```bash +npm install @aws-amplify/ui-react-native @react-native-async-storage/async-storage +``` + +Same `` prop API as web React (from `@aws-amplify/ui-react-native`). +`@react-native-async-storage/async-storage` is **required** for token persistence. + +### Social Login + +`signInWithRedirect({ provider: 'Google' })` — same as web. Ensure +callback URLs in `defineAuth` include your Expo scheme. + +## Pitfalls + +- **Missing CSS import:** Without the `styles.css` import, the + `` renders as unstyled HTML. +- **Unhandled sign-in steps:** Not switching on ALL `signInStep` values + causes the flow to silently stall on MFA or password-reset challenges. + You **MUST** handle every possible value — missing any causes the auth + flow to hang with no visible error. +- **MFA timing:** Calling `updateMFAPreference()` before authentication + completes fails silently because the user is not yet authenticated. + Wait until `signInStep` is `'DONE'`. +- **OAuth in multi-page apps:** You **MUST** call `Hub.listen('auth', ...)` + to capture the OAuth redirect callback on page reload. +- **Vue component syntax:** Vue **MUST** use PascalCase `` + component syntax (not kebab-case ``). + +## Links + +- [Auth Overview (React)](https://docs.amplify.aws/react/build-a-backend/auth/) +- [Set Up Auth (React)](https://docs.amplify.aws/react/build-a-backend/auth/set-up-auth/) +- [Connect Auth Frontend (React)](https://docs.amplify.aws/react/frontend/auth/) +- [Auth Overview (Next.js)](https://docs.amplify.aws/nextjs/build-a-backend/auth/) +- [Set Up Auth (Next.js)](https://docs.amplify.aws/nextjs/build-a-backend/auth/set-up-auth/) +- [Connect Auth Frontend (Next.js)](https://docs.amplify.aws/nextjs/frontend/auth/) +- [Auth Overview (React Native)](https://docs.amplify.aws/react-native/build-a-backend/auth/) +- [Set Up Auth (React Native)](https://docs.amplify.aws/react-native/build-a-backend/auth/set-up-auth/) +- [Connect Auth Frontend (React Native)](https://docs.amplify.aws/react-native/frontend/auth/) diff --git a/aws-amplify/steering/core-mobile.md b/aws-amplify/steering/core-mobile.md new file mode 100644 index 0000000..ae5c93b --- /dev/null +++ b/aws-amplify/steering/core-mobile.md @@ -0,0 +1,185 @@ +# Core — Mobile + +## Critical Rules + +These patterns apply to **every** task — not just new projects. You **MUST** +verify each one before implementing any feature. + +### Gen2 Detection + +Before modifying any code, check if the project is already Gen2: + +1. `amplify/` directory exists with `backend.ts` +2. `amplify_flutter` in `pubspec.yaml` (Flutter) or + `@aws-amplify/backend` in `package.json` devDependencies (for projects using a JS/TS-based Amplify backend alongside a native mobile frontend) + +If both are true, the project is already Gen2 — skip to feature +implementation. If `amplify/.config/` exists instead, this is a Gen1 +project — **MUST NOT** proceed (requires separate migration skill). + +### Plugin Initialization Order + +Flutter, Swift, and Android all require plugins to be added **before** +calling configure. Reversing the order causes a runtime exception. + +**Flutter:** + +```dart +await Amplify.addPlugins([AmplifyAuthCognito()]); +await Amplify.configure(amplifyConfig); +``` + +**Swift:** + +```swift +try Amplify.add(plugin: AWSCognitoAuthPlugin()) +try Amplify.configure(with: .amplifyOutputs) +``` + +**Android (Kotlin):** + +```kotlin +Amplify.addPlugin(AWSCognitoAuthPlugin()) +Amplify.configure(AmplifyOutputs(R.raw.amplify_outputs), applicationContext) +``` + +### Required Packages + +**Flutter** — `pubspec.yaml`: + +```yaml +dependencies: + amplify_flutter: ^2.0.0 + amplify_auth_cognito: ^2.0.0 +``` + +**Swift:** Add via Xcode SPM: `https://github.com/aws-amplify/amplify-swift` +(Up to Next Major Version). + +**Android** — `app/build.gradle.kts`: + +```kotlin +dependencies { + implementation("com.amplifyframework:core:2.+") + implementation("com.amplifyframework:aws-auth-cognito:2.+") +} +``` + +### Configure Entry Points + +**Flutter** — `lib/main.dart`: + +```dart +import 'package:amplify_flutter/amplify_flutter.dart'; +import 'package:amplify_auth_cognito/amplify_auth_cognito.dart'; +import 'amplify_outputs.dart'; + +Future main() async { + WidgetsFlutterBinding.ensureInitialized(); + await Amplify.addPlugins([AmplifyAuthCognito()]); + await Amplify.configure(amplifyConfig); + runApp(const MyApp()); +} +``` + +**Swift (SwiftUI)** — `MyApp.swift`: + +```swift +import SwiftUI +import Amplify +import AWSCognitoAuthPlugin + +@main +struct MyApp: App { + init() { + do { + try Amplify.add(plugin: AWSCognitoAuthPlugin()) + try Amplify.configure(with: .amplifyOutputs) + } catch { + print("Failed to configure Amplify: \(error)") + } + } + var body: some Scene { + WindowGroup { ContentView() } + } +} +``` + +**Swift (UIKit)** — `AppDelegate.swift`: + +```swift +import Amplify +import AWSCognitoAuthPlugin + +func application(_ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + do { + try Amplify.add(plugin: AWSCognitoAuthPlugin()) + try Amplify.configure(with: .amplifyOutputs) + } catch { + print("Failed to configure Amplify: \(error)") + } + return true +} +``` + +**Android** — Application class: + +```kotlin +import com.amplifyframework.core.Amplify +import com.amplifyframework.auth.cognito.AWSCognitoAuthPlugin +import com.amplifyframework.core.configuration.AmplifyOutputs + +class MyApp : Application() { + override fun onCreate() { + super.onCreate() + Amplify.addPlugin(AWSCognitoAuthPlugin()) + Amplify.configure(AmplifyOutputs(R.raw.amplify_outputs), applicationContext) + } +} +``` + +For Android, `amplify_outputs.json` **MUST** go in `app/src/main/res/raw/`, +not in the project root. + +## Platform-Specific MUST Steps + +### Android: Core Library Desugaring + +Core library desugaring **MUST** be enabled for Android API level < 26. The agent **MUST** provide explicit step-by-step desugaring instructions to the customer — do not just mention it. See: https://docs.amplify.aws/android/start/quickstart/#5-install-dependencies + +In `app/build.gradle.kts`: + +```kotlin +android { + compileOptions { + isCoreLibraryDesugaringEnabled = true + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } +} +dependencies { + coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.4") +} +``` + +### Swift (Apple platforms): Xcode Project Configuration + +> Supported: iOS 13+, macOS 12+, tvOS 13+, watchOS 9+, visionOS 1+ (preview). + +For iOS/Swift projects, `amplify_outputs.json` **MUST** be added to the Xcode project. The agent should instruct the user to drag the `amplify_outputs.json` file into their Xcode project navigator so it is included in the app bundle. + +### Flutter: Dart Output Format + +For Flutter, `amplify_outputs.dart` **MUST** be generated by specifying the dart output format when running sandbox or deploy commands: + +```bash +npx ampx sandbox --outputs-format dart --outputs-out-dir lib +npx ampx generate outputs --format dart --out-dir lib +``` + +## Links + +- [Android Quickstart](https://docs.amplify.aws/android/start/quickstart/) +- [Swift Quickstart](https://docs.amplify.aws/swift/start/quickstart/) +- [Flutter Quickstart](https://docs.amplify.aws/flutter/start/quickstart/) diff --git a/aws-amplify/steering/core-web.md b/aws-amplify/steering/core-web.md new file mode 100644 index 0000000..c97ec27 --- /dev/null +++ b/aws-amplify/steering/core-web.md @@ -0,0 +1,151 @@ +# Core — Web + +## Critical Rules + +These patterns apply to **every** task — not just new projects. You **MUST** +verify each one before implementing any feature. + +### Gen2 Detection + +Before modifying any code, check if the project is already Gen2: + +1. `amplify/` directory exists with `backend.ts` +2. `@aws-amplify/backend` in `package.json` devDependencies + +If both are true, the project is already Gen2 — skip to feature +implementation. If `amplify/.config/` exists instead, this is a Gen1 +project — **MUST NOT** proceed (requires separate migration skill). + +### Directory Structure + +`amplify/` and `src/` **MUST** be siblings under the project root. Placing +them at different directory levels breaks sandbox detection. + +``` +project-root/ +├── amplify/ +│ ├── backend.ts +│ ├── auth/resource.ts +│ ├── data/resource.ts +│ └── storage/resource.ts +├── src/ +├── amplify_outputs.json # Generated — DO NOT edit +└── package.json +``` + +### Frontend Configuration + +Import the generated outputs and configure Amplify in the **correct entry +point** for your framework. Placing this in the wrong file causes silent +failures — Amplify API calls return undefined or empty responses with no error. + +**WARNING:** `amplify_outputs.json` **MUST** exist before the app can +compile. If missing, the build fails with a module-not-found error. +Run `npx ampx sandbox` (or `npx ampx sandbox --once`) first to +generate it. See [scaffolding.md](scaffolding.md) for the correct sequence. +**React (Vite)** — `src/main.tsx`: + +```typescript +import { Amplify } from 'aws-amplify'; +import outputs from '../amplify_outputs.json'; +Amplify.configure(outputs); +``` +**Next.js (App Router)** — `app/layout.tsx`: + +```typescript +import { Amplify } from 'aws-amplify'; +import outputs from '@/amplify_outputs.json'; +Amplify.configure(outputs, { ssr: true }); +``` + +**`{ ssr: true }` applies only to Next.js App Router.** All other frameworks +(Vue, Angular, React SPA) omit this option. +**Vue** — `src/main.js`: + +```javascript +import { Amplify } from 'aws-amplify'; +import outputs from '../amplify_outputs.json'; +Amplify.configure(outputs); +``` +**Angular** — `src/main.ts`: + +```typescript +import { Amplify } from 'aws-amplify'; +import outputs from '../amplify_outputs.json'; +Amplify.configure(outputs); +``` + +## Data Client Best Practices + +See [data-web.md](data-web.md) for `generateClient` setup and module-scope rules. + +For Next.js Server Components, use `generateServerClientUsingCookies` from +`@aws-amplify/adapter-nextjs/data` — NOT `generateClient`. Server +components have no browser session, so `generateClient` fails silently. +`` is required in `layout.tsx` for auth context. + +## React Native + +React Native uses the same `aws-amplify` JS package as web frameworks (it is +part of amplify-js, not the native mobile SDKs). All web APIs apply to RN +with the additions below. + +### Required Packages + +```bash +npm install aws-amplify @aws-amplify/react-native \ + @react-native-async-storage/async-storage \ + react-native-get-random-values +``` + +`@react-native-async-storage/async-storage` is **required** — the Amplify +SDK uses it for token persistence and will fail at runtime without it. + +### Configure Entry Points + +No plugin registration needed — configure only. + +**React Native (Expo)** — `App.tsx`: + +```typescript +import 'react-native-get-random-values'; // MUST be first +import { Amplify } from 'aws-amplify'; +import outputs from './amplify_outputs.json'; +Amplify.configure(outputs); +``` + +**React Native (Bare CLI)** — `index.js` (before `AppRegistry.registerComponent`): + +```typescript +import 'react-native-get-random-values'; // MUST be first +import { Amplify } from 'aws-amplify'; +import outputs from './amplify_outputs.json'; +Amplify.configure(outputs); +``` + +### Gen2 Detection (React Native) + +Same as web — check for `amplify/` directory with `backend.ts` and +`@aws-amplify/backend` in `package.json` devDependencies. + +### React Native Pitfalls + +- **Import order:** `react-native-get-random-values` **MUST** be the FIRST + import in the entry file, before `aws-amplify`. Reversing the order causes + cryptographic failures at runtime. +- **Missing AsyncStorage:** Without + `@react-native-async-storage/async-storage`, auth tokens are not persisted + and users must re-authenticate on every app restart. + +## Pitfalls + +- Forgetting to import `amplify_outputs.json` in the entry point — the app + will load but all Amplify API calls will fail silently. + +## Links + +- [React Quickstart](https://docs.amplify.aws/react/start/quickstart/) +- [Next.js Quickstart](https://docs.amplify.aws/nextjs/start/quickstart/) +- [Angular Quickstart](https://docs.amplify.aws/angular/start/quickstart/) +- [Vue Quickstart](https://docs.amplify.aws/vue/start/quickstart/) +- [React Native Quickstart](https://docs.amplify.aws/react-native/start/quickstart/) diff --git a/aws-amplify/steering/data-backend.md b/aws-amplify/steering/data-backend.md new file mode 100644 index 0000000..494c108 --- /dev/null +++ b/aws-amplify/steering/data-backend.md @@ -0,0 +1,297 @@ +# Data — Backend + +## Schema Definition + +Define your data models in `amplify/data/resource.ts`: + +```typescript +import { type ClientSchema, a, defineData } from '@aws-amplify/backend'; + +const schema = a.schema({ + Todo: a.model({ + content: a.string().required(), + priority: a.enum(['low', 'medium', 'high']), + done: a.boolean().default(false), + dueDate: a.date(), + owner: a.string(), + }).authorization(allow => [allow.owner()]), +}); + +export type Schema = ClientSchema; +export const data = defineData({ + schema, + authorizationModes: { + defaultAuthorizationMode: 'apiKey', + apiKeyAuthorizationMode: { + expiresInDays: 30, + }, + }, +}); +``` + +Import into `amplify/backend.ts`: + +```typescript +import { defineBackend } from '@aws-amplify/backend'; +import { auth } from './auth/resource'; +import { data } from './data/resource'; +defineBackend({ auth, data }); +``` + +You **MUST** export `Schema` as `ClientSchema` — without +this export, frontend clients lose all type inference. +**Field types:** `a.string()`, `a.integer()`, `a.float()`, `a.boolean()`, +`a.date()`, `a.datetime()`, `a.timestamp()`, `a.time()`, `a.email()`, +`a.url()`, `a.phone()`, `a.ipAddress()`, `a.json()`, `a.id()`, +`a.enum([...])`. Chain `.required()`, `.default(value)`, or `.array()` on +any field. + +## Authorization Rules + +Six strategies, applied per-model or per-field: + +**WARNING:** In data authorization rules, `allow.guest()` is a **method +call** (with parentheses). In storage access rules, `allow.guest` is a +**property** (no parentheses). Mixing these up causes TypeScript errors. + +```typescript +a.model({ /* fields */ }).authorization(allow => [ + allow.publicApiKey().to(['read']), // API key: public read + allow.guest().to(['read']), // Requires defaultAuthorizationMode: 'iam' — NOTE: method call () + allow.owner(), // Creator has full CRUD + allow.authenticated().to(['read']), // Any signed-in user can read + allow.group('Admins'), // Named Cognito group + allow.custom(), // Lambda authorizer +]) +``` + +Per-field authorization overrides model-level rules: + +```typescript +Post: a.model({ + title: a.string(), + secret: a.string().authorization(allow => [allow.owner()]), +}).authorization(allow => [allow.authenticated().to(['read'])]) +``` +**Multi-owner:** Use `allow.ownersDefinedIn('editors')` with an +`editors: a.string().array()` field to grant multiple users ownership. +**Dynamic groups:** Use `allow.groupsDefinedIn('teamGroups')` with a +string field to control access via group names stored on each record. + +## Relationships + +Three types — reference field types **MUST** match the related model's +identifier type. + +```typescript +const schema = a.schema({ + Team: a.model({ + name: a.string().required(), + members: a.hasMany('Member', 'teamId'), + }).authorization(allow => [allow.owner()]), + + Member: a.model({ + name: a.string().required(), + teamId: a.id().required(), + team: a.belongsTo('Team', 'teamId'), + profile: a.hasOne('Profile', 'memberId'), + }).authorization(allow => [allow.owner()]), + + Profile: a.model({ + bio: a.string(), + memberId: a.id().required(), + member: a.belongsTo('Member', 'memberId'), + }).authorization(allow => [allow.owner()]), +}); +``` + +The second argument to `hasMany`/`belongsTo`/`hasOne` is the foreign key +field name. That field **MUST** be declared explicitly on the child model. + +You **MUST** declare **both sides** of every relationship — the parent model +needs `a.hasMany('Child', 'fkField')` AND the child model needs +`a.belongsTo('Parent', 'fkField')`. Omitting either side causes silent +query failures (e.g., lazy-loading the relation returns `undefined`). + +The foreign-key field **MUST** use `a.id()` — NOT `a.string()` — to match +the related model's identifier type. Using `a.string()` causes runtime +relationship resolution failures. + +```typescript +// CORRECT — both sides declared, FK uses a.id() +Team: a.model({ + name: a.string().required(), + members: a.hasMany('Member', 'teamId'), // parent side +}) + +Member: a.model({ + name: a.string().required(), + teamId: a.id().required(), // FK: a.id(), NOT a.string() + team: a.belongsTo('Team', 'teamId'), // child side — REQUIRED +}) +``` + +## Secondary Indexes + +```typescript +Todo: a.model({ + content: a.string(), + status: a.string(), + createdAt: a.datetime(), +}).secondaryIndexes(index => [ + index('status').sortKeys(['createdAt']).queryField('listByStatus'), +]) +``` + +Indexes enable `client.models.Todo.listByStatus({ status: 'active' })`. +Composite sort keys allow multi-field sorting within a partition. You +**SHOULD** name the `queryField` descriptively — it becomes the typed +client method name. + +## Enum Types + +Define enums with `a.enum()` at the top level of `a.schema()`, then reference them in model fields with `a.ref()`: + +```typescript +const schema = a.schema({ + Priority: a.enum(['low', 'medium', 'high']), + + Task: a.model({ + title: a.string().required(), + priority: a.ref('Priority'), + }).authorization(allow => [allow.owner()]), +}); +``` + +You can also use `a.enum()` inline on a model field: + +```typescript +Todo: a.model({ + content: a.string().required(), + priority: a.enum(['low', 'medium', 'high']), +}) +``` + +> ⚠️ **Pitfall:** `.default()` does not work on `a.enum()` fields — default values are only supported on scalar types (`a.string()`, `a.integer()`, etc.). Applying `.default()` to an enum field silently fails at deployment. + +## Custom Types + +Custom types group related fields into a reusable structure: + +```typescript +const schema = a.schema({ + Location: a.customType({ lat: a.float(), lng: a.float() }), + + Task: a.model({ + title: a.string().required(), + location: a.ref('Location'), + }).authorization(allow => [allow.owner()]), +}); +``` + +Use `a.ref('TypeName')` to reference custom types or enums in model fields. + +## Custom Queries and Mutations + +Expose Lambda-backed operations through the schema: + +```typescript +const schema = a.schema({ + // ... models ... + echo: a.query() + .arguments({ message: a.string().required() }) + .returns(a.string()) + .handler(a.handler.function('echoHandler')) + .authorization(allow => [allow.authenticated()]), + + placeOrder: a.mutation() + .arguments({ productId: a.id().required(), qty: a.integer() }) + .returns(a.json()) + .handler(a.handler.function('orderHandler')) + .authorization(allow => [allow.authenticated()]), +}); +``` + +The handler function name **MUST** match a `defineFunction` name imported +into `backend.ts`. + +## Authorization Modes + +Configure default and additional auth modes in `defineData`: + +**Starter template default** (public access): + +```typescript +export const data = defineData({ + schema, + authorizationModes: { + defaultAuthorizationMode: 'apiKey', + apiKeyAuthorizationMode: { expiresInDays: 30 }, + }, +}); +``` + +**With auth** (user-scoped access): + +```typescript +export const data = defineData({ + schema, + authorizationModes: { + defaultAuthorizationMode: 'userPool', + apiKeyAuthorizationMode: { expiresInDays: 30 }, + // lambdaAuthorizationMode: { function: myAuthFn }, + }, +}); +``` + +The `defaultAuthorizationMode` **MUST** match at least one strategy used in +your model `authorization()` rules (e.g., `userPool` ↔ `owner()` / +`authenticated()` / `group()`; `apiKey` ↔ `publicApiKey()`; `iam` ↔ `guest()`). + +Guest access is enabled by default in Amplify Gen2 — see [auth-backend.md](auth-backend.md) for details and how to disable it. + +**Guest access configuration** (with `allow.guest()`): + +```typescript +// amplify/data/resource.ts — set IAM as default auth mode for guest access +export const data = defineData({ + schema, + authorizationModes: { + defaultAuthorizationMode: 'iam', + }, +}); +``` + +## Pitfalls + +- **Missing `ClientSchema` export:** Without `export type Schema = + ClientSchema`, frontend `generateClient()` has no + type information and all operations are untyped. +- **FK field type `a.string()` instead of `a.id()`:** Using `a.string()` + for foreign key fields causes relationship resolution to fail silently — + queries return `null` for related models. Always use `a.id()` for FK fields. +- **Missing relationship side:** Omitting `belongsTo` on the child model + (or `hasMany` on the parent) causes lazy-loading the relation to return + `undefined` with no error. +- **Guest access auth mode:** `allow.guest()` requires + `defaultAuthorizationMode: 'iam'` in `defineData`. Guest access + (unauthenticated identities) is enabled by default in Amplify Gen2. +- **Auth mode conflict:** Using `allow.publicApiKey()` in model rules but + setting `defaultAuthorizationMode: 'userPool'` without adding + `apiKeyAuthorizationMode` causes API key requests to be rejected. +- **Forgetting `defineBackend`:** Defining `data` without importing it + into `backend.ts` means the schema is never deployed. +- **`.default()` on enum fields:** `.default()` does not work on + `a.enum()` fields — default values are only supported on scalar types + (`a.string()`, `a.integer()`, `a.float()`, `a.boolean()`, etc.). + Applying `.default()` to an enum field silently fails at deployment. + +## Links + +- [Data Overview](https://docs.amplify.aws/react/build-a-backend/data/) +- [Set Up Data](https://docs.amplify.aws/react/build-a-backend/data/set-up-data/) +- [Data Modeling](https://docs.amplify.aws/react/build-a-backend/data/data-modeling/) +- [Data Modeling — Relationships](https://docs.amplify.aws/react/build-a-backend/data/data-modeling/relationships/) +- [Data Modeling — Add Fields](https://docs.amplify.aws/react/build-a-backend/data/data-modeling/add-fields/) +- [Customize Authorization](https://docs.amplify.aws/react/build-a-backend/data/customize-authz/) +- [Connect to Existing Data Sources](https://docs.amplify.aws/react/build-a-backend/data/connect-to-existing-data-sources/) diff --git a/aws-amplify/steering/data-mobile.md b/aws-amplify/steering/data-mobile.md new file mode 100644 index 0000000..67f9ecc --- /dev/null +++ b/aws-amplify/steering/data-mobile.md @@ -0,0 +1,117 @@ +# Data — Mobile + +> **Backend required:** Data must be defined in `amplify/data/resource.ts` +> using `defineData` — see [data-backend.md](data-backend.md). + +## Flutter + +Import `package:amplify_flutter/amplify_flutter.dart`. All operations go through `Amplify.API`. + +**Queries:** `Amplify.API.query(request: ModelQueries.list(Todo.classType))` — response in `.response.data?.items`. +Same pattern for `.get()`. + +**Mutations:** `Amplify.API.mutate(request: ModelMutations.create(todo))` — same shape for `.update()`, `.delete()`. +Build updated models with `todo.copyWith(done: true)`. + +**Subscriptions:** `Amplify.API.subscribe(ModelSubscriptions.onCreate(Todo.classType))` → returns a stream. Listen with `.listen()`, cancel with `sub.cancel()`. + +## Swift (Apple platforms) + +> Supported: iOS 13+, macOS 12+, tvOS 13+, watchOS 9+, visionOS 1+ (preview). + +Uses `Amplify.API.query/mutate` with async/await. +Swift uses `ModelQueries`, `ModelMutations`, and `ModelSubscriptions` (plural, like Flutter). + +**Queries:** `try await Amplify.API.query(request: .list(Todo.self))` — result is `.success(let todos)`. + +**Mutations:** `try await Amplify.API.mutate(request: .create(newTodo))` — same for `.update()`, `.delete()`. +Modify models directly: `updated.done = true`. + +**Subscriptions:** `Amplify.API.subscribe(request: .subscription(of: Todo.self, type: .onCreate))` → use `for try await event in subscription`. Cancel via `task.cancel()` when the view disappears. + +## Android (Kotlin) + +Android supports both callback-based and coroutine-based APIs. +Coroutine example (recommended): + +**Queries:** + +```kotlin +suspend fun getTodo(id: String) { + try { + val response = Amplify.API.query(ModelQuery.get(Todo::class.java, id)) + Log.i("MyAmplifyApp", response.data.name) + } catch (error: ApiException) { + Log.e("MyAmplifyApp", "Query failed", error) + } +} +``` + +**Mutations:** + +```kotlin +val todo = Todo.builder() + .name("My todo") + .build() +try { + val response = Amplify.API.mutate(ModelMutation.create(todo)) + Log.i("MyAmplifyApp", "Todo with id: ${response.data.id}") +} catch (error: ApiException) { + Log.e("MyAmplifyApp", "Create failed", error) +} +``` + +Same pattern for `.update()` and `.delete()`. +Build models via `Todo.builder().name("text").build()`; update via `todo.copyOfBuilder().done(true).build()`. + +**Subscriptions (coroutine — uses Kotlin Flow):** + +```kotlin +val job = scope.launch { + try { + Amplify.API.subscribe(ModelSubscription.onCreate(Todo::class.java)) + .catch { Log.e("MyAmplifyApp", "Error on subscription", it) } + .collect { Log.i("MyAmplifyApp", "Todo created: ${it.data.name}") } + } catch (error: ApiException) { + Log.e("MyAmplifyApp", "Subscription not established", error) + } +} +// When done: +job.cancel() +``` + +**Callback alternative:** all operations also accept `onSuccess`/`onError` lambdas — e.g. +`Amplify.API.query(ModelQuery.list(Todo::class.java), { response -> ... }, { error -> ... })`. + +## Pitfalls + +- **Missing codegen for native platforms:** Flutter, Swift, and Android + **MUST** run `npx ampx generate graphql-client-code` to produce typed model + classes. Without this step, model types do not exist. You **SHOULD** use + typed model classes for compile-time safety. +- **GraphQL vs REST confusion:** All data operations use the GraphQL API + (`Amplify.API.query`/`mutate`), not REST. Using REST methods for model + CRUD returns errors. +- **Subscription cleanup:** Every platform **MUST** perform explicit + subscription cleanup (`.cancel()` on Swift tasks, `job.cancel()` for + Kotlin coroutines, `subscription.cancel()` for callbacks, or + `sub.cancel()` for Flutter). Missing cleanup causes connection leaks and + stale data. +- **Offline sync (Flutter/Swift/Android):** DataStore is a separate API + from direct API operations. Do not mix `DataStore.query()` with + `Amplify.API.query()` in the same model workflow. + +## Links + +- [Data Overview (Android)](https://docs.amplify.aws/android/build-a-backend/data/) +- [Set Up Data (Android)](https://docs.amplify.aws/android/build-a-backend/data/set-up-data/) +- [Connect to Existing Data Sources (Android)](https://docs.amplify.aws/android/build-a-backend/data/connect-to-existing-data-sources/) +- [Data Client (Android)](https://docs.amplify.aws/android/frontend/data/) +- [Data Overview (Swift)](https://docs.amplify.aws/swift/build-a-backend/data/) +- [Set Up Data (Swift)](https://docs.amplify.aws/swift/build-a-backend/data/set-up-data/) +- [Connect to Existing Data Sources (Swift)](https://docs.amplify.aws/swift/build-a-backend/data/connect-to-existing-data-sources/) +- [Data Client (Swift)](https://docs.amplify.aws/swift/frontend/data/) +- [Data Overview (Flutter)](https://docs.amplify.aws/flutter/build-a-backend/data/) +- [Set Up Data (Flutter)](https://docs.amplify.aws/flutter/build-a-backend/data/set-up-data/) +- [Connect to Existing Data Sources (Flutter)](https://docs.amplify.aws/flutter/build-a-backend/data/connect-to-existing-data-sources/) +- [Data Client (Flutter)](https://docs.amplify.aws/flutter/frontend/data/) diff --git a/aws-amplify/steering/data-web.md b/aws-amplify/steering/data-web.md new file mode 100644 index 0000000..f3442a5 --- /dev/null +++ b/aws-amplify/steering/data-web.md @@ -0,0 +1,106 @@ +# Data — Web + +> **Backend required:** Data must be defined in `amplify/data/resource.ts` +> using `defineData` — see [data-backend.md](data-backend.md). + +## Client Setup + +**`generateClient()` MUST be called at module scope** (outside +any React component). Calling it inside a component creates a new client +per render, breaking subscriptions and caching. + +```typescript +import { generateClient } from 'aws-amplify/data'; +import type { Schema } from '../amplify/data/resource'; + +// Module scope — called once +const client = generateClient(); +``` + +The `` generic gives full type inference on all model operations. + +## CRUD Operations + +All operations return `{ data, errors }`. You **SHOULD** check `errors` before using `data`. + +```typescript +const { data, errors } = await client.models.Todo.create({ content: 'Ship feature', priority: 'high' }); +``` + +Same shape for `.list()`, `.get({ id })`, `.update({ id, done: true })`, `.delete({ id })`. +`.list()` accepts an optional `filter`: `{ filter: { done: { eq: false } } }`. + +### Error Handling + +You **SHOULD** handle both GraphQL-level errors and network failures: + +```tsx +try { + const { data, errors } = await client.models.Todo.create({ content: 'New todo' }); + if (errors) { /* handle GraphQL field/validation errors */ } +} catch (err) { + /* handle network or unexpected errors */ +} +``` + +## Real-Time + +- **`observeQuery()`** — auto-updating list, returns `{ items }` snapshots. Recommended default. +- **`onCreate()` / `onUpdate()` / `onDelete()`** — per-event subscriptions. + +Both return an observable; call `.subscribe({ next })` and **MUST** call `sub.unsubscribe()` in cleanup. + +```tsx +useEffect(() => { + const sub = client.models.Todo.observeQuery().subscribe({ + next: ({ items }) => setTodos(items), + }); + return () => sub.unsubscribe(); +}, []); +``` + +## Server-Side (Next.js) + +```typescript +import { generateServerClientUsingCookies } from '@aws-amplify/adapter-nextjs/data'; +import { cookies } from 'next/headers'; +import outputs from '@/amplify_outputs.json'; +import type { Schema } from '@/amplify/data/resource'; + +const cookieClient = generateServerClientUsingCookies({ config: outputs, cookies }); +``` + +Use `cookieClient.models.*` the same as the browser client. Works in Server Components, Server Actions, and App Router API routes. + +## React Native + +Identical to the web client — uses `generateClient()` from `aws-amplify/data`. +All CRUD, `observeQuery()`, and subscription APIs (`onCreate`, `onUpdate`, `onDelete`) are the same. + +## Pitfalls + +- **Subscription memory leaks:** `useEffect` **MUST** return + `() => sub.unsubscribe()` as a cleanup function. Without it, + subscriptions accumulate across re-renders, causing memory leaks and + duplicate data updates. +- **Wrong auth mode for subscriptions:** Subscriptions require a + WebSocket-compatible auth mode (`userPool` or `iam`). API key auth on + subscriptions fails silently. +- **Missing `` generic:** `generateClient()` without `` + returns an untyped client — all operations lose autocomplete and type checking. +- **Server client without cookies:** Using `generateClient()` in Next.js + server components fails (no browser session) — you **MUST** use + `generateServerClientUsingCookies`. + +## Links + +- [Data Overview (React)](https://docs.amplify.aws/react/build-a-backend/data/) +- [Set Up Data (React)](https://docs.amplify.aws/react/build-a-backend/data/set-up-data/) +- [Connect to API (React)](https://docs.amplify.aws/react/frontend/data/connect-to-API/) +- [Data Client (React)](https://docs.amplify.aws/react/frontend/data/) +- [Data Overview (Next.js)](https://docs.amplify.aws/nextjs/build-a-backend/data/) +- [Set Up Data (Next.js)](https://docs.amplify.aws/nextjs/build-a-backend/data/set-up-data/) +- [Data Client (Next.js)](https://docs.amplify.aws/nextjs/frontend/data/) +- [Data Overview (React Native)](https://docs.amplify.aws/react-native/build-a-backend/data/) +- [Set Up Data (React Native)](https://docs.amplify.aws/react-native/build-a-backend/data/set-up-data/) +- [Data Client (React Native)](https://docs.amplify.aws/react-native/frontend/data/) diff --git a/aws-amplify/steering/deployment.md b/aws-amplify/steering/deployment.md new file mode 100644 index 0000000..13a82ca --- /dev/null +++ b/aws-amplify/steering/deployment.md @@ -0,0 +1,270 @@ +# Deployment + +## Prerequisites + +Before deploying, verify: + +- `npx ampx --version` returns a valid version +- `aws sts get-caller-identity` succeeds +- Node.js ≥ 18.x installed +- `.gitignore` includes `node_modules/`, `.env*`, `amplify_outputs.json`, + `.amplify/` + +**`amplify_outputs.json` is gitignored** — it is generated at build +time, NOT committed to source control: +- **Local dev:** `npx ampx sandbox` generates it automatically +- **CI/CD:** `npx ampx pipeline-deploy` generates it during the build phase +- **Other frontend apps in a monorepo:** Use + `npx ampx generate outputs --app-id ` to generate it +- Project is a Gen2 project — see + [core-web.md](core-web.md) or + [core-mobile.md](core-mobile.md) for detection + logic (Gen2 uses `amplify/backend.ts` + `defineBackend()`) + +## Sandbox Deployment + +Deploy a personal development environment: + +```bash +AWS_REGION=us-east-1 npx ampx sandbox --once +``` + +You **MUST** use the `--once` flag in agent and CI environments — without +it, the command starts a file watcher that never exits. If prompted to +bootstrap, run `npx ampx sandbox --once` again after bootstrapping +completes. + +Verify `amplify_outputs.json` was generated in the project root. + +## CI/CD Setup + +### Create the Amplify App + +```bash +REPO="github.com//" +APP_ID=$(aws amplify create-app \ + --name my-app \ + --repository "$REPO" \ + --access-token "$(gh auth token)" \ + --query 'app.appId' --output text) +``` + +You **MUST** use `github.com/user/repo` format — **not** `https://`. + +### IAM Service Role + +Create a dedicated role for Amplify backend deployments: + +```bash +ROLE_NAME="AmplifyBackendRole-${APP_ID}" + +# 1. Create the role with Amplify trust policy +aws iam create-role --role-name "$ROLE_NAME" --assume-role-policy-document '{ + "Version": "2012-10-17", + "Statement": [{ + "Effect": "Allow", + "Principal": {"Service": "amplify.amazonaws.com"}, + "Action": "sts:AssumeRole" + }] +}' + +# 2. Attach the backend deploy policy +aws iam attach-role-policy --role-name "$ROLE_NAME" \ + --policy-arn arn:aws:iam::aws:policy/service-role/AmplifyBackendDeployFullAccess + +# 3. Attach the role to the app +ROLE_ARN=$(aws iam get-role --role-name "$ROLE_NAME" --query 'Role.Arn' --output text) +aws amplify update-app --app-id "$APP_ID" --iam-service-role-arn "$ROLE_ARN" +``` + +All three steps are required — missing the role causes +`AccessDeniedException` during deployment. + +### Create Branch + +```bash +aws amplify create-branch --app-id "$APP_ID" --branch-name main +``` + +### amplify.yml + +Create `amplify.yml` in the project root. Set `baseDirectory` per +framework: + +| Framework | baseDirectory | +|-----------|---------------| +| Vite (React/Vue) | `dist` | +| CRA | `build` | +| Next.js (export) | `out` | +| Next.js (SSR) | `.next` | +| Angular | `dist//browser` | + +**Wrong `baseDirectory` = blank page in production** (silent failure). +Always match the framework table above. + +```yaml +version: 1 +backend: + phases: + build: + commands: + - npm ci --cache .npm --prefer-offline + - npx ampx pipeline-deploy --branch $AWS_BRANCH --app-id $AWS_APP_ID +frontend: + phases: + build: + commands: + - npm run build + artifacts: + baseDirectory: dist # Change per framework (see table above) + files: + - '**/*' + cache: + paths: + - .npm/**/* + - node_modules/**/* +``` + +### Monorepo Configuration + +For monorepos, set `appRoot` in `amplify.yml` to the subdirectory +containing the Amplify app: + +```yaml +appRoot: packages/web +``` + +**WARNING:** `appRoot` must have **NO leading slash**. +`appRoot: packages/web` (correct) vs `appRoot: /packages/web` (wrong) + +Monorepo rules: +- Only **ONE** app runs `npx ampx pipeline-deploy`; other apps use + `npx ampx generate outputs --app-id ` to get their + `amplify_outputs.json`. +- Run `npm ci` at the **repo root**, NOT inside `appRoot`. + +### Trigger Deployment + +```bash +aws amplify start-job --app-id "$APP_ID" --branch-name main --job-type RELEASE +``` + +## Secrets Management +**Sandbox:** Set secrets via CLI: + +```bash +echo "" | npx ampx sandbox secret set MY_API_KEY +``` + +You **MUST** pipe the value via stdin — without the pipe, the command +prompts interactively. + +This stores the secret for your personal sandbox environment. +**Branch environments (production):** Set secrets via the `ampx` CLI: + +```bash +npx ampx secret set MY_API_KEY --branch main --app-id $APP_ID +``` + +Or via the Amplify console under App settings → Environment variables, or +via the AWS CLI: + +```bash +aws amplify update-app --app-id "$APP_ID" \ + --environment-variables MY_API_KEY= +``` + +Reference secrets in functions using `secret()` — see +[functions-and-api.md](functions-and-api.md) for the pattern. + +## Multi-Environment + +Use branch-based environments — each Git branch deploys independently: + +```bash +# Create a staging branch +git checkout -b staging +git push origin staging +aws amplify create-branch --app-id "$APP_ID" --branch-name staging +aws amplify start-job --app-id "$APP_ID" --branch-name staging --job-type RELEASE +``` + +Each branch gets isolated backend resources (Cognito pool, AppSync API, +DynamoDB tables). Set branch-specific secrets separately. + +## Custom Domains + +Associate a custom domain with the Amplify app: + +```bash +aws amplify create-domain-association \ + --app-id "$APP_ID" \ + --domain-name example.com \ + --sub-domain-settings '[ + {"prefix": "", "branchName": "main"}, + {"prefix": "staging", "branchName": "staging"} + ]' +``` + +Amplify auto-provisions an SSL certificate. You **MUST** add the +provided CNAME records to your DNS for verification. Check status: + +```bash +aws amplify get-domain-association --app-id "$APP_ID" --domain-name example.com +``` + +## Amplify Hosting + +Amplify Hosting provides framework-aware builds with SSR support for +Next.js. The build pipeline auto-detects the framework from +`package.json`. For SSR apps, Amplify deploys a Lambda@Edge or +CloudFront function — no manual CloudFront configuration needed. + +Production URL format: `https://..amplifyapp.com` + +## Deployment Validation + +After deployment, check job status with `aws amplify list-jobs --app-id "$APP_ID" --branch-name main --query 'jobSummaries[0].status'` and verify `amplify_outputs.json` endpoints match expected values. + +## Post-Deployment +**Rollback:** Revert via Git and redeploy: + +```bash +git revert HEAD --no-edit +git push origin main +# Amplify auto-triggers a new build from the push +``` + +For CI/CD, manually trigger: `aws amplify start-job --app-id "$APP_ID" +--branch-name main --job-type RELEASE`. + +## Pitfalls + +- **Missing `--once` flag:** Without `--once`, sandbox starts a file + watcher that never exits — agent sessions and CI pipelines hang + indefinitely. **MUST** use `npx ampx sandbox --once` in any + non-interactive environment. +- **Repo format:** You **MUST** use `github.com/user/repo` — the + `https://` prefix causes `create-app` to fail silently. +- **Missing IAM service role:** Skipping role creation causes + `AccessDeniedException` on every backend deployment. +- **Wrong `baseDirectory`:** Using `build` for a Vite app (which outputs + to `dist`) causes a blank page in production — match the framework table + above. This is a silent failure with no error message. +- **Monorepo `appRoot` leading slash:** `appRoot: packages/web` vs + `appRoot: /packages/web` — leading slash breaks path resolution. +- **`amplify_outputs.json` not committed:** This file is gitignored and + generated at build time. CI uses `pipeline-deploy` to generate it; + local dev uses `sandbox`. +- **Not bootstrapping:** First sandbox run in a new account/region + requires CDK bootstrapping — follow prompts or run + `npx ampx sandbox --once` again after bootstrap. + +## Links + +- [Fullstack Branching](https://docs.amplify.aws/react/deploy-and-host/fullstack-branching/) +- [Secrets and Variables](https://docs.amplify.aws/react/deploy-and-host/fullstack-branching/secrets-and-vars/) +- [Mono and Multi-Repos](https://docs.amplify.aws/react/deploy-and-host/fullstack-branching/mono-and-multi-repos/) +- [Custom Pipelines](https://docs.amplify.aws/react/deploy-and-host/fullstack-branching/custom-pipelines/) +- [Sandbox Environments](https://docs.amplify.aws/react/deploy-and-host/sandbox-environments/) +- [Sandbox Setup](https://docs.amplify.aws/react/deploy-and-host/sandbox-environments/setup/) diff --git a/aws-amplify/steering/functions-and-api.md b/aws-amplify/steering/functions-and-api.md new file mode 100644 index 0000000..0e078a2 --- /dev/null +++ b/aws-amplify/steering/functions-and-api.md @@ -0,0 +1,243 @@ +# Functions & API + +## Lambda Functions + +Define a function in `amplify/functions//resource.ts`: + +```typescript +import { defineFunction } from '@aws-amplify/backend'; + +export const myFunc = defineFunction({ + name: 'my-func', + entry: './handler.ts', + timeoutSeconds: 30, // default 3, max 900 + memoryMB: 512, // default 512 + runtime: 22, // Node.js version (18, 20, 22, 24); default 22 + environment: { + TABLE_NAME: 'my-table', + REGION: 'us-east-1', + }, +}); +``` + +Create the handler at `amplify/functions//handler.ts`: + +```typescript +import type { Handler } from 'aws-lambda'; +import { env } from '$amplify/env/my-func'; + +export const handler: Handler = async (event) => { + const table = env.TABLE_NAME; // typed, from defineFunction environment + return { statusCode: 200, body: JSON.stringify({ table }) }; +}; +``` + +Import into `amplify/backend.ts`: + +```typescript +import { defineBackend } from '@aws-amplify/backend'; +import { auth } from './auth/resource'; +import { myFunc } from './functions/my-func/resource'; +defineBackend({ auth, myFunc }); +``` + +## Environment Variables & Secrets + +You **SHOULD** import environment variables from `$amplify/env/` +— this provides **type-safe** access to values defined in `defineFunction`. +Values are also available at runtime via `process.env.VAR_NAME`, but the +`$amplify/env` import is preferred because it gives you compile-time type +checking and autocompletion. + +For sensitive values, use `secret()`: + +```typescript +import { defineFunction, secret } from '@aws-amplify/backend'; + +export const myFunc = defineFunction({ + name: 'my-func', + entry: './handler.ts', + environment: { + API_KEY: secret('MY_API_KEY'), + }, +}); +``` + +Set secrets via CLI: `echo "" | npx ampx sandbox secret set MY_API_KEY`. + +> **IMPORTANT:** The `ampx sandbox secret set` command is for **local/sandbox development only**. For apps deployed to **Amplify Hosting**, secrets **MUST** be created via the Hosting console or CLI — sandbox secrets are NOT available in hosted environments. See: https://docs.amplify.aws/react/deploy-and-host/fullstack-branching/secrets-and-vars/#set-secrets + +## Scheduled Functions + +Use `schedule` to invoke a function on a cron or natural-language schedule: + +```typescript +import { defineFunction } from '@aws-amplify/backend'; + +export const cronJob = defineFunction({ + name: 'cron-job', + entry: './handler.ts', + schedule: 'every 1h', // natural-language shorthand + // Valid shorthands: 'every 5m', 'every 1h', 'every 6h', 'every 1d' + // OR: schedule: '0 */1 * * ? *', // cron expression — same property +}); +``` + +The handler **MUST** use `EventBridgeHandler` type: + +```typescript +import type { EventBridgeHandler } from 'aws-lambda'; +export const handler: EventBridgeHandler<'Scheduled Event', void, void> = async () => { + // scheduled logic +}; +``` + +## Resource Access + +Grant a function access to other Amplify resources: + +```typescript +const backend = defineBackend({ auth, data, storage, myFunc }); + +// Grant function access to auth, data, and storage +backend.myFunc.resources.lambda.addEnvironment( + 'USER_POOL_ID', backend.auth.resources.userPool.userPoolId +); +backend.data.resources.tables['Todo'].grantReadData(backend.myFunc.resources.lambda); +backend.storage.resources.bucket.grantReadWrite(backend.myFunc.resources.lambda); +``` + +For data schema access, use `allow.resource()` in authorization rules: + +```typescript +const schema = a.schema({ + Todo: a.model({ + content: a.string(), + }).authorization(allow => [allow.resource(myFunc)]), +}); +``` + +## Custom Queries and Mutations + +Use `a.query()` and `a.mutation()` with `.handler()` to add custom server-side logic through AppSync (no API Gateway needed): + +```typescript +// amplify/data/resource.ts +const schema = a.schema({ + // Custom query with Lambda handler + summarize: a.query() + .arguments({ text: a.string().required() }) + .returns(a.string()) + .handler(a.handler.function(summarizeHandler)) + .authorization(allow => [allow.authenticated()]), + + // Custom mutation with Lambda handler + processOrder: a.mutation() + .arguments({ orderId: a.string().required() }) + .returns(a.json()) + .handler(a.handler.function(processOrderHandler)) + .authorization(allow => [allow.authenticated()]), +}); +``` + +> **When to use which:** +> - `a.query()` / `a.mutation()` with `.handler()` — AppSync-native, type-safe, uses the data schema. **Preferred for most custom logic.** +> - API Gateway + Lambda — Use when you need REST endpoints, webhooks, or third-party integrations that require a specific URL. + +## REST API (API Gateway) + +Create a REST API using CDK in `amplify/backend.ts`: + +```typescript +import { defineBackend } from '@aws-amplify/backend'; +import * as apigateway from 'aws-cdk-lib/aws-apigateway'; +import { myFunc } from './functions/my-func/resource'; + +const backend = defineBackend({ auth, myFunc }); +const apiStack = backend.createStack('RestApiStack'); + +const api = new apigateway.RestApi(apiStack, 'MyRestApi', { + restApiName: 'my-rest-api', + deployOptions: { stageName: 'prod' }, +}); +api.root.addResource('items').addMethod( + 'GET', new apigateway.LambdaIntegration(backend.myFunc.resources.lambda) +); + +backend.addOutput({ custom: { restApiUrl: api.url } }); +``` + +The handler **MUST** use `APIGatewayProxyHandler` type for REST API (v1): + +```typescript +import type { APIGatewayProxyHandler } from 'aws-lambda'; +``` + +## HTTP API (API Gateway v2) + +For a lightweight HTTP API: + +```typescript +import type { APIGatewayProxyHandlerV2 } from 'aws-lambda'; +import * as apigwv2 from 'aws-cdk-lib/aws-apigatewayv2'; +import { HttpLambdaIntegration } from 'aws-cdk-lib/aws-apigatewayv2-integrations'; + +const httpApi = new apigwv2.HttpApi(apiStack, 'MyHttpApi', { + corsPreflight: { allowOrigins: ['*'], allowMethods: [apigwv2.CorsHttpMethod.GET] }, +}); +httpApi.addRoutes({ + path: '/items', + methods: [apigwv2.HttpMethod.GET], + integration: new HttpLambdaIntegration('GetItems', backend.myFunc.resources.lambda), +}); + +backend.addOutput({ custom: { httpApiUrl: httpApi.url! } }); +``` + +The handler **MUST** use `APIGatewayProxyHandlerV2` type for HTTP API (v2). + +## Backend Outputs + +Use `backend.addOutput()` to expose custom values to the frontend via +`amplify_outputs.json`: + +```typescript +backend.addOutput({ custom: { apiUrl: api.url, region: 'us-east-1' } }); +``` + +Frontend reads custom outputs from the configured Amplify outputs. + +## Calling from Client + +For custom queries and mutations defined via `a.query()` or `a.mutation()`, call them from the client: + +```typescript +const { data } = await client.queries.myCustomQuery({ input: 'value' }); +``` + +For REST/HTTP API outputs added via `backend.addOutput()`, read the endpoint URL from `amplify_outputs.json` and use standard HTTP clients. + +## Pitfalls + +- **`runtime` must be an integer:** Use `runtime: 22`, NOT + `runtime: "nodejs22.x"`. String format causes build errors. +- **Wrong handler type:** REST API (v1) requires `APIGatewayProxyHandler` + with `event.httpMethod`; HTTP API (v2) requires `APIGatewayProxyHandlerV2` + with `event.requestContext.http.method`. Mixing them causes malformed + responses. Both return `{ statusCode, body }`. +- **Missing resource access:** A function without explicit grants cannot + access auth, data, or storage resources — add grants in `backend.ts`. +- **Secrets in plain `environment`:** Sensitive values **MUST** use + `secret()`, not string literals. +- **`createStack` name collision:** Stack names passed to + `backend.createStack()` **MUST** be unique across the backend. + Duplicate names cause deployment failures. + +## Links + +- [Functions Overview](https://docs.amplify.aws/react/build-a-backend/functions/) +- [Set Up Function](https://docs.amplify.aws/react/build-a-backend/functions/set-up-function/) +- [Environment Variables and Secrets](https://docs.amplify.aws/react/build-a-backend/functions/environment-variables-and-secrets/) +- [Grant Access to Other Resources](https://docs.amplify.aws/react/build-a-backend/functions/grant-access-to-other-resources/) +- [Add custom queries and mutations](https://docs.amplify.aws/react/build-a-backend/data/custom-business-logic/) +- [Connect to Existing Data Sources](https://docs.amplify.aws/react/build-a-backend/data/connect-to-existing-data-sources/) diff --git a/aws-amplify/steering/phase1-backend.md b/aws-amplify/steering/phase1-backend.md deleted file mode 100644 index 72c21f0..0000000 --- a/aws-amplify/steering/phase1-backend.md +++ /dev/null @@ -1,49 +0,0 @@ -# Phase 1: Backend - -Create or modify Amplify Gen 2 backend resources. - ---- - -## Prerequisites Confirmed - -Prerequisites (Node.js, npm, AWS credentials) were already validated by the orchestrator workflow. Do not re-validate. - ---- - -## Critical Constraints - -- **Do NOT create frontend scaffolding or templates during this phase.** Do not run `create-next-app`, `create-react-app`, `create-vite`, `npm create`, or any frontend project generators. This phase is strictly for Amplify backend resources (the `amplify/` directory). If a frontend project already exists, leave it untouched. If no frontend project exists and the user only asked for backend work, do NOT create one. - -- Before creating any files, ensure `.gitignore` exists in the project root and includes: - `node_modules/`, `.env*`, `amplify_outputs.json`, `.amplify/`, `dist/`, `build/`. - Create or update it if these entries are missing. - ---- - -## Retrieve and Follow the SOP - -**Do NOT write any code until you have retrieved and read the SOP.** - -Use the SOP retrieval tool to get **"amplify-backend-implementation"** and follow it completely. - -### SOP Overrides - -- **Skip the SOP's Step 1** ("Verify Dependencies") — prerequisites were already validated by the orchestrator. -- **Skip the SOP's Step 12** ("Determine Next SOP Requirements") — phase sequencing is controlled by the orchestrator workflow, not the SOP. - -Follow all other SOP steps (2 through 11) completely. Do not improvise or skip them. - -### Error Handling - -1. If you encounter an error, fix the immediate issue -2. Return to the SOP and continue from where you left off -3. Do NOT abandon the SOP or start improvising -4. If you lose track, retrieve the SOP again, identify your last completed step, and continue - ---- - -## Phase Complete - -After the SOP is fully executed, summarize what was created (which resources, files, configurations). - -**STOP HERE.** Do NOT read any other steering files. Do NOT proceed to the next phase. The orchestrator workflow will handle what comes next. diff --git a/aws-amplify/steering/phase2-sandbox.md b/aws-amplify/steering/phase2-sandbox.md deleted file mode 100644 index 46f16a2..0000000 --- a/aws-amplify/steering/phase2-sandbox.md +++ /dev/null @@ -1,47 +0,0 @@ -# Phase 2: Sandbox Deployment - -Deploy the Amplify Gen 2 backend to a sandbox environment for testing. - ---- - -## Prerequisites Confirmed - -Prerequisites (Node.js, npm, AWS credentials) were already validated by the orchestrator workflow. Do not re-validate. - ---- - -## Retrieve and Follow the SOP - -Use the SOP retrieval tool to get **"amplify-deployment-guide"** and follow it completely. - -### SOP Overrides - -- **Skip the SOP's Step 1** ("Verify Dependencies") — prerequisites were already validated by the orchestrator. -- **deployment_type is `sandbox`** — do not ask the user for the deployment type. This phase is always a sandbox deployment. -- **app_name** — infer from the project's `package.json` or existing Amplify configuration. Only ask the user if it cannot be determined. - -### SOP Parameter Mapping - -The SOP uses `deployment_type` with values `sandbox` or `cicd`. For this phase: -- deployment_type: **sandbox** - -Follow all applicable SOP steps for sandbox deployment. Do not improvise or skip them. - -### Error Handling - -1. If you encounter an error, fix the immediate issue -2. Return to the SOP and continue from where you left off -3. Do NOT abandon the SOP or start improvising -4. If you lose track, retrieve the SOP again, identify your last completed step, and continue - ---- - -## Phase Complete - -After the SOP is fully executed: - -1. Confirm deployment succeeded -2. Verify `amplify_outputs.json` exists in the project root -3. Summarize the deployment results - -**STOP HERE.** Do NOT read any other steering files. Do NOT proceed to the next phase. The orchestrator workflow will handle what comes next. diff --git a/aws-amplify/steering/phase3-frontend.md b/aws-amplify/steering/phase3-frontend.md deleted file mode 100644 index 57da093..0000000 --- a/aws-amplify/steering/phase3-frontend.md +++ /dev/null @@ -1,63 +0,0 @@ -# Phase 3: Frontend Integration & Testing - -Connect the frontend application to the Amplify Gen 2 backend and verify everything works. - ---- - -## Prerequisites Confirmed - -Prerequisites (Node.js, npm, AWS credentials) were already validated by the orchestrator workflow. Do not re-validate. - -**Required:** `amplify_outputs.json` must exist in the project root. If it does not exist, inform the user that sandbox deployment (Phase 2) must be completed first. Do NOT proceed without it. - ---- - -## Retrieve and Follow the SOP - -**Do NOT write any code until you have retrieved and read the SOP.** - -Use the SOP retrieval tool to get **"amplify-frontend-integration"** and follow it completely. - -### SOP Overrides - -- **Skip the SOP's Step 12** ("Determine Next SOP Requirements") — phase sequencing is controlled by the orchestrator workflow, not the SOP. - -Follow all other SOP steps completely. Do not improvise or skip them. - -### Error Handling - -1. If you encounter an error, fix the immediate issue -2. Return to the SOP and continue from where you left off -3. Do NOT abandon the SOP or start improvising -4. If you lose track, retrieve the SOP again, identify your last completed step, and continue - ---- - -## Local Testing - -After the SOP is fully executed, present the testing instructions to the user: - -``` -## Time to test! - -### Start your dev server -[framework-specific command, e.g., npm run dev, npx next dev, etc.] - -### Try these features -[list all features that were implemented in this session] - -Let me know how it goes — or if anything needs changes! -``` - -**Wait for the user to test and respond.** - -- If the user reports issues, fix them within this phase. Use the SOP's troubleshooting section and documentation tools as needed. After fixing, ask the user to test again. -- If the user confirms everything works (or has no further changes), proceed to phase completion below. - ---- - -## Phase Complete - -Once the user confirms testing is successful (or has no changes needed), summarize the frontend integration and testing results. - -**STOP HERE.** Do NOT read any other steering files. Do NOT proceed to the next phase. The orchestrator workflow will handle what comes next. diff --git a/aws-amplify/steering/phase4-production.md b/aws-amplify/steering/phase4-production.md deleted file mode 100644 index a483d75..0000000 --- a/aws-amplify/steering/phase4-production.md +++ /dev/null @@ -1,55 +0,0 @@ -# Phase 4: Production Deployment - -Deploy the Amplify Gen 2 application to production. - ---- - -## Prerequisites Confirmed - -Prerequisites (Node.js, npm, AWS credentials) were already validated by the orchestrator workflow. Do not re-validate. - ---- - -## Retrieve and Follow the SOP - -Use the SOP retrieval tool to get **"amplify-deployment-guide"** and follow it completely. - -### SOP Overrides - -- **Skip the SOP's Step 1** ("Verify Dependencies") — prerequisites were already validated by the orchestrator. -- **deployment_type is `cicd`** — do not ask the user for the deployment type. This phase is always a production deployment. -- **app_name** — infer from the project's `package.json` or existing Amplify configuration. Only ask the user if it cannot be determined. - -### SOP Parameter Mapping - -The SOP uses `deployment_type` with values `sandbox` or `cicd`. For this phase: -- deployment_type: **cicd** - -Follow all applicable SOP steps for CI/CD deployment. Do not improvise or skip them. - -### Error Handling - -1. If you encounter an error, fix the immediate issue -2. Return to the SOP and continue from where you left off -3. Do NOT abandon the SOP or start improvising -4. If you lose track, retrieve the SOP again, identify your last completed step, and continue - ---- - -## Phase Complete - -After the SOP is fully executed, present to the user: - -``` -## You're live! - -### Production URL -[url from deployment output] - -### Amplify Console -https://console.aws.amazon.com/amplify/home - -Your app is now deployed! Future updates: just push to your repo and it auto-deploys. -``` - -This is the final phase. The workflow is complete. diff --git a/aws-amplify/steering/scaffolding.md b/aws-amplify/steering/scaffolding.md new file mode 100644 index 0000000..9f7e861 --- /dev/null +++ b/aws-amplify/steering/scaffolding.md @@ -0,0 +1,198 @@ +# Scaffolding + +## Web — Greenfield + +You **MUST** use official starter templates. You **MUST NOT** manually +scaffold the project structure — hand-crafted structures **MAY** break +Amplify Hosting deployment detection. + +### React (Vite) + +```bash +git clone https://github.com/aws-samples/amplify-vite-react-template.git my-app +cd my-app && rm -rf .git && git init +npm install +``` + +### Next.js + +App Router (default): + +```bash +git clone https://github.com/aws-samples/amplify-next-template.git my-app +cd my-app && rm -rf .git && git init +npm install +``` + +Pages Router: + +```bash +git clone https://github.com/aws-samples/amplify-next-pages-template.git my-app +cd my-app && rm -rf .git && git init +npm install +``` + +### Vue + +```bash +git clone https://github.com/aws-samples/amplify-vue-template.git my-app +cd my-app && rm -rf .git && git init +npm install +``` + +### Angular + +```bash +git clone https://github.com/aws-samples/amplify-angular-template.git my-app +cd my-app && rm -rf .git && git init +npm install +``` + +## Web — Brownfield + +For existing web projects, add Amplify Gen2 without overwriting application +code. You **SHOULD** use the create command for automatic setup: + +```bash +npm create amplify@latest -y +``` + +You **MUST** use the `-y` flag for non-interactive execution. This +scaffolds the `amplify/` directory and installs backend dependencies. + +For monorepos or custom build pipelines where the create command conflicts, +install manually: + +```bash +npm install --save-dev @aws-amplify/backend@latest @aws-amplify/backend-cli@latest typescript +``` + +Then create `amplify/backend.ts`: + +```typescript +import { defineBackend } from '@aws-amplify/backend'; +defineBackend({}); +``` + +Install the frontend library: + +```bash +npm install aws-amplify +``` + +## Web — React Native + +### Expo + +```bash +npx --yes create-expo-app@latest my-app +cd my-app +npm create amplify@latest -y +npm install aws-amplify @aws-amplify/react-native @react-native-async-storage/async-storage +``` + +### Bare CLI + +```bash +npx --yes @react-native-community/cli init MyApp --pm npm +cd MyApp +npm create amplify@latest -y +npm install aws-amplify @aws-amplify/react-native @react-native-async-storage/async-storage +npx --yes pod-install # iOS only +``` + +You **MUST** use the `-y` flag with `npm create amplify@latest` for +non-interactive execution. + +## Mobile — Flutter + +```bash +flutter create --platforms ios,android my_app +cd my_app +npm create amplify@latest -y +``` + +Add dependencies to `pubspec.yaml`: + +```yaml +dependencies: + amplify_flutter: ^2.0.0 + amplify_auth_cognito: ^2.0.0 +``` + +Then run `flutter pub get`. + +## Mobile — Swift (Apple platforms) + +You **MUST NOT** create the Xcode project from the CLI — assume an existing +Xcode project is open in Xcode. + +1. In the project root (where `.xcodeproj` lives), run: + `npm create amplify@latest -y` +2. Add the Swift package via Xcode: File → Add Package Dependencies → + `https://github.com/aws-amplify/amplify-swift` (Up to Next Major Version). +3. Add `amplify_outputs.json` to the Xcode project (drag into navigator, + check "Copy items if needed"). + +## Mobile — Android + +You **MUST NOT** create the Android project from the CLI — assume an +existing Android Studio project. + +1. In the project root, run: `npm create amplify@latest -y` +2. Add dependencies to `app/build.gradle.kts`: + +```kotlin +dependencies { + implementation("com.amplifyframework:core:2.+") + implementation("com.amplifyframework:aws-auth-cognito:2.+") +} +``` + +3. Copy `amplify_outputs.json` into `app/src/main/res/raw/`. + +## Generate amplify_outputs + +> For mobile projects, this step must be completed before the app can build. +> Run the sandbox before opening the mobile project. + +**WARNING:** After scaffolding, you **MUST** run `npx ampx sandbox --once` +(or `npx ampx sandbox` for local dev) **before** `npm run dev`. This +generates `amplify_outputs.json`, which the frontend imports at build time. +Without it, the app fails to compile because +`import outputs from '../amplify_outputs.json'` resolves to nothing. + +```bash +# After npm install: +npx ampx sandbox --once # generates amplify_outputs.json +npm run dev # NOW the app can compile +``` + +`amplify_outputs.json` is gitignored — see [deployment.md](deployment.md) for generation details. + +## Pitfalls + +- Using the wrong template for a web framework causes broken build configs. + Always match template to framework exactly. +- Forgetting `npm create amplify@latest -y` after the framework scaffold + is the most common mistake — without it, there is no `amplify/` directory. +- **Running `npm run dev` before `npx ampx sandbox`:** The app cannot + compile without `amplify_outputs.json` — always run sandbox first. +- React Native requires `@react-native-async-storage/async-storage` — the + Amplify SDK uses it for token persistence and will fail at runtime without it. +- For Android, `amplify_outputs.json` goes in `app/src/main/res/raw/` — see [core-mobile.md](core-mobile.md). + +## Links + +- [React Quickstart](https://docs.amplify.aws/react/start/quickstart/) +- [Next.js Quickstart](https://docs.amplify.aws/nextjs/start/quickstart/) +- [Vue Quickstart](https://docs.amplify.aws/vue/start/quickstart/) +- [Angular Quickstart](https://docs.amplify.aws/angular/start/quickstart/) +- [React Native Quickstart](https://docs.amplify.aws/react-native/start/quickstart/) +- [Flutter Quickstart](https://docs.amplify.aws/flutter/start/quickstart/) +- [Swift Quickstart](https://docs.amplify.aws/swift/start/quickstart/) +- [Android Quickstart](https://docs.amplify.aws/android/start/quickstart/) +- [Manual Installation](https://docs.amplify.aws/react/start/manual-installation/) +- [Account Setup](https://docs.amplify.aws/react/start/account-setup/) +- [Sandbox Environments](https://docs.amplify.aws/react/deploy-and-host/sandbox-environments/setup/) +- [CLI Commands](https://docs.amplify.aws/react/reference/cli-commands/) diff --git a/aws-amplify/steering/storage-backend.md b/aws-amplify/steering/storage-backend.md new file mode 100644 index 0000000..d48f3f9 --- /dev/null +++ b/aws-amplify/steering/storage-backend.md @@ -0,0 +1,123 @@ +# Storage — Backend + +## Basic Setup + +Define storage in `amplify/storage/resource.ts`: + +```typescript +import { defineStorage } from '@aws-amplify/backend'; + +export const storage = defineStorage({ + name: 'myFiles', + access: (allow) => ({ + 'public/*': [ + allow.guest.to(['read']), + allow.authenticated.to(['read', 'write', 'delete']), + ], + 'protected/{entity_id}/*': [ + allow.authenticated.to(['read']), + allow.entity('identity').to(['read', 'write', 'delete']), + ], + 'private/{entity_id}/*': [ + allow.entity('identity').to(['read', 'write', 'delete']), + ], + }), +}); +``` + +Import into `amplify/backend.ts`: + +```typescript +import { defineBackend } from '@aws-amplify/backend'; +import { auth } from './auth/resource'; +import { storage } from './storage/resource'; +defineBackend({ auth, storage }); +``` + +## Access Rules + +Path patterns control who can access files. The `{entity_id}` placeholder +resolves to the authenticated user's identity ID at runtime — each user +gets an isolated directory. + +Actions: `'read'`, `'write'`, `'delete'` (granular: `'get'` and `'list'` +instead of `'read'`). Subjects: `allow.guest.to([...])`, +`allow.authenticated.to([...])`, `allow.groups(['Admins']).to([...])`, +`allow.entity('identity').to([...])`. Every rule **MUST** end with `.to()` +specifying the permitted actions — omitting `.to()` means NO permissions +are granted. + +**WARNING:** Storage access rules use `allow.guest` (PROPERTY, no +parentheses) and `allow.authenticated` (PROPERTY). Data authorization +rules use `allow.guest()` (METHOD, with parentheses). Mixing these up +causes TypeScript errors. + +**WARNING:** `{entity_id}` **MUST** be paired with +`allow.entity('identity')`. Using `{entity_id}` in a path without +`allow.entity('identity')` in that path's rules has no effect. + +Paths **MUST** end with `/*` to match all objects under that prefix. +Paths **MUST NOT** start with `/`. + +## Multiple Buckets + +```typescript +export const primaryStorage = defineStorage({ name: 'primaryFiles', isDefault: true, access: (allow) => ({ /* rules */ }) }); +export const secondaryStorage = defineStorage({ name: 'secondaryFiles', access: (allow) => ({ /* rules */ }) }); +``` + +You **MUST** set `isDefault: true` on exactly one bucket when defining +multiple. Each bucket **MUST** have a unique `name` property. The `name` +is what clients reference when targeting a non-default bucket. + +## Event Triggers + +```typescript +import { defineFunction, defineStorage } from '@aws-amplify/backend'; + +const onUploadHandler = defineFunction({ entry: './on-upload-handler.ts' }); + +export const storage = defineStorage({ + name: 'myFiles', + triggers: { onUpload: onUploadHandler, onDelete: onUploadHandler }, + access: (allow) => ({ 'public/*': [allow.authenticated.to(['read', 'write'])] }), +}); +``` + +The trigger handler receives an `S3Handler` event with bucket name and +object key. You **MUST** import the trigger function into `backend.ts`. + +Typed handler example: + +```ts +import type { S3Handler } from 'aws-lambda'; + +export const handler: S3Handler = async (event) => { + const objectKeys = event.Records.map((record) => record.s3.object.key); + console.log(`Upload handler invoked for objects [${objectKeys.join(', ')}]`); +}; +``` + +## Pitfalls + +- **Paths without `/*`:** A path like `'public'` matches nothing — you + **MUST** use `'public/*'` to match files under that prefix. +- **Missing `.to([])`:** Omitting `.to(['read', 'write'])` from an access + rule grants NO permissions — the rule is silently ignored. +- **Missing `{entity_id}`:** Using `'private/*'` instead of + `'private/{entity_id}/*'` exposes every user's private files to all + authenticated users. +- **Leading slash:** Paths **MUST NOT** start with `/` — use `'public/*'`, + not `'/public/*'`. +- **Forgetting `isDefault`:** With multiple buckets and no `isDefault: true`, + client operations fail because no default bucket is resolved. +- **`grantReadWrite()` path argument:** Do NOT pass a path argument to + `grantReadWrite(lambda)` — it operates on the whole bucket. There is no + per-path grant API. + +## Links + +- [Storage Overview](https://docs.amplify.aws/react/build-a-backend/storage/) +- [Set Up Storage](https://docs.amplify.aws/react/build-a-backend/storage/set-up-storage/) +- [Storage Authorization](https://docs.amplify.aws/react/build-a-backend/storage/authorization/) +- [Storage Event Triggers](https://docs.amplify.aws/react/build-a-backend/storage/lambda-triggers/) diff --git a/aws-amplify/steering/storage-mobile.md b/aws-amplify/steering/storage-mobile.md new file mode 100644 index 0000000..cfbe2e3 --- /dev/null +++ b/aws-amplify/steering/storage-mobile.md @@ -0,0 +1,167 @@ +# Storage — Mobile + +> **Backend required:** Storage must be defined in `amplify/storage/resource.ts` +> using `defineStorage` — see [storage-backend.md](storage-backend.md). + +## Flutter + +Imports: `amplify_flutter` + `amplify_storage_s3`. All paths wrapped with `StoragePath.fromString()`. + +| Operation | Call | +|---|---| +| Upload file | `Amplify.Storage.uploadFile(localFile: AWSFile.fromPath(path), path: const StoragePath.fromString('public/photo.jpg'))` | +| Download file | `Amplify.Storage.downloadFile(path: const StoragePath.fromString('public/photo.jpg'), localFile: localFile)` | +| List | `Amplify.Storage.list(path: const StoragePath.fromString('public/'))` → `.result.items` | +| Presigned URL | `Amplify.Storage.getUrl(path: const StoragePath.fromString('public/file.jpg'))` | +| Remove | `Amplify.Storage.remove(path: const StoragePath.fromString('public/file.jpg'))` | + +Upload progress — use the `onProgress` callback parameter: + +```dart +final op = Amplify.Storage.uploadFile( + localFile: AWSFile.fromPath('/path/to/file'), + path: const StoragePath.fromString('public/photos/photo.jpg'), + onProgress: (p) => print('fraction: ${p.fractionCompleted}'), +); +final result = await op.result; +``` + +**MUST** use `const` with `StoragePath.fromString()` for compile-time constant paths. + +## Swift (Apple platforms) + +> Supported: iOS 13+, macOS 12+, tvOS 13+, watchOS 9+, visionOS 1+ (preview). + +Uses `Amplify.Storage` with async/await. Import: `Amplify`. + +| Operation | Call | +|---|---| +| Upload data | `Amplify.Storage.uploadData(path: .fromString("public/file.txt"), data: data)` → `try await task.value` | +| Upload file | `Amplify.Storage.uploadFile(path: .fromString("public/file.txt"), local: fileUrl)` → `try await task.value` | +| Download data | `Amplify.Storage.downloadData(path: .fromString("public/file.txt"))` → `.value` returns `Data` | +| Download file | `Amplify.Storage.downloadFile(path: .fromString("public/path"), local: fileUrl)` → `try await task.value` | +| List | `try await Amplify.Storage.list(path: .fromString("public/"))` → `.items` | +| Presigned URL | `try await Amplify.Storage.getURL(path: .fromString("public/file.jpg"))` | +| Remove | `try await Amplify.Storage.remove(path: .fromString("public/file.jpg"))` | + +**Download with progress tracking:** + +```swift +let downloadTask = Amplify.Storage.downloadData( + path: .fromString("public/example.jpg") +) +Task { + for await progress in await downloadTask.progress { + print("Progress: \(progress.fractionCompleted)") + } +} +let data = try await downloadTask.value +``` + +**Upload with progress tracking:** + +```swift +let uploadTask = Amplify.Storage.uploadData( + path: .fromString("public/photo.jpg"), + data: imageData +) +Task { + for await progress in await uploadTask.progress { + print("Progress: \(progress)") + } +} +let result = try await uploadTask.value +``` + +Use SwiftUI's `PhotosPicker` (from `import PhotosUI`) to obtain image data, +then pass to `uploadData`. + +## Android (Kotlin) + +Android supports both callback-based and coroutine-based APIs. +Import: `com.amplifyframework.core.Amplify`, `com.amplifyframework.storage.StoragePath`. + +**Coroutine example (recommended):** + +```kotlin +private suspend fun uploadFile() { + val exampleFile = File(applicationContext.filesDir, "example") + exampleFile.writeText("Example file contents") + val upload = Amplify.Storage.uploadFile( + StoragePath.fromString("public/example"), exampleFile + ) + try { + val result = upload.result() + Log.i("MyAmplifyApp", "Successfully uploaded: ${result.path}") + } catch (error: StorageException) { + Log.e("MyAmplifyApp", "Upload failed", error) + } +} +``` + +```kotlin +private suspend fun downloadFile() { + val download = Amplify.Storage.downloadFile( + StoragePath.fromString("public/example"), localFile + ) + try { + val result = download.result() + Log.i("MyAmplifyApp", "Successfully downloaded: ${result.file.name}") + } catch (error: StorageException) { + Log.e("MyAmplifyApp", "Download failed", error) + } +} +``` + +| Operation (coroutine) | Call | +|---|---| +| Upload file | `Amplify.Storage.uploadFile(StoragePath.fromString("public/photo.jpg"), file)` → `.result()` | +| Upload stream | `Amplify.Storage.uploadInputStream(StoragePath.fromString("public/example"), stream)` → `.result()` | +| Download file | `Amplify.Storage.downloadFile(StoragePath.fromString("public/photo.jpg"), localFile)` → `.result()` | +| List | `Amplify.Storage.list(StoragePath.fromString("public/"))` → `.items` | +| Presigned URL | `Amplify.Storage.getUrl(StoragePath.fromString("public/file.jpg"))` → `.url` | +| Remove | `Amplify.Storage.remove(StoragePath.fromString("public/file.jpg"))` | + +**Callback alternative:** all operations also accept `onSuccess`/`onError` lambdas — e.g. +`Amplify.Storage.uploadFile(StoragePath.fromString("public/photo.jpg"), file, { result -> ... }, { error -> ... })`. + +## Permissions + +For authenticated user paths, use `protected/{entity_id}/` or `private/{entity_id}/` — the `{entity_id}` resolves to the user's Cognito identity ID at runtime. + +- **Android:** Verify `INTERNET` permission is declared in `AndroidManifest.xml` (usually present by default). If the app accesses the camera, add `CAMERA`; for gallery access, add `READ_MEDIA_IMAGES` (API 33+) or `READ_EXTERNAL_STORAGE` (older). +- **Apple (iOS/macOS):** No special permissions for S3 storage operations. If the app accesses the camera, add `NSCameraUsageDescription` in `Info.plist`. If the app accesses the photo library, add `NSPhotoLibraryUsageDescription`. +- **Flutter:** Follows Android/iOS rules above — add permissions in `AndroidManifest.xml` and `Info.plist` respectively. + +## Pitfalls + +- **Swift SDK uses `getURL` (capital URL), not `getUrl`:** Using the + wrong casing (lowercase `l`) causes compile errors. JS/web uses + `getUrl` (lowercase), but Swift uses `getURL`. +- **Wrong file wrapper per platform:** Flutter requires + `AWSFile.fromPath()`, Swift uses `Data` (for `uploadData`) or a file + URL (for `uploadFile`), Android uses `File`. Using the wrong type + causes compile errors — check the platform's expected input. +- **Missing `StoragePath.fromString()`:** Flutter and Android require + `StoragePath.fromString('path')` to wrap path strings. Passing a raw + string literal does not compile. +- **Large file uploads on mobile:** For files over 5 MB, the SDK + automatically uses multipart upload. You **SHOULD** implement + progress tracking (`onProgress` in Flutter, `for await progress in ...` + in Swift, `transferObserver` or progress callback in Android) to show + upload progress to the user. + +## Links + +- [Storage Overview (Android)](https://docs.amplify.aws/android/build-a-backend/storage/) +- [Set Up Storage (Android)](https://docs.amplify.aws/android/build-a-backend/storage/set-up-storage/) +- [Upload Files (Android)](https://docs.amplify.aws/android/frontend/storage/upload-files/) +- [Download Files (Android)](https://docs.amplify.aws/android/frontend/storage/download-files/) +- [Storage Overview (Swift)](https://docs.amplify.aws/swift/build-a-backend/storage/) +- [Set Up Storage (Swift)](https://docs.amplify.aws/swift/build-a-backend/storage/set-up-storage/) +- [Upload Files (Swift)](https://docs.amplify.aws/swift/frontend/storage/upload-files/) +- [Download Files (Swift)](https://docs.amplify.aws/swift/frontend/storage/download-files/) +- [Storage Overview (Flutter)](https://docs.amplify.aws/flutter/build-a-backend/storage/) +- [Set Up Storage (Flutter)](https://docs.amplify.aws/flutter/build-a-backend/storage/set-up-storage/) +- [Upload Files (Flutter)](https://docs.amplify.aws/flutter/frontend/storage/upload-files/) +- [Download Files (Flutter)](https://docs.amplify.aws/flutter/frontend/storage/download-files/) diff --git a/aws-amplify/steering/storage-web.md b/aws-amplify/steering/storage-web.md new file mode 100644 index 0000000..c2560dd --- /dev/null +++ b/aws-amplify/steering/storage-web.md @@ -0,0 +1,65 @@ +# Storage — Web + +> **Backend required:** Storage must be defined in `amplify/storage/resource.ts` +> using `defineStorage` — see [storage-backend.md](storage-backend.md). + +## API Reference + +All imports from `'aws-amplify/storage'`. + +| Operation | Call | +|---|---| +| Upload | `uploadData({ path: 'public/file.txt', data })` | +| Download blob | `(await downloadData({ path }).result).body.blob()` | +| Presigned URL | `await getUrl({ path })` (default 15 min expiry) | +| List | `await list({ path: 'public/' })` → `{ items }` | +| Remove | `await remove({ path })` | +| Copy | `await copy({ source: { path }, destination: { path } })` | + +`uploadData` returns a control object: `.pause()`, `.resume()`, `.cancel()`, `.result` (Promise). Progress: `options.onProgress: ({ transferredBytes, totalBytes }) => …`. + +Custom bucket: `options: { bucket: 'nameFromDefineStorage' }` or `{ bucket: { bucketName, region } }`. Raw ARN does **NOT** work. + +## React UI Components + +`npm add @aws-amplify/ui-react-storage` — you **MUST** import **BOTH** CSS files or components render unstyled: + +```typescript +import '@aws-amplify/ui-react/styles.css'; +import '@aws-amplify/ui-react-storage/styles.css'; +``` + +**WARNING:** Missing either CSS import causes unstyled components. +Training data often omits the second import. + +| Component | Import from | Key props / setup | +|---|---|---| +| `` | `@aws-amplify/ui-react-storage/browser` | `createStorageBrowser({ config: createAmplifyAuthAdapter() })` — bucket specified by name string, NOT ARN | +| `` | `@aws-amplify/ui-react-storage` | `alt`, `path` | +| `` | `@aws-amplify/ui-react-storage` | `path`, `maxFileCount`, `acceptedFileTypes` | + +## React Native + +Same JS API as web — all imports from `'aws-amplify/storage'`: + +`uploadData`, `downloadData`, `getUrl`, `list`, `remove` — identical signatures. Use `react-native-image-picker` or `expo-document-picker` for file selection. + +## Pitfalls + +- **`{entity_id}` paths:** `protected/{entity_id}/` and `private/{entity_id}/` resolve to the user's Cognito identity ID at runtime. +- **Upload cancellation:** `result.cancel()` rejects the promise — you **MUST** catch `CanceledError`. +- **Bucket option:** Accepts string name (matching `defineStorage` `name`) or `{ bucketName, region }` — raw ARN does **NOT** work. + +## Links + +- [Storage Overview (React)](https://docs.amplify.aws/react/build-a-backend/storage/) +- [Set Up Storage (React)](https://docs.amplify.aws/react/build-a-backend/storage/set-up-storage/) +- [Upload Files (React)](https://docs.amplify.aws/react/frontend/storage/upload-files/) +- [Download Files (React)](https://docs.amplify.aws/react/frontend/storage/download-files/) +- [List Files (React)](https://docs.amplify.aws/react/frontend/storage/list-files/) +- [Remove Files (React)](https://docs.amplify.aws/react/frontend/storage/remove-files/) +- [Copy Files (React)](https://docs.amplify.aws/react/frontend/storage/copy-files/) +- [Storage Overview (Next.js)](https://docs.amplify.aws/nextjs/build-a-backend/storage/) +- [Storage Overview (React Native)](https://docs.amplify.aws/react-native/build-a-backend/storage/) +- [Upload Files (React Native)](https://docs.amplify.aws/react-native/frontend/storage/upload-files/) +- [Download Files (React Native)](https://docs.amplify.aws/react-native/frontend/storage/download-files/) From 913211c4296444cad17d9847b43f1794fff808fc Mon Sep 17 00:00:00 2001 From: "H. Furkan Bozkurt" Date: Mon, 4 May 2026 08:29:40 +0000 Subject: [PATCH 02/10] fix(aws-amplify): update POWER.md MCP reference to unified AWS MCP Server The POWER.md incorrectly referenced awslabs.aws-documentation-mcp-server (a standalone server) while mcp.json correctly uses the unified AWS MCP Server via mcp-proxy-for-aws. Updated the reference to point to the official AWS MCP Server documentation. --- aws-amplify/POWER.md | 417 ++++++++-------------- aws-amplify/steering/advanced-features.md | 13 +- aws-amplify/steering/ai.md | 4 +- aws-amplify/steering/auth-backend.md | 3 +- aws-amplify/steering/auth-mobile.md | 55 ++- aws-amplify/steering/auth-web.md | 60 ++-- aws-amplify/steering/core-mobile.md | 4 +- aws-amplify/steering/core-web.md | 2 + aws-amplify/steering/data-backend.md | 12 +- aws-amplify/steering/deployment.md | 23 +- aws-amplify/steering/functions-and-api.md | 3 +- aws-amplify/steering/scaffolding.md | 16 +- aws-amplify/steering/storage-mobile.md | 48 +-- aws-amplify/steering/storage-web.md | 26 +- 14 files changed, 308 insertions(+), 378 deletions(-) diff --git a/aws-amplify/POWER.md b/aws-amplify/POWER.md index c8549f1..f0fc9a9 100644 --- a/aws-amplify/POWER.md +++ b/aws-amplify/POWER.md @@ -11,9 +11,7 @@ description: 'Build and deploy full-stack web and mobile apps with AWS Amplify G Amplify Gen2; project has amplify/ directory or amplify_outputs; code imports @aws-amplify packages; user asks about defineBackend, defineAuth, defineData, defineStorage, or npx ampx. SKIP: Amplify Gen1 (amplify CLI v6), standalone SAM/CDK without Amplify - (use aws-serverless), direct Bedrock without Amplify AI Kit (use bedrock). - - ' + (use aws-serverless), direct Bedrock without Amplify AI Kit (use bedrock).' keywords: - amplify - gen2 @@ -34,309 +32,204 @@ author: AWS # AWS Amplify Gen2 -## Overview - -AWS Amplify Gen2 is a TypeScript code-first developer experience for building full-stack web and mobile applications. All backend resources — authentication (Cognito), data (AppSync/DynamoDB), storage (S3), serverless functions (Lambda), and AI (Bedrock via Amplify AI Kit) — are defined in TypeScript under an `amplify/` directory. A single `amplify_outputs.json` file is generated to configure frontends. - -**Supported frameworks:** React (Vite), Next.js, Vue, Angular, React Native, Flutter, Swift, Android. +Build and deploy full-stack applications using AWS Amplify Gen2's TypeScript +code-first approach. This skill covers backend resource creation, frontend +integration across 8 frameworks, and deployment workflows. -**Key differences from Gen1:** No CLI wizards, no `amplify push` — everything is code-defined and deployed via `npx ampx sandbox` (dev) or `npx ampx pipeline-deploy` (CI/CD). - -## Getting Started - -### Prerequisites +## Prerequisites - Node.js ^18.19.0 || ^20.6.0 || >=22 and npm - AWS credentials configured (`aws sts get-caller-identity` succeeds) - For sandbox: `npx ampx --version` returns a valid version - For mobile: Platform-specific tooling (Xcode, Android Studio, Flutter SDK) -### Quick Start - -```bash -# Create a new Amplify Gen2 project -npm create amplify@latest - -# Start local sandbox (watches for changes, hot-deploys to AWS) -npx ampx sandbox -``` - -### Project Structure - -``` -project-root/ -├── amplify/ -│ ├── backend.ts # defineBackend({ auth, data, ... }) -│ ├── auth/resource.ts # defineAuth({ ... }) -│ ├── data/resource.ts # defineData({ schema }) -│ ├── storage/resource.ts # defineStorage({ ... }) -│ └── functions/ -│ └── my-func/ -│ ├── resource.ts # defineFunction({ ... }) -│ └── handler.ts # export const handler = ... -├── src/ # Frontend code -├── amplify_outputs.json # Generated — DO NOT edit or commit -└── package.json -``` - -### Key Packages - -| Package | Purpose | -|---------|---------| -| `@aws-amplify/backend` | `defineAuth`, `defineData`, `defineStorage`, `defineFunction`, `defineBackend` | -| `aws-amplify` | Frontend: `Amplify.configure()`, `generateClient()`, auth/data/storage APIs | -| `@aws-amplify/ui-react` | Pre-built UI: ``, `` | -| `@aws-amplify/ui-react-ai` | AI UI: ``, `useAIConversation` | - -## When to Load Steering Files - -**IMPORTANT:** Always load the appropriate steering file(s) before starting any Amplify work. Do not improvise — these files contain validated, version-specific patterns. - -### Step 0: Always Load the Core Reference First - -Before reading any feature-specific steering file, you **MUST** load the core reference for your target platform. These contain Gen2 detection, `Amplify.configure()` placement per framework, sandbox commands, required packages, and directory structure rules. +## Defaults & Assumptions -| Platform | Steering File | When | -|----------|---------------|------| -| Web (React, Next.js, Vue, Angular, React Native) | `core-web.md` | Any web/RN frontend work | -| Mobile (Flutter, Swift, Android) | `core-mobile.md` | Any native mobile frontend work | -| Backend only (no frontend) | Skip to Step 1 | No frontend changes needed | +When the user does not specify a framework: + +- **Web:** You **SHOULD** default to **React** (Vite) and explain the choice. +- **Mobile:** You **MUST** ask which platform the user wants (Flutter, + Swift, Android, or React Native). There is no universal mobile default. +- **Neither specified:** If the user says "build an app" without clarifying web + vs. mobile, you **MUST** ask before proceeding. +- **Backend only:** If only backend changes are requested and no frontend + framework is mentioned, skip the frontend integration step entirely. + +When the user does not specify tooling or strategy: + +- **Package manager:** You **SHOULD** default to **npm** unless the user + specifies yarn or pnpm. +- **Language:** You **SHOULD** default to **TypeScript**. Gen2 backends are + TypeScript-only; frontends **SHOULD** follow the project's existing language. +- **Next.js:** You **SHOULD** default to **App Router** unless the user + specifies Pages Router. +- **React Native:** Ask the user whether they use **Expo** or **bare + React Native CLI**. +- **Auth:** You **SHOULD** default to **email/password** as the login method + unless the user specifies social login, SAML, or another provider. +- **Data authorization:** default to **`publicApiKey`** + (`allow.publicApiKey()`) — this is the starter template default. When + auth is added, switch to **owner-based** (`allow.owner()`) with + `defaultAuthorizationMode: 'userPool'`. + +## Quick Start — Route to the Right Reference + +### Step 0: Read Core Reference (ALWAYS) + +You **MUST** read the core reference for your target platform **before +reading any other reference file**. These contain Gen2 detection, +`Amplify.configure()` placement per framework, sandbox commands, required +packages, and directory structure rules — patterns needed for **all** tasks, +not just new projects. + +- **Web** (React, Next.js, Vue, Angular, React Native): You **MUST** read + [core-web.md](references/core-web.md) +- **Mobile** (Flutter, Swift, Android): You **MUST** read + [core-mobile.md](references/core-mobile.md) +- **Backend only** (no frontend work): Skip to Step 1. + +### Step 1: Identify the Task Type + +| Task | Go To | +| ---------------------------------------- | ------------------------------------------------------------------------ | +| **Create a new project** | → [scaffolding.md](references/scaffolding.md), then Step 2 and/or Step 3 | +| **Add or modify a backend feature** | → Step 2 (Backend Features) | +| **Connect frontend to existing backend** | → Step 3 (Frontend Integration) | +| **Deploy the application** | → [deployment.md](references/deployment.md) | -### Step 1: Project Scaffolding +### Step 2: Backend Features -| Task | Steering File | -|------|---------------| -| Create a new Amplify Gen2 project | `scaffolding.md` → then continue to Step 2 and/or Step 3 | +You **MUST** read the corresponding reference for each backend feature: -### Step 2: Backend Features +| Feature | Reference | When to Use | +| ---------------- | ------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | +| Authentication | [auth-backend.md](references/auth-backend.md) | Email/password, social login, MFA, SAML/OIDC | +| Data Models | [data-backend.md](references/data-backend.md) | GraphQL schema, DynamoDB, relationships, auth rules | +| File Storage | [storage-backend.md](references/storage-backend.md) | S3 uploads/downloads, access rules | +| Functions & API | [functions-and-api.md](references/functions-and-api.md) | Lambda, custom resolvers, REST/HTTP APIs, calling from client | +| AI Features | [ai.md](references/ai.md) | Conversation, generation, AI tools via Bedrock _(backend config + React/Next.js frontend)_ | +| Geo, PubSub, CDK | [advanced-features.md](references/advanced-features.md) | Backend-only: custom CDK stacks, overrides, custom outputs. Backend + frontend: Geo, PubSub, Face Liveness | -Load the steering file for each backend feature you need to add or modify: +Each backend feature file is self-contained. Load only what you need. -| Feature | Steering File | Covers | -|---------|---------------|--------| -| Authentication | `auth-backend.md` | Email/password, social login, MFA, SAML/OIDC, user groups, custom attributes | -| Data Models | `data-backend.md` | GraphQL schema, DynamoDB, relationships, enum types, authorization rules | -| File Storage | `storage-backend.md` | S3 buckets, access rules (guest/authenticated/groups/entity), paths | -| Functions & APIs | `functions-and-api.md` | Lambda functions, custom resolvers, REST/HTTP APIs, environment variables | -| AI Features | `ai.md` | Conversation routes, generation routes, AI tools via Bedrock (backend + React/Next.js frontend) | -| Geo, PubSub, CDK | `advanced-features.md` | Custom CDK stacks, overrides, custom outputs, Geo, PubSub, Face Liveness | +> **Routing note:** These files apply for both **adding** and **modifying** +> features. Route to the same file whether the user says "add auth" or +> "change auth config" — each reference covers the full define surface. ### Step 3: Frontend Integration -After configuring backend resources, load the frontend steering file for your platform and feature: +After configuring backend resources, connect the frontend. Choose by +platform and feature: **Web** (React, Next.js, Vue, Angular, React Native): -| Feature | Steering File | -|---------|---------------| -| Auth UI & flows | `auth-web.md` | -| Data CRUD & subscriptions | `data-web.md` | -| Storage upload/download | `storage-web.md` | +| Feature | Reference | +| ------------------------- | ------------------------------------------- | +| Auth UI & flows | [auth-web.md](references/auth-web.md) | +| Data CRUD & subscriptions | [data-web.md](references/data-web.md) | +| Storage upload/download | [storage-web.md](references/storage-web.md) | **Mobile** (Flutter, Swift, Android): -| Feature | Steering File | -|---------|---------------| -| Auth UI & flows | `auth-mobile.md` | -| Data CRUD & subscriptions | `data-mobile.md` | -| Storage upload/download | `storage-mobile.md` | - -> **Note:** AI and Functions frontend patterns are included in `ai.md` and `functions-and-api.md` respectively — they are not split into separate web/mobile files. - -### Step 4: Deployment - -| Task | Steering File | -|------|---------------| -| Deploy to sandbox or production | `deployment.md` | +| Feature | Reference | +| ------------------------- | ------------------------------------------------- | +| Auth UI & flows | [auth-mobile.md](references/auth-mobile.md) | +| Data CRUD & subscriptions | [data-mobile.md](references/data-mobile.md) | +| Storage upload/download | [storage-mobile.md](references/storage-mobile.md) | -### Quick Routing Examples +> **Note:** AI and Functions frontend patterns are included in +> [ai.md](references/ai.md) and +> [functions-and-api.md](references/functions-and-api.md) respectively — +> they are **not** split into separate web/mobile files. -| User Says | Load | -|-----------|------| -| "Build a full-stack app" | `core-web.md` → `scaffolding.md` → backend files → frontend files → `deployment.md` | -| "Add authentication" | `auth-backend.md` (+ `core-web.md` → `auth-web.md` if frontend needed) | -| "Add a data model" | `data-backend.md` | -| "Connect my React app to Amplify" | `core-web.md` → relevant frontend files | -| "Deploy to production" | `deployment.md` | -| "Add AI chat" | `ai.md` (includes both backend and React/Next.js frontend) | -| "Build a Flutter app with auth" | `core-mobile.md` → `scaffolding.md` → `auth-backend.md` → `auth-mobile.md` | +## Core Concepts -## Available MCP Tools +### Amplify Gen2 Architecture -When AWS documentation MCP tools are available, use them to look up advanced CDK constructs, service limits, or provider-specific configuration. - -| Tool | Use For | -|------|---------| -| `search_documentation` | Find Amplify Gen2 documentation pages by topic | -| `read_documentation` | Read specific Amplify documentation pages | -| `recommend` | Get related documentation recommendations | - -**Tip:** Amplify's LLM-optimized docs are at [https://docs.amplify.aws/ai/llms.txt](https://docs.amplify.aws/ai/llms.txt) - -## Common Workflows - -### 1. Scaffold a New Full-Stack Project - -```bash -npm create amplify@latest -cd my-amplify-app -npx ampx sandbox -``` - -Load `scaffolding.md` for framework-specific setup, directory structure, and starter template details. - -### 2. Add Authentication - -In `amplify/auth/resource.ts`: -```typescript -import { defineAuth } from '@aws-amplify/backend'; - -export const auth = defineAuth({ - loginWith: { - email: true, // or phone, or external providers - }, -}); -``` +- **Code-first:** All backend resources defined in TypeScript under `amplify/` +- **Main config:** `amplify/backend.ts` imports and combines all resources via + `defineBackend()` +- **Resource files:** `amplify/auth/resource.ts`, `amplify/data/resource.ts`, + `amplify/storage/resource.ts`, `amplify/functions//resource.ts` +- **Generated output:** `amplify_outputs.json` — consumed by frontend + `Amplify.configure()`. **Gitignored** — generated by `npx ampx sandbox` + (local dev) or `npx ampx pipeline-deploy` (CI/CD), never committed. -Register in `amplify/backend.ts`: -```typescript -import { defineBackend } from '@aws-amplify/backend'; -import { auth } from './auth/resource'; +### Directory Structure -defineBackend({ auth }); ``` - -Load `auth-backend.md` for MFA, social login, SAML/OIDC, custom attributes, and user groups. - -### 3. Add a Data Model - -In `amplify/data/resource.ts`: -```typescript -import { type ClientSchema, a, defineData } from '@aws-amplify/backend'; - -const schema = a.schema({ - Todo: a.model({ - content: a.string(), - isDone: a.boolean(), - }).authorization(allow => [allow.publicApiKey()]), -}); - -export type Schema = ClientSchema; -export const data = defineData({ schema }); -``` - -Load `data-backend.md` for relationships, enum types, secondary indexes, and authorization rules. - -### 4. Deploy to Production - -```bash -# CI/CD pipeline deployment -npx ampx pipeline-deploy --branch main --app-id +project-root/ +├── amplify/ +│ ├── backend.ts # defineBackend({ auth, data, ... }) +│ ├── auth/resource.ts # defineAuth({ ... }) +│ ├── data/resource.ts # defineData({ schema }) +│ ├── storage/resource.ts # defineStorage({ ... }) +│ └── functions/ +│ └── my-func/ +│ ├── resource.ts # defineFunction({ ... }) +│ └── handler.ts # export const handler = ... +├── src/ # Frontend code +├── amplify_outputs.json # Generated — DO NOT edit or commit (gitignored) +└── package.json ``` -Load `deployment.md` for Amplify Hosting, custom CI/CD pipelines, environment management, and fullstack branch deployments. - -## Defaults & Assumptions - -When the user does not specify preferences: - -| Choice | Default | Notes | -|--------|---------|-------| -| Web framework | React (Vite) | Explain the choice; user can override | -| Mobile framework | **ASK** | No default — must ask Flutter/Swift/Android/RN | -| Package manager | npm | Unless user specifies yarn or pnpm | -| Language | TypeScript | Gen2 backends are TS-only; frontends follow project convention | -| Next.js router | App Router | Unless user specifies Pages Router | -| Auth login method | Email/password | Unless user specifies social/SAML/other | -| Data authorization | `publicApiKey` | Switch to `owner`-based when auth is added | - -**Critical:** If the user says "build an app" without specifying web vs. mobile, you **MUST** ask before proceeding. - -## Best Practices - -1. **Always use the `amplify/` directory** — all backend resources are TypeScript files under `amplify/`. Never use CLI wizards or manual AWS console configuration. - -2. **Never edit `amplify_outputs.json`** — this file is auto-generated by `npx ampx sandbox` (dev) or `npx ampx pipeline-deploy` (CI/CD). It should be in `.gitignore`. - -3. **One `defineBackend()` call** — in `amplify/backend.ts`, combine all resources into a single `defineBackend({ auth, data, storage })` call. - -4. **Use `a.schema()` for data models** — define your GraphQL schema using the type-safe `a` builder from `@aws-amplify/backend`. Avoid writing raw GraphQL SDL. - -5. **Authorization rules are mandatory** — every model needs `.authorization(allow => [...])`. Start with `allow.publicApiKey()` for prototyping, switch to `allow.owner()` or `allow.groups()` for production. - -6. **Configure Amplify once at the app root** — call `Amplify.configure(outputs)` in your root layout/entry file, never in individual components. - -7. **Use pre-built UI components** — `` from `@aws-amplify/ui-react` handles the entire auth flow. Don't build custom login forms unless you have specific requirements. - -8. **Sandbox for development** — `npx ampx sandbox` creates an isolated cloud environment per developer. Use `--identifier` for multiple sandboxes. +### Key APIs -## Troubleshooting +| Package | Purpose | +| -------------------------- | ------------------------------------------------------------------------------ | +| `@aws-amplify/backend` | `defineAuth`, `defineData`, `defineStorage`, `defineFunction`, `defineBackend` | +| `aws-amplify` | Frontend: `Amplify.configure()`, `generateClient()`, auth/data/storage APIs | +| `@aws-amplify/ui-react` | Pre-built UI: ``, `` | +| `@aws-amplify/ui-react-ai` | AI UI: ``, `useAIConversation` | -### Sandbox Won't Start +## Documentation & Resource Verification -**Symptoms:** `npx ampx sandbox` fails with credential or bootstrap errors. +When you need AWS documentation (advanced CDK constructs, service limits, +provider-specific auth config): -**Solutions:** -1. Verify AWS credentials: `aws sts get-caller-identity` -2. Bootstrap CDK if first time: `npx ampx sandbox` will prompt automatically -3. Check Node.js version: must be ^18.19.0, ^20.6.0, or >=22 -4. Ensure no other sandbox is running with the same identifier +1. **If AWS documentation tools are available (e.g., via AWS MCP)**, you **SHOULD** + use them to search and retrieve relevant documentation pages. +2. **If AWS documentation tools are unavailable**, you **MUST** fall back to web + search or the `aws` CLI for resource verification. -### "Cannot find module" or Import Errors +> **Why conditional:** Amplify Gen2 is code-first — the primary workflow is +> editing TypeScript files and running `npx ampx` commands. AWS MCP tools +> are useful for post-deployment verification but are **not** required. -**Cause:** Missing dependencies or incorrect import paths. +## Security Considerations -**Solutions:** -1. Run `npm install` to ensure all packages are installed -2. Check that `@aws-amplify/backend` is in `devDependencies` (not `dependencies`) -3. Check that `aws-amplify` is in `dependencies` for frontend code -4. Verify import paths match the package names exactly +- Use `secret()` for all credentials and API keys — never hardcode or use plain environment variables for sensitive values +- Review `allow.guest()` exposure carefully — guest access is enabled by default and grants unauthenticated users access to IAM-authorized resources +- Scope IAM policies to specific resource ARNs — avoid `resources: ['*']` in production +- Never log secrets or include them in error messages -### Data Model Authorization Errors +## Links -**Symptoms:** "Not Authorized" errors when querying or mutating data. +> All documentation links use `react` as the default platform slug. Replace `/react/` in any URL with your target framework: -**Solutions:** -1. Ensure every model has `.authorization(allow => [...])` rules -2. Check `defaultAuthorizationMode` in `defineData()` matches your auth setup -3. For authenticated access, ensure the user is signed in before making API calls -4. For `owner`-based auth, the `owner` field is auto-managed — don't set it manually +| Framework | Slug | +| ------------ | -------------- | +| React | `react` | +| Next.js | `nextjs` | +| Vue | `vue` | +| Angular | `angular` | +| React Native | `react-native` | +| Flutter | `flutter` | +| Swift | `swift` | +| Android | `android` | -### `amplify_outputs.json` Not Found - -**Cause:** Sandbox hasn't been started, or the file is gitignored (expected). - -**Solutions:** -1. Run `npx ampx sandbox` to generate the file locally -2. For CI/CD, `npx ampx pipeline-deploy` generates it during build -3. Ensure your `.gitignore` includes `amplify_outputs.json` — it should NOT be committed - -### Next.js SSR/SSG Issues - -**Symptoms:** `Amplify.configure()` errors in server components, hydration mismatches. - -**Solutions:** -1. Call `Amplify.configure()` in a client-side wrapper component (`'use client'`) -2. For server-side data access, use `generateServerClientUsingCookies()` from `aws-amplify/adapter-nextjs` -3. Never import `aws-amplify` in server components directly - -## Resources - -- [Amplify Gen2 Documentation](https://docs.amplify.aws/react/) - [Amplify Docs for LLMs](https://docs.amplify.aws/ai/llms.txt) -- [Getting Started Guide](https://docs.amplify.aws/react/start/) -- [Quickstart Tutorial](https://docs.amplify.aws/react/start/quickstart/) +- [Amplify Docs](https://docs.amplify.aws/) +- [Gen2 Docs](https://docs.amplify.aws/react/) +- [Getting Started](https://docs.amplify.aws/react/start/) +- [Quickstart](https://docs.amplify.aws/react/start/quickstart/) - [Account Setup](https://docs.amplify.aws/react/start/account-setup/) - [How Amplify Works](https://docs.amplify.aws/react/how-amplify-works/) +- [Core Concepts](https://docs.amplify.aws/react/how-amplify-works/concepts/) - [Build a Backend](https://docs.amplify.aws/react/build-a-backend/) - [Deploy and Host](https://docs.amplify.aws/react/deploy-and-host/) -- [CLI Reference](https://docs.amplify.aws/react/reference/cli-commands/) -- [Project Structure Reference](https://docs.amplify.aws/react/reference/project-structure/) -- [Amplify UI Components](https://ui.docs.amplify.aws/) -- [Amplify GitHub](https://github.com/aws-amplify) - -> All documentation links use `react` as the default platform slug. Replace `/react/` with `/nextjs/`, `/vue/`, `/angular/`, `/react-native/`, `/flutter/`, `/swift/`, or `/android/` for other frameworks. - ---- - -This power integrates with [`awslabs.aws-documentation-mcp-server@latest`](https://github.com/awslabs/mcp) for documentation search and retrieval. +- [Troubleshooting](https://docs.amplify.aws/react/build-a-backend/troubleshooting/) +- [CLI Commands](https://docs.amplify.aws/react/reference/cli-commands/) +- [Amplify Outputs](https://docs.amplify.aws/react/reference/amplify_outputs/) +- [Project Structure](https://docs.amplify.aws/react/reference/project-structure/) +- [Amplify UI](https://ui.docs.amplify.aws/) diff --git a/aws-amplify/steering/advanced-features.md b/aws-amplify/steering/advanced-features.md index 7aebc9a..33d8814 100644 --- a/aws-amplify/steering/advanced-features.md +++ b/aws-amplify/steering/advanced-features.md @@ -45,15 +45,18 @@ Real-time messaging via AWS IoT Core. Configure an IoT endpoint and attach an IAM policy for authenticated users in `amplify/backend.ts`: ```typescript -import * as iot from 'aws-cdk-lib/aws-iot'; import * as iam from 'aws-cdk-lib/aws-iam'; const pubsubStack = backend.createStack('PubSubStack'); backend.auth.resources.authenticatedUserIamRole.addToPrincipalPolicy( new iam.PolicyStatement({ - actions: ['iot:Connect', 'iot:Subscribe', 'iot:Publish', 'iot:Receive'], - resources: ['*'], + actions: ['iot:Connect', 'iot:Publish', 'iot:Subscribe', 'iot:Receive'], + resources: [ + `arn:aws:iot:*:*:client/\${cognito-identity.amazonaws.com:sub}`, + `arn:aws:iot:*:*:topic/amplify/*`, + `arn:aws:iot:*:*:topicfilter/amplify/*`, + ], }) ); @@ -197,7 +200,7 @@ backend.auth.resources.authenticatedUserIamRole.addToPrincipalPolicy( 'rekognition:StartFaceLivenessSession', 'rekognition:GetFaceLivenessSessionResults', ], - resources: ['*'], + resources: ['*'], // Rekognition session ARNs are generated at runtime — scope with conditions if needed }) ); ``` @@ -256,7 +259,7 @@ for the full Compose integration guide. ## Pitfalls - **Duplicate stack names:** `backend.createStack()` names **MUST** be - unique across the entire backend — reusing a name silently overwrites. + unique across the entire backend — reusing a name causes deployment failures. - **Missing IAM permissions:** Geo, PubSub, and Face Liveness all require explicit IAM policies — Amplify does not auto-grant access to these services. diff --git a/aws-amplify/steering/ai.md b/aws-amplify/steering/ai.md index 015a98e..59bb9f4 100644 --- a/aws-amplify/steering/ai.md +++ b/aws-amplify/steering/ai.md @@ -9,6 +9,7 @@ aiModel: a.ai.model('Claude 3.5 Sonnet v2') ``` `a.ai.model()` accepts any supported model name: + - **Anthropic**: `'Claude 3 Haiku'`, `'Claude 3 Sonnet'`, `'Claude 3 Opus'`, `'Claude 3.5 Haiku'`, `'Claude 3.5 Sonnet'`, `'Claude 3.5 Sonnet v2'`, `'Claude 3.7 Sonnet'`, `'Claude Opus 4'`, `'Claude Sonnet 4'`, `'Claude Haiku 4.5'`, `'Claude Sonnet 4.5'`, `'Claude Opus 4.5'`, `'Claude Sonnet 4.6'`, `'Claude Opus 4.6'` - **Amazon**: `'Amazon Nova Pro'`, `'Amazon Nova Lite'`, `'Amazon Nova Micro'` - **Meta**: `'Llama 3.1 405B Instruct'`, `'Llama 3.1 70B Instruct'`, `'Llama 3.1 8B Instruct'` @@ -59,6 +60,7 @@ const schema = a.schema({ ``` **CRITICAL — Authorization Constraints:** + - **Conversation routes** (`a.conversation()`) **MUST** use `allow.owner()` authorization — `allow.authenticated()` and other non-owner strategies throw a TypeError at CDK assembly time (before deployment even begins). - **Generation routes** (`a.generation()`) **MUST** use non-owner authorization (`allow.authenticated()`, `allow.guest()`, `allow.group()`, or `allow.publicApiKey()`) — `allow.owner()` throws a TypeError at CDK assembly time (before deployment even begins). @@ -120,7 +122,7 @@ npm install @aws-amplify/ui-react-ai Set up hooks and render the conversation component: ```tsx -import { generateClient } from 'aws-amplify/api'; +import { generateClient } from 'aws-amplify/data'; import { createAIHooks, AIConversation } from '@aws-amplify/ui-react-ai'; import type { Schema } from '../amplify/data/resource'; diff --git a/aws-amplify/steering/auth-backend.md b/aws-amplify/steering/auth-backend.md index 12431a3..7f1a127 100644 --- a/aws-amplify/steering/auth-backend.md +++ b/aws-amplify/steering/auth-backend.md @@ -144,6 +144,7 @@ externalProviders: { logoutUrls: ['http://localhost:3000/'], } ``` + **SAML** is NOT supported in `defineAuth` — the `ExternalProviderSpecificFactoryProps` type has no `saml` property. The lower-level `auth-construct` package supports SAML, but it was never wired up to the high-level API. Use CDK escape hatches via `backend.auth.resources` to configure SAML providers: ```typescript @@ -181,7 +182,7 @@ import { defineFunction } from '@aws-amplify/backend'; export const preSignUp = defineFunction({ name: 'pre-sign-up' }); ``` -### Guest (Unauthenticated) Access +## Guest (Unauthenticated) Access Guest access is **enabled by default** in Amplify Gen2 — the Cognito Identity Pool is created with `allowUnauthenticatedIdentities: true` automatically. diff --git a/aws-amplify/steering/auth-mobile.md b/aws-amplify/steering/auth-mobile.md index 59913f9..04e8168 100644 --- a/aws-amplify/steering/auth-mobile.md +++ b/aws-amplify/steering/auth-mobile.md @@ -10,7 +10,7 @@ handles sign-in, sign-up, MFA, social login, passwordless, password reset, and all intermediate auth states automatically. **Use it unless you need a fully custom UI.** Zero manual `signInStep` handling is required. -> **Passwordless:** The Authenticator component handles passwordless flows (email OTP, SMS OTP, and WebAuthn/passkey) automatically when configured in `defineAuth`. No custom UI code needed for passwordless authentication. On Swift/Android, use `authenticationFlow: .userChoice(preferredAuthFactor: .webAuthn)` to default to passkeys. Custom OTP/passkey flows require additional challenge handling. +> **Passwordless:** The Authenticator component handles passwordless flows (email OTP, SMS OTP, and WebAuthn/passkey) automatically when configured in `defineAuth`. No custom UI code needed for passwordless authentication. To default to passkeys, see the platform-specific "Passwordless / user-choice flow" examples below. Custom OTP/passkey flows require additional challenge handling. ### Flutter @@ -31,7 +31,7 @@ import 'package:amplify_authenticator/amplify_authenticator.dart'; import 'package:amplify_flutter/amplify_flutter.dart'; import 'package:flutter/material.dart'; -import 'amplifyconfiguration.dart'; +import 'amplify_outputs.dart'; class MyApp extends StatefulWidget { const MyApp({super.key}); @@ -49,7 +49,7 @@ class _MyAppState extends State { void _configureAmplify() async { try { await Amplify.addPlugin(AmplifyAuthCognito()); - await Amplify.configure(amplifyconfig); + await Amplify.configure(amplifyOutputs); } on Exception catch (e) { safePrint('Error configuring Amplify: $e'); } @@ -76,10 +76,10 @@ class _MyAppState extends State { **Dependencies** — add both SPM packages in Xcode (**File > Add Packages…**): -| Package | URL | Libraries | -|---|---|---| -| Amplify Library for Swift | `https://github.com/aws-amplify/amplify-swift` | `Amplify`, `AWSCognitoAuthPlugin` | -| Amplify UI Swift Authenticator | `https://github.com/aws-amplify/amplify-ui-swift-authenticator` | `Authenticator` | +| Package | URL | Libraries | +| ------------------------------ | --------------------------------------------------------------- | --------------------------------- | +| Amplify Library for Swift | `https://github.com/aws-amplify/amplify-swift` | `Amplify`, `AWSCognitoAuthPlugin` | +| Amplify UI Swift Authenticator | `https://github.com/aws-amplify/amplify-ui-swift-authenticator` | `Authenticator` | > **SPM versioning:** For both packages, select **"Up to Next Major Version"** in Xcode's dependency rule. Do NOT pin to a specific branch (e.g., `main`) — use "Up to Next Major Version" to get compatible updates automatically. @@ -129,27 +129,28 @@ Authenticator(authenticationFlow: .userChoice( ### Android (Kotlin) -**Dependencies** — add to your app's `build.gradle`: +**Dependencies** — add to your app's `build.gradle.kts`: -```groovy +```kotlin // Enable Jetpack Compose android { compileOptions { - coreLibraryDesugaringEnabled true - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + isCoreLibraryDesugaringEnabled = true + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 } - buildFeatures { compose true } - composeOptions { kotlinCompilerExtensionVersion '1.5.3' } + buildFeatures { compose = true } + composeOptions { kotlinCompilerExtensionVersion = "1.5.3" } } dependencies { - implementation 'com.amplifyframework.ui:authenticator:1.4.0' - coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' + implementation("com.amplifyframework.ui:authenticator:1.4.0") + coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.4") } ``` `INTERNET` permission is required in `AndroidManifest.xml`: + ```xml ``` @@ -216,6 +217,7 @@ import 'package:amplify_flutter/amplify_flutter.dart'; ``` **Sign in:** + ```dart final result = await Amplify.Auth.signIn( username: username, @@ -233,6 +235,7 @@ if (result.isSignedIn) { ``` **Confirm sign-in** (for MFA / challenge steps): + ```dart final result = await Amplify.Auth.confirmSignIn( confirmationValue: codeFromUser, @@ -240,6 +243,7 @@ final result = await Amplify.Auth.confirmSignIn( ``` **Sign up:** + ```dart final result = await Amplify.Auth.signUp( username: username, @@ -254,6 +258,7 @@ if (result.nextStep.signUpStep == AuthSignUpStep.confirmSignUp) { ``` **Confirm sign-up:** + ```dart await Amplify.Auth.confirmSignUp( username: username, @@ -270,6 +275,7 @@ import Amplify ``` **Sign in:** + ```swift do { let result = try await Amplify.Auth.signIn( @@ -293,6 +299,7 @@ do { ``` **Confirm sign-in:** + ```swift let result = try await Amplify.Auth.confirmSignIn( challengeResponse: codeFromUser @@ -300,6 +307,7 @@ let result = try await Amplify.Auth.confirmSignIn( ``` **Sign up:** + ```swift let options = AuthSignUpRequest.Options( userAttributes: [AuthUserAttribute(.email, value: email)] @@ -315,6 +323,7 @@ if case .confirmUser(let details, _, _) = result.nextStep { ``` **Confirm sign-up:** + ```swift try await Amplify.Auth.confirmSignUp( for: username, @@ -328,12 +337,13 @@ Android supports **both** Kotlin coroutines and callbacks. Coroutines are recommended. ```kotlin -import com.amplifyframework.core.Amplify +import com.amplifyframework.kotlin.core.Amplify import com.amplifyframework.auth.AuthUserAttributeKey import com.amplifyframework.auth.options.AuthSignUpOptions ``` **Sign in (coroutines — recommended):** + ```kotlin try { val result = Amplify.Auth.signIn("username", "password") @@ -352,7 +362,10 @@ try { ``` **Sign in (callbacks — alternative):** + ```kotlin +import com.amplifyframework.core.Amplify // Java facade for callback style + Amplify.Auth.signIn("username", "password", { result -> Log.i("Auth", "Signed in: ${result.isSignedIn}") }, { error -> Log.e("Auth", "Sign in failed", error) } @@ -360,6 +373,7 @@ Amplify.Auth.signIn("username", "password", ``` **Confirm sign-in (coroutines):** + ```kotlin try { val result = Amplify.Auth.confirmSignIn("code from user") @@ -370,6 +384,7 @@ try { ``` **Sign up (coroutines):** + ```kotlin val options = AuthSignUpOptions.builder() .userAttributes(listOf( @@ -385,6 +400,7 @@ try { ``` **Confirm sign-up (coroutines):** + ```kotlin try { Amplify.Auth.confirmSignUp("username", "123456") @@ -399,6 +415,7 @@ Social sign-in uses an OAuth web UI redirect. **Callback URLs must match** the `callbackUrls` configured in your `defineAuth` backend resource. **Flutter:** + ```dart final result = await Amplify.Auth.signInWithWebUI( provider: AuthProvider.google, @@ -406,11 +423,13 @@ final result = await Amplify.Auth.signInWithWebUI( ``` Platform setup for Flutter OAuth: + - **Android:** Add `` with your callback scheme to `MainActivity` in `AndroidManifest.xml`. - **iOS:** No additional platform configuration required. - **macOS:** Enable App Sandbox → "Incoming Connections (Server)" in Xcode. **Swift:** + ```swift let result = try await Amplify.Auth.signInWithWebUI( for: .google, @@ -421,6 +440,7 @@ let result = try await Amplify.Auth.signInWithWebUI( Platform setup: Add callback URL scheme to `Info.plist` under `CFBundleURLSchemes`. **Android (coroutines):** + ```kotlin try { val result = Amplify.Auth.signInWithSocialWebUI( @@ -433,6 +453,7 @@ try { ``` Platform setup: Add `HostedUIRedirectActivity` with your callback scheme to `AndroidManifest.xml`: + ```xml ` | `@aws-amplify/ui-react/styles.css` | -| Vue | `@aws-amplify/ui-vue` | `` | `@aws-amplify/ui-vue/styles.css` | -| Angular | `@aws-amplify/ui-angular` | `` + `AmplifyAuthenticatorModule` | `@aws-amplify/ui-angular/theme.css` | +| Framework | Package | Tag | CSS (MUST import) | +| --------------- | ------------------------- | -------------------------------------------------------- | ----------------------------------- | +| React / Next.js | `@aws-amplify/ui-react` | `` | `@aws-amplify/ui-react/styles.css` | +| Vue | `@aws-amplify/ui-vue` | `` | `@aws-amplify/ui-vue/styles.css` | +| Angular | `@aws-amplify/ui-angular` | `` + `AmplifyAuthenticatorModule` | `@aws-amplify/ui-angular/theme.css` | Props: `loginMechanisms={['email']}`, `socialProviders={['google']}`. Slot: `{({ signOut, user }) => ...}` — access `user?.signInDetails?.loginId`. @@ -21,32 +21,32 @@ Imports from `aws-amplify/auth`: `signIn`, `signUp`, `confirmSignUp`, `confirmSi After `signIn()`, you **MUST** switch on `result.nextStep.signInStep`: -| signInStep value | Action | -|---|---| -| `DONE` | Authenticated | -| `CONFIRM_SIGN_UP` | Call `confirmSignUp()` | -| `CONFIRM_SIGN_IN_WITH_TOTP_CODE` | Prompt TOTP, call `confirmSignIn({ challengeResponse })` | -| `CONFIRM_SIGN_IN_WITH_SMS_CODE` | Prompt SMS code, same | -| `CONFIRM_SIGN_IN_WITH_EMAIL_CODE` | Prompt email code, same | -| `CONTINUE_SIGN_IN_WITH_TOTP_SETUP` | Show QR URI, call `confirmSignIn()` | -| `CONTINUE_SIGN_IN_WITH_MFA_SELECTION` | `confirmSignIn({ challengeResponse: 'TOTP'\|'SMS'\|'EMAIL' })` | -| `RESET_PASSWORD` | Call `resetPassword()` | -| `CONFIRM_SIGN_IN_WITH_NEW_PASSWORD_REQUIRED` | `confirmSignIn({ challengeResponse: newPassword })` | -| `CONFIRM_SIGN_IN_WITH_CUSTOM_CHALLENGE` | `confirmSignIn({ challengeResponse })` | -| `CONFIRM_SIGN_IN_WITH_PASSWORD` | `confirmSignIn({ challengeResponse: password })` | -| `CONTINUE_SIGN_IN_WITH_MFA_SETUP_SELECTION` | `confirmSignIn({ challengeResponse: 'TOTP'\|'EMAIL' })` | -| `CONTINUE_SIGN_IN_WITH_EMAIL_SETUP` | Prompt email, call `confirmSignIn()` | -| `CONTINUE_SIGN_IN_WITH_FIRST_FACTOR_SELECTION` | `confirmSignIn({ challengeResponse: selectedFactor })` | +| signInStep value | Action | +| ---------------------------------------------- | ------------------------------------------------------------------ | +| `DONE` | Authenticated | +| `CONFIRM_SIGN_UP` | Call `confirmSignUp()` | +| `CONFIRM_SIGN_IN_WITH_TOTP_CODE` | Prompt TOTP, call `confirmSignIn({ challengeResponse })` | +| `CONFIRM_SIGN_IN_WITH_SMS_CODE` | Prompt SMS code, same | +| `CONFIRM_SIGN_IN_WITH_EMAIL_CODE` | Prompt email code, same | +| `CONTINUE_SIGN_IN_WITH_TOTP_SETUP` | Show QR URI, call `confirmSignIn()` | +| `CONTINUE_SIGN_IN_WITH_MFA_SELECTION` | `confirmSignIn({ challengeResponse: 'TOTP' \| 'SMS' \| 'EMAIL' })` | +| `RESET_PASSWORD` | Call `resetPassword()` | +| `CONFIRM_SIGN_IN_WITH_NEW_PASSWORD_REQUIRED` | `confirmSignIn({ challengeResponse: newPassword })` | +| `CONFIRM_SIGN_IN_WITH_CUSTOM_CHALLENGE` | `confirmSignIn({ challengeResponse })` | +| `CONFIRM_SIGN_IN_WITH_PASSWORD` | `confirmSignIn({ challengeResponse: password })` | +| `CONTINUE_SIGN_IN_WITH_MFA_SETUP_SELECTION` | `confirmSignIn({ challengeResponse: 'TOTP' \| 'EMAIL' })` | +| `CONTINUE_SIGN_IN_WITH_EMAIL_SETUP` | Prompt email, call `confirmSignIn()` | +| `CONTINUE_SIGN_IN_WITH_FIRST_FACTOR_SELECTION` | `confirmSignIn({ challengeResponse: selectedFactor })` | OAuth/social: `signInWithRedirect({ provider: 'Google' })`. ## Session Management -| API (from `aws-amplify/auth`) | Returns | -|---|---| -| `getCurrentUser()` | `{ userId, username, signInDetails? }` | -| `fetchAuthSession()` | `{ tokens?, credentials?, identityId?, userSub? }` — access `.tokens?.idToken`, `.tokens?.accessToken` | -| `fetchUserAttributes()` | `{ email, phone_number, ... }` | +| API (from `aws-amplify/auth`) | Returns | +| ----------------------------- | ------------------------------------------------------------------------------------------------------ | +| `getCurrentUser()` | `{ userId, username, signInDetails? }` | +| `fetchAuthSession()` | `{ tokens?, credentials?, identityId?, userSub? }` — access `.tokens?.idToken`, `.tokens?.accessToken` | +| `fetchUserAttributes()` | `{ email, phone_number, ... }` | Tokens refresh automatically. @@ -57,9 +57,9 @@ For server components and route handlers, use cookie-based auth: ```typescript import { generateServerClientUsingCookies } from '@aws-amplify/adapter-nextjs/data'; import { cookies } from 'next/headers'; -import { amplifyOutputs } from '@/amplify_outputs'; +import outputs from '@/amplify_outputs.json'; -const client = generateServerClientUsingCookies({ config: amplifyOutputs, cookies }); +const client = generateServerClientUsingCookies({ config: outputs, cookies }); ``` > **Critical:** `generateServerClientUsingCookies` from `@aws-amplify/adapter-nextjs/data` is the ONLY way to access authenticated data in Next.js server components. Do NOT use `generateClient()` on the server side — it has no access to the user's session cookies. @@ -68,9 +68,9 @@ For server actions and middleware, use `createServerRunner` from `@aws-amplify/a ```typescript import { createServerRunner } from '@aws-amplify/adapter-nextjs'; -import { amplifyOutputs } from '@/amplify_outputs'; +import outputs from '@/amplify_outputs.json'; -export const { runWithAmplifyServerContext } = createServerRunner({ config: amplifyOutputs }); +export const { runWithAmplifyServerContext } = createServerRunner({ config: outputs }); ``` ## React Native diff --git a/aws-amplify/steering/core-mobile.md b/aws-amplify/steering/core-mobile.md index ae5c93b..34dacec 100644 --- a/aws-amplify/steering/core-mobile.md +++ b/aws-amplify/steering/core-mobile.md @@ -26,7 +26,7 @@ calling configure. Reversing the order causes a runtime exception. ```dart await Amplify.addPlugins([AmplifyAuthCognito()]); -await Amplify.configure(amplifyConfig); +await Amplify.configure(amplifyOutputs); ``` **Swift:** @@ -77,7 +77,7 @@ import 'amplify_outputs.dart'; Future main() async { WidgetsFlutterBinding.ensureInitialized(); await Amplify.addPlugins([AmplifyAuthCognito()]); - await Amplify.configure(amplifyConfig); + await Amplify.configure(amplifyOutputs); runApp(const MyApp()); } ``` diff --git a/aws-amplify/steering/core-web.md b/aws-amplify/steering/core-web.md index c97ec27..e6c7821 100644 --- a/aws-amplify/steering/core-web.md +++ b/aws-amplify/steering/core-web.md @@ -50,6 +50,7 @@ import { Amplify } from 'aws-amplify'; import outputs from '../amplify_outputs.json'; Amplify.configure(outputs); ``` + **Next.js (App Router)** — `app/layout.tsx`: ```typescript @@ -67,6 +68,7 @@ import { Amplify } from 'aws-amplify'; import outputs from '../amplify_outputs.json'; Amplify.configure(outputs); ``` + **Angular** — `src/main.ts`: ```typescript diff --git a/aws-amplify/steering/data-backend.md b/aws-amplify/steering/data-backend.md index 494c108..fb1b8aa 100644 --- a/aws-amplify/steering/data-backend.md +++ b/aws-amplify/steering/data-backend.md @@ -21,10 +21,7 @@ export type Schema = ClientSchema; export const data = defineData({ schema, authorizationModes: { - defaultAuthorizationMode: 'apiKey', - apiKeyAuthorizationMode: { - expiresInDays: 30, - }, + defaultAuthorizationMode: 'userPool', }, }); ``` @@ -43,8 +40,8 @@ this export, frontend clients lose all type inference. **Field types:** `a.string()`, `a.integer()`, `a.float()`, `a.boolean()`, `a.date()`, `a.datetime()`, `a.timestamp()`, `a.time()`, `a.email()`, `a.url()`, `a.phone()`, `a.ipAddress()`, `a.json()`, `a.id()`, -`a.enum([...])`. Chain `.required()`, `.default(value)`, or `.array()` on -any field. +`a.enum([...])`. Chain `.required()` or `.array()` on any field; +`.default(value)` on scalar fields only (not enums — see Pitfalls). ## Authorization Rules @@ -57,7 +54,7 @@ call** (with parentheses). In storage access rules, `allow.guest` is a ```typescript a.model({ /* fields */ }).authorization(allow => [ allow.publicApiKey().to(['read']), // API key: public read - allow.guest().to(['read']), // Requires defaultAuthorizationMode: 'iam' — NOTE: method call () + allow.guest().to(['read']), // Requires defaultAuthorizationMode: 'iam' allow.owner(), // Creator has full CRUD allow.authenticated().to(['read']), // Any signed-in user can read allow.group('Admins'), // Named Cognito group @@ -73,6 +70,7 @@ Post: a.model({ secret: a.string().authorization(allow => [allow.owner()]), }).authorization(allow => [allow.authenticated().to(['read'])]) ``` + **Multi-owner:** Use `allow.ownersDefinedIn('editors')` with an `editors: a.string().array()` field to grant multiple users ownership. **Dynamic groups:** Use `allow.groupsDefinedIn('teamGroups')` with a diff --git a/aws-amplify/steering/deployment.md b/aws-amplify/steering/deployment.md index 13a82ca..baee251 100644 --- a/aws-amplify/steering/deployment.md +++ b/aws-amplify/steering/deployment.md @@ -12,6 +12,7 @@ Before deploying, verify: **`amplify_outputs.json` is gitignored** — it is generated at build time, NOT committed to source control: + - **Local dev:** `npx ampx sandbox` generates it automatically - **CI/CD:** `npx ampx pipeline-deploy` generates it during the build phase - **Other frontend apps in a monorepo:** Use @@ -91,13 +92,13 @@ aws amplify create-branch --app-id "$APP_ID" --branch-name main Create `amplify.yml` in the project root. Set `baseDirectory` per framework: -| Framework | baseDirectory | -|-----------|---------------| -| Vite (React/Vue) | `dist` | -| CRA | `build` | -| Next.js (export) | `out` | -| Next.js (SSR) | `.next` | -| Angular | `dist//browser` | +| Framework | baseDirectory | +| ---------------- | ----------------------------- | +| Vite (React/Vue) | `dist` | +| CRA | `build` | +| Next.js (export) | `out` | +| Next.js (SSR) | `.next` | +| Angular | `dist//browser` | **Wrong `baseDirectory` = blank page in production** (silent failure). Always match the framework table above. @@ -138,6 +139,7 @@ appRoot: packages/web `appRoot: packages/web` (correct) vs `appRoot: /packages/web` (wrong) Monorepo rules: + - Only **ONE** app runs `npx ampx pipeline-deploy`; other apps use `npx ampx generate outputs --app-id ` to get their `amplify_outputs.json`. @@ -150,6 +152,7 @@ aws amplify start-job --app-id "$APP_ID" --branch-name main --job-type RELEASE ``` ## Secrets Management + **Sandbox:** Set secrets via CLI: ```bash @@ -174,6 +177,11 @@ aws amplify update-app --app-id "$APP_ID" \ --environment-variables MY_API_KEY= ``` +> **Important:** `--environment-variables` stores values as **plain text**. +> For sensitive values (API keys, tokens), use `npx ampx sandbox secret set` +> (sandbox) or `npx ampx secret set --branch` (production) which stores in +> SSM SecureString. + Reference secrets in functions using `secret()` — see [functions-and-api.md](functions-and-api.md) for the pattern. @@ -227,6 +235,7 @@ Production URL format: `https://..amplifyapp.com` After deployment, check job status with `aws amplify list-jobs --app-id "$APP_ID" --branch-name main --query 'jobSummaries[0].status'` and verify `amplify_outputs.json` endpoints match expected values. ## Post-Deployment + **Rollback:** Revert via Git and redeploy: ```bash diff --git a/aws-amplify/steering/functions-and-api.md b/aws-amplify/steering/functions-and-api.md index 0e078a2..6b1c855 100644 --- a/aws-amplify/steering/functions-and-api.md +++ b/aws-amplify/steering/functions-and-api.md @@ -141,6 +141,7 @@ const schema = a.schema({ ``` > **When to use which:** +> > - `a.query()` / `a.mutation()` with `.handler()` — AppSync-native, type-safe, uses the data schema. **Preferred for most custom logic.** > - API Gateway + Lambda — Use when you need REST endpoints, webhooks, or third-party integrations that require a specific URL. @@ -212,7 +213,7 @@ Frontend reads custom outputs from the configured Amplify outputs. For custom queries and mutations defined via `a.query()` or `a.mutation()`, call them from the client: ```typescript -const { data } = await client.queries.myCustomQuery({ input: 'value' }); +const { data } = await client.queries.summarize({ text: '...' }); ``` For REST/HTTP API outputs added via `backend.addOutput()`, read the endpoint URL from `amplify_outputs.json` and use standard HTTP clients. diff --git a/aws-amplify/steering/scaffolding.md b/aws-amplify/steering/scaffolding.md index 9f7e861..6b86f1a 100644 --- a/aws-amplify/steering/scaffolding.md +++ b/aws-amplify/steering/scaffolding.md @@ -88,7 +88,7 @@ npm install aws-amplify npx --yes create-expo-app@latest my-app cd my-app npm create amplify@latest -y -npm install aws-amplify @aws-amplify/react-native @react-native-async-storage/async-storage +npm install aws-amplify @aws-amplify/react-native @react-native-async-storage/async-storage react-native-get-random-values ``` ### Bare CLI @@ -97,7 +97,7 @@ npm install aws-amplify @aws-amplify/react-native @react-native-async-storage/as npx --yes @react-native-community/cli init MyApp --pm npm cd MyApp npm create amplify@latest -y -npm install aws-amplify @aws-amplify/react-native @react-native-async-storage/async-storage +npm install aws-amplify @aws-amplify/react-native @react-native-async-storage/async-storage react-native-get-random-values npx --yes pod-install # iOS only ``` @@ -142,12 +142,12 @@ existing Android Studio project. 1. In the project root, run: `npm create amplify@latest -y` 2. Add dependencies to `app/build.gradle.kts`: -```kotlin -dependencies { - implementation("com.amplifyframework:core:2.+") - implementation("com.amplifyframework:aws-auth-cognito:2.+") -} -``` + ```kotlin + dependencies { + implementation("com.amplifyframework:core:2.+") + implementation("com.amplifyframework:aws-auth-cognito:2.+") + } + ``` 3. Copy `amplify_outputs.json` into `app/src/main/res/raw/`. diff --git a/aws-amplify/steering/storage-mobile.md b/aws-amplify/steering/storage-mobile.md index cfbe2e3..46a75f0 100644 --- a/aws-amplify/steering/storage-mobile.md +++ b/aws-amplify/steering/storage-mobile.md @@ -7,13 +7,13 @@ Imports: `amplify_flutter` + `amplify_storage_s3`. All paths wrapped with `StoragePath.fromString()`. -| Operation | Call | -|---|---| -| Upload file | `Amplify.Storage.uploadFile(localFile: AWSFile.fromPath(path), path: const StoragePath.fromString('public/photo.jpg'))` | -| Download file | `Amplify.Storage.downloadFile(path: const StoragePath.fromString('public/photo.jpg'), localFile: localFile)` | -| List | `Amplify.Storage.list(path: const StoragePath.fromString('public/'))` → `.result.items` | -| Presigned URL | `Amplify.Storage.getUrl(path: const StoragePath.fromString('public/file.jpg'))` | -| Remove | `Amplify.Storage.remove(path: const StoragePath.fromString('public/file.jpg'))` | +| Operation | Call | +| ------------- | ----------------------------------------------------------------------------------------------------------------------- | +| Upload file | `Amplify.Storage.uploadFile(localFile: AWSFile.fromPath(path), path: const StoragePath.fromString('public/photo.jpg'))` | +| Download file | `Amplify.Storage.downloadFile(path: const StoragePath.fromString('public/photo.jpg'), localFile: localFile)` | +| List | `Amplify.Storage.list(path: const StoragePath.fromString('public/'))` → `.result.items` | +| Presigned URL | `Amplify.Storage.getUrl(path: const StoragePath.fromString('public/file.jpg'))` | +| Remove | `Amplify.Storage.remove(path: const StoragePath.fromString('public/file.jpg'))` | Upload progress — use the `onProgress` callback parameter: @@ -34,15 +34,15 @@ final result = await op.result; Uses `Amplify.Storage` with async/await. Import: `Amplify`. -| Operation | Call | -|---|---| -| Upload data | `Amplify.Storage.uploadData(path: .fromString("public/file.txt"), data: data)` → `try await task.value` | -| Upload file | `Amplify.Storage.uploadFile(path: .fromString("public/file.txt"), local: fileUrl)` → `try await task.value` | -| Download data | `Amplify.Storage.downloadData(path: .fromString("public/file.txt"))` → `.value` returns `Data` | -| Download file | `Amplify.Storage.downloadFile(path: .fromString("public/path"), local: fileUrl)` → `try await task.value` | -| List | `try await Amplify.Storage.list(path: .fromString("public/"))` → `.items` | -| Presigned URL | `try await Amplify.Storage.getURL(path: .fromString("public/file.jpg"))` | -| Remove | `try await Amplify.Storage.remove(path: .fromString("public/file.jpg"))` | +| Operation | Call | +| ------------- | ----------------------------------------------------------------------------------------------------------- | +| Upload data | `Amplify.Storage.uploadData(path: .fromString("public/file.txt"), data: data)` → `try await task.value` | +| Upload file | `Amplify.Storage.uploadFile(path: .fromString("public/file.txt"), local: fileUrl)` → `try await task.value` | +| Download data | `Amplify.Storage.downloadData(path: .fromString("public/file.txt"))` → `.value` returns `Data` | +| Download file | `Amplify.Storage.downloadFile(path: .fromString("public/path"), local: fileUrl)` → `try await task.value` | +| List | `try await Amplify.Storage.list(path: .fromString("public/"))` → `.items` | +| Presigned URL | `try await Amplify.Storage.getURL(path: .fromString("public/file.jpg"))` | +| Remove | `try await Amplify.Storage.remove(path: .fromString("public/file.jpg"))` | **Download with progress tracking:** @@ -113,14 +113,14 @@ private suspend fun downloadFile() { } ``` -| Operation (coroutine) | Call | -|---|---| -| Upload file | `Amplify.Storage.uploadFile(StoragePath.fromString("public/photo.jpg"), file)` → `.result()` | -| Upload stream | `Amplify.Storage.uploadInputStream(StoragePath.fromString("public/example"), stream)` → `.result()` | -| Download file | `Amplify.Storage.downloadFile(StoragePath.fromString("public/photo.jpg"), localFile)` → `.result()` | -| List | `Amplify.Storage.list(StoragePath.fromString("public/"))` → `.items` | -| Presigned URL | `Amplify.Storage.getUrl(StoragePath.fromString("public/file.jpg"))` → `.url` | -| Remove | `Amplify.Storage.remove(StoragePath.fromString("public/file.jpg"))` | +| Operation (coroutine) | Call | +| --------------------- | --------------------------------------------------------------------------------------------------- | +| Upload file | `Amplify.Storage.uploadFile(StoragePath.fromString("public/photo.jpg"), file)` → `.result()` | +| Upload stream | `Amplify.Storage.uploadInputStream(StoragePath.fromString("public/example"), stream)` → `.result()` | +| Download file | `Amplify.Storage.downloadFile(StoragePath.fromString("public/photo.jpg"), localFile)` → `.result()` | +| List | `Amplify.Storage.list(StoragePath.fromString("public/"))` → `.items` | +| Presigned URL | `Amplify.Storage.getUrl(StoragePath.fromString("public/file.jpg"))` → `.url` | +| Remove | `Amplify.Storage.remove(StoragePath.fromString("public/file.jpg"))` | **Callback alternative:** all operations also accept `onSuccess`/`onError` lambdas — e.g. `Amplify.Storage.uploadFile(StoragePath.fromString("public/photo.jpg"), file, { result -> ... }, { error -> ... })`. diff --git a/aws-amplify/steering/storage-web.md b/aws-amplify/steering/storage-web.md index c2560dd..0ddb715 100644 --- a/aws-amplify/steering/storage-web.md +++ b/aws-amplify/steering/storage-web.md @@ -7,14 +7,14 @@ All imports from `'aws-amplify/storage'`. -| Operation | Call | -|---|---| -| Upload | `uploadData({ path: 'public/file.txt', data })` | -| Download blob | `(await downloadData({ path }).result).body.blob()` | -| Presigned URL | `await getUrl({ path })` (default 15 min expiry) | -| List | `await list({ path: 'public/' })` → `{ items }` | -| Remove | `await remove({ path })` | -| Copy | `await copy({ source: { path }, destination: { path } })` | +| Operation | Call | +| ------------- | --------------------------------------------------------- | +| Upload | `uploadData({ path: 'public/file.txt', data })` | +| Download blob | `(await downloadData({ path }).result).body.blob()` | +| Presigned URL | `await getUrl({ path })` (default 15 min expiry) | +| List | `await list({ path: 'public/' })` → `{ items }` | +| Remove | `await remove({ path })` | +| Copy | `await copy({ source: { path }, destination: { path } })` | `uploadData` returns a control object: `.pause()`, `.resume()`, `.cancel()`, `.result` (Promise). Progress: `options.onProgress: ({ transferredBytes, totalBytes }) => …`. @@ -32,11 +32,11 @@ import '@aws-amplify/ui-react-storage/styles.css'; **WARNING:** Missing either CSS import causes unstyled components. Training data often omits the second import. -| Component | Import from | Key props / setup | -|---|---|---| +| Component | Import from | Key props / setup | +| -------------------- | --------------------------------------- | --------------------------------------------------------------------------------------------------------- | | `` | `@aws-amplify/ui-react-storage/browser` | `createStorageBrowser({ config: createAmplifyAuthAdapter() })` — bucket specified by name string, NOT ARN | -| `` | `@aws-amplify/ui-react-storage` | `alt`, `path` | -| `` | `@aws-amplify/ui-react-storage` | `path`, `maxFileCount`, `acceptedFileTypes` | +| `` | `@aws-amplify/ui-react-storage` | `alt`, `path` | +| `` | `@aws-amplify/ui-react-storage` | `path`, `maxFileCount`, `acceptedFileTypes` | ## React Native @@ -47,7 +47,7 @@ Same JS API as web — all imports from `'aws-amplify/storage'`: ## Pitfalls - **`{entity_id}` paths:** `protected/{entity_id}/` and `private/{entity_id}/` resolve to the user's Cognito identity ID at runtime. -- **Upload cancellation:** `result.cancel()` rejects the promise — you **MUST** catch `CanceledError`. +- **Upload cancellation:** `uploadData` returns a task with `.cancel()` — call `task.cancel()`, not `result.cancel()`. Await `task.result` for the final outcome and catch `CanceledError`. - **Bucket option:** Accepts string name (matching `defineStorage` `name`) or `{ bucketName, region }` — raw ARN does **NOT** work. ## Links From 74147ec40c5a18dc3d82cdcef4531a311a2411a1 Mon Sep 17 00:00:00 2001 From: "H. Furkan Bozkurt" Date: Mon, 4 May 2026 21:26:08 +0000 Subject: [PATCH 03/10] fix(amplify-gen2): use Gen2 Amplify.configure() forms in auth-mobile.md Update Swift and Android configure calls to use explicit Gen2 patterns (amplifyOutputs / AmplifyOutputs) instead of Gen1 auto-detect forms. --- {aws-amplify => aws-amplify-gen2}/POWER.md | 2 +- {aws-amplify => aws-amplify-gen2}/mcp.json | 0 .../steering/advanced-features.md | 0 {aws-amplify => aws-amplify-gen2}/steering/ai.md | 0 {aws-amplify => aws-amplify-gen2}/steering/auth-backend.md | 0 {aws-amplify => aws-amplify-gen2}/steering/auth-mobile.md | 4 ++-- {aws-amplify => aws-amplify-gen2}/steering/auth-web.md | 0 {aws-amplify => aws-amplify-gen2}/steering/core-mobile.md | 0 {aws-amplify => aws-amplify-gen2}/steering/core-web.md | 6 ++++-- {aws-amplify => aws-amplify-gen2}/steering/data-backend.md | 0 {aws-amplify => aws-amplify-gen2}/steering/data-mobile.md | 0 {aws-amplify => aws-amplify-gen2}/steering/data-web.md | 0 {aws-amplify => aws-amplify-gen2}/steering/deployment.md | 0 .../steering/functions-and-api.md | 0 {aws-amplify => aws-amplify-gen2}/steering/scaffolding.md | 3 +++ .../steering/storage-backend.md | 0 .../steering/storage-mobile.md | 0 {aws-amplify => aws-amplify-gen2}/steering/storage-web.md | 0 18 files changed, 10 insertions(+), 5 deletions(-) rename {aws-amplify => aws-amplify-gen2}/POWER.md (99%) rename {aws-amplify => aws-amplify-gen2}/mcp.json (100%) rename {aws-amplify => aws-amplify-gen2}/steering/advanced-features.md (100%) rename {aws-amplify => aws-amplify-gen2}/steering/ai.md (100%) rename {aws-amplify => aws-amplify-gen2}/steering/auth-backend.md (100%) rename {aws-amplify => aws-amplify-gen2}/steering/auth-mobile.md (99%) rename {aws-amplify => aws-amplify-gen2}/steering/auth-web.md (100%) rename {aws-amplify => aws-amplify-gen2}/steering/core-mobile.md (100%) rename {aws-amplify => aws-amplify-gen2}/steering/core-web.md (94%) rename {aws-amplify => aws-amplify-gen2}/steering/data-backend.md (100%) rename {aws-amplify => aws-amplify-gen2}/steering/data-mobile.md (100%) rename {aws-amplify => aws-amplify-gen2}/steering/data-web.md (100%) rename {aws-amplify => aws-amplify-gen2}/steering/deployment.md (100%) rename {aws-amplify => aws-amplify-gen2}/steering/functions-and-api.md (100%) rename {aws-amplify => aws-amplify-gen2}/steering/scaffolding.md (97%) rename {aws-amplify => aws-amplify-gen2}/steering/storage-backend.md (100%) rename {aws-amplify => aws-amplify-gen2}/steering/storage-mobile.md (100%) rename {aws-amplify => aws-amplify-gen2}/steering/storage-web.md (100%) diff --git a/aws-amplify/POWER.md b/aws-amplify-gen2/POWER.md similarity index 99% rename from aws-amplify/POWER.md rename to aws-amplify-gen2/POWER.md index f0fc9a9..702d574 100644 --- a/aws-amplify/POWER.md +++ b/aws-amplify-gen2/POWER.md @@ -1,5 +1,5 @@ --- -name: aws-amplify +name: aws-amplify-gen2 displayName: Build full-stack apps with AWS Amplify Gen2 description: 'Build and deploy full-stack web and mobile apps with AWS Amplify Gen2 (TypeScript code-first). Covers auth (Cognito), data (AppSync/DynamoDB including diff --git a/aws-amplify/mcp.json b/aws-amplify-gen2/mcp.json similarity index 100% rename from aws-amplify/mcp.json rename to aws-amplify-gen2/mcp.json diff --git a/aws-amplify/steering/advanced-features.md b/aws-amplify-gen2/steering/advanced-features.md similarity index 100% rename from aws-amplify/steering/advanced-features.md rename to aws-amplify-gen2/steering/advanced-features.md diff --git a/aws-amplify/steering/ai.md b/aws-amplify-gen2/steering/ai.md similarity index 100% rename from aws-amplify/steering/ai.md rename to aws-amplify-gen2/steering/ai.md diff --git a/aws-amplify/steering/auth-backend.md b/aws-amplify-gen2/steering/auth-backend.md similarity index 100% rename from aws-amplify/steering/auth-backend.md rename to aws-amplify-gen2/steering/auth-backend.md diff --git a/aws-amplify/steering/auth-mobile.md b/aws-amplify-gen2/steering/auth-mobile.md similarity index 99% rename from aws-amplify/steering/auth-mobile.md rename to aws-amplify-gen2/steering/auth-mobile.md index 04e8168..dd8d412 100644 --- a/aws-amplify/steering/auth-mobile.md +++ b/aws-amplify-gen2/steering/auth-mobile.md @@ -96,7 +96,7 @@ struct MyApp: App { init() { do { try Amplify.add(plugin: AWSCognitoAuthPlugin()) - try Amplify.configure() + try Amplify.configure(with: .amplifyOutputs) } catch { print("Unable to configure Amplify \(error)") } @@ -160,7 +160,7 @@ dependencies { ```kotlin try { Amplify.addPlugin(AWSCognitoAuthPlugin()) - Amplify.configure(applicationContext) + Amplify.configure(AmplifyOutputs(R.raw.amplify_outputs), applicationContext) } catch (error: AmplifyException) { Log.e("MyApp", "Could not initialize Amplify", error) } diff --git a/aws-amplify/steering/auth-web.md b/aws-amplify-gen2/steering/auth-web.md similarity index 100% rename from aws-amplify/steering/auth-web.md rename to aws-amplify-gen2/steering/auth-web.md diff --git a/aws-amplify/steering/core-mobile.md b/aws-amplify-gen2/steering/core-mobile.md similarity index 100% rename from aws-amplify/steering/core-mobile.md rename to aws-amplify-gen2/steering/core-mobile.md diff --git a/aws-amplify/steering/core-web.md b/aws-amplify-gen2/steering/core-web.md similarity index 94% rename from aws-amplify/steering/core-web.md rename to aws-amplify-gen2/steering/core-web.md index e6c7821..99dd0b2 100644 --- a/aws-amplify/steering/core-web.md +++ b/aws-amplify-gen2/steering/core-web.md @@ -111,6 +111,7 @@ No plugin registration needed — configure only. ```typescript import 'react-native-get-random-values'; // MUST be first +import '@aws-amplify/react-native'; // MUST come before aws-amplify import { Amplify } from 'aws-amplify'; import outputs from './amplify_outputs.json'; Amplify.configure(outputs); @@ -120,6 +121,7 @@ Amplify.configure(outputs); ```typescript import 'react-native-get-random-values'; // MUST be first +import '@aws-amplify/react-native'; // MUST come before aws-amplify import { Amplify } from 'aws-amplify'; import outputs from './amplify_outputs.json'; Amplify.configure(outputs); @@ -133,8 +135,8 @@ Same as web — check for `amplify/` directory with `backend.ts` and ### React Native Pitfalls - **Import order:** `react-native-get-random-values` **MUST** be the FIRST - import in the entry file, before `aws-amplify`. Reversing the order causes - cryptographic failures at runtime. + import in the entry file, `@aws-amplify/react-native` **MUST** come before + `aws-amplify`. Reversing the order causes cryptographic failures at runtime. - **Missing AsyncStorage:** Without `@react-native-async-storage/async-storage`, auth tokens are not persisted and users must re-authenticate on every app restart. diff --git a/aws-amplify/steering/data-backend.md b/aws-amplify-gen2/steering/data-backend.md similarity index 100% rename from aws-amplify/steering/data-backend.md rename to aws-amplify-gen2/steering/data-backend.md diff --git a/aws-amplify/steering/data-mobile.md b/aws-amplify-gen2/steering/data-mobile.md similarity index 100% rename from aws-amplify/steering/data-mobile.md rename to aws-amplify-gen2/steering/data-mobile.md diff --git a/aws-amplify/steering/data-web.md b/aws-amplify-gen2/steering/data-web.md similarity index 100% rename from aws-amplify/steering/data-web.md rename to aws-amplify-gen2/steering/data-web.md diff --git a/aws-amplify/steering/deployment.md b/aws-amplify-gen2/steering/deployment.md similarity index 100% rename from aws-amplify/steering/deployment.md rename to aws-amplify-gen2/steering/deployment.md diff --git a/aws-amplify/steering/functions-and-api.md b/aws-amplify-gen2/steering/functions-and-api.md similarity index 100% rename from aws-amplify/steering/functions-and-api.md rename to aws-amplify-gen2/steering/functions-and-api.md diff --git a/aws-amplify/steering/scaffolding.md b/aws-amplify-gen2/steering/scaffolding.md similarity index 97% rename from aws-amplify/steering/scaffolding.md rename to aws-amplify-gen2/steering/scaffolding.md index 6b86f1a..d8ad3b9 100644 --- a/aws-amplify/steering/scaffolding.md +++ b/aws-amplify-gen2/steering/scaffolding.md @@ -166,6 +166,9 @@ Without it, the app fails to compile because # After npm install: npx ampx sandbox --once # generates amplify_outputs.json npm run dev # NOW the app can compile + +# Flutter requires the Dart output format (see core-mobile.md): +npx ampx sandbox --once --outputs-format dart --outputs-out-dir lib ``` `amplify_outputs.json` is gitignored — see [deployment.md](deployment.md) for generation details. diff --git a/aws-amplify/steering/storage-backend.md b/aws-amplify-gen2/steering/storage-backend.md similarity index 100% rename from aws-amplify/steering/storage-backend.md rename to aws-amplify-gen2/steering/storage-backend.md diff --git a/aws-amplify/steering/storage-mobile.md b/aws-amplify-gen2/steering/storage-mobile.md similarity index 100% rename from aws-amplify/steering/storage-mobile.md rename to aws-amplify-gen2/steering/storage-mobile.md diff --git a/aws-amplify/steering/storage-web.md b/aws-amplify-gen2/steering/storage-web.md similarity index 100% rename from aws-amplify/steering/storage-web.md rename to aws-amplify-gen2/steering/storage-web.md From c792a7f9b11bce4b2c25acbd3f365fb285b43d0c Mon Sep 17 00:00:00 2001 From: "H. Furkan Bozkurt" Date: Tue, 5 May 2026 10:04:19 +0000 Subject: [PATCH 04/10] chore: rename aws-amplify-gen2 to aws-amplify --- {aws-amplify-gen2 => aws-amplify}/POWER.md | 2 +- {aws-amplify-gen2 => aws-amplify}/mcp.json | 0 {aws-amplify-gen2 => aws-amplify}/steering/advanced-features.md | 0 {aws-amplify-gen2 => aws-amplify}/steering/ai.md | 0 {aws-amplify-gen2 => aws-amplify}/steering/auth-backend.md | 0 {aws-amplify-gen2 => aws-amplify}/steering/auth-mobile.md | 0 {aws-amplify-gen2 => aws-amplify}/steering/auth-web.md | 0 {aws-amplify-gen2 => aws-amplify}/steering/core-mobile.md | 0 {aws-amplify-gen2 => aws-amplify}/steering/core-web.md | 0 {aws-amplify-gen2 => aws-amplify}/steering/data-backend.md | 0 {aws-amplify-gen2 => aws-amplify}/steering/data-mobile.md | 0 {aws-amplify-gen2 => aws-amplify}/steering/data-web.md | 0 {aws-amplify-gen2 => aws-amplify}/steering/deployment.md | 0 {aws-amplify-gen2 => aws-amplify}/steering/functions-and-api.md | 0 {aws-amplify-gen2 => aws-amplify}/steering/scaffolding.md | 0 {aws-amplify-gen2 => aws-amplify}/steering/storage-backend.md | 0 {aws-amplify-gen2 => aws-amplify}/steering/storage-mobile.md | 0 {aws-amplify-gen2 => aws-amplify}/steering/storage-web.md | 0 18 files changed, 1 insertion(+), 1 deletion(-) rename {aws-amplify-gen2 => aws-amplify}/POWER.md (99%) rename {aws-amplify-gen2 => aws-amplify}/mcp.json (100%) rename {aws-amplify-gen2 => aws-amplify}/steering/advanced-features.md (100%) rename {aws-amplify-gen2 => aws-amplify}/steering/ai.md (100%) rename {aws-amplify-gen2 => aws-amplify}/steering/auth-backend.md (100%) rename {aws-amplify-gen2 => aws-amplify}/steering/auth-mobile.md (100%) rename {aws-amplify-gen2 => aws-amplify}/steering/auth-web.md (100%) rename {aws-amplify-gen2 => aws-amplify}/steering/core-mobile.md (100%) rename {aws-amplify-gen2 => aws-amplify}/steering/core-web.md (100%) rename {aws-amplify-gen2 => aws-amplify}/steering/data-backend.md (100%) rename {aws-amplify-gen2 => aws-amplify}/steering/data-mobile.md (100%) rename {aws-amplify-gen2 => aws-amplify}/steering/data-web.md (100%) rename {aws-amplify-gen2 => aws-amplify}/steering/deployment.md (100%) rename {aws-amplify-gen2 => aws-amplify}/steering/functions-and-api.md (100%) rename {aws-amplify-gen2 => aws-amplify}/steering/scaffolding.md (100%) rename {aws-amplify-gen2 => aws-amplify}/steering/storage-backend.md (100%) rename {aws-amplify-gen2 => aws-amplify}/steering/storage-mobile.md (100%) rename {aws-amplify-gen2 => aws-amplify}/steering/storage-web.md (100%) diff --git a/aws-amplify-gen2/POWER.md b/aws-amplify/POWER.md similarity index 99% rename from aws-amplify-gen2/POWER.md rename to aws-amplify/POWER.md index 702d574..f0fc9a9 100644 --- a/aws-amplify-gen2/POWER.md +++ b/aws-amplify/POWER.md @@ -1,5 +1,5 @@ --- -name: aws-amplify-gen2 +name: aws-amplify displayName: Build full-stack apps with AWS Amplify Gen2 description: 'Build and deploy full-stack web and mobile apps with AWS Amplify Gen2 (TypeScript code-first). Covers auth (Cognito), data (AppSync/DynamoDB including diff --git a/aws-amplify-gen2/mcp.json b/aws-amplify/mcp.json similarity index 100% rename from aws-amplify-gen2/mcp.json rename to aws-amplify/mcp.json diff --git a/aws-amplify-gen2/steering/advanced-features.md b/aws-amplify/steering/advanced-features.md similarity index 100% rename from aws-amplify-gen2/steering/advanced-features.md rename to aws-amplify/steering/advanced-features.md diff --git a/aws-amplify-gen2/steering/ai.md b/aws-amplify/steering/ai.md similarity index 100% rename from aws-amplify-gen2/steering/ai.md rename to aws-amplify/steering/ai.md diff --git a/aws-amplify-gen2/steering/auth-backend.md b/aws-amplify/steering/auth-backend.md similarity index 100% rename from aws-amplify-gen2/steering/auth-backend.md rename to aws-amplify/steering/auth-backend.md diff --git a/aws-amplify-gen2/steering/auth-mobile.md b/aws-amplify/steering/auth-mobile.md similarity index 100% rename from aws-amplify-gen2/steering/auth-mobile.md rename to aws-amplify/steering/auth-mobile.md diff --git a/aws-amplify-gen2/steering/auth-web.md b/aws-amplify/steering/auth-web.md similarity index 100% rename from aws-amplify-gen2/steering/auth-web.md rename to aws-amplify/steering/auth-web.md diff --git a/aws-amplify-gen2/steering/core-mobile.md b/aws-amplify/steering/core-mobile.md similarity index 100% rename from aws-amplify-gen2/steering/core-mobile.md rename to aws-amplify/steering/core-mobile.md diff --git a/aws-amplify-gen2/steering/core-web.md b/aws-amplify/steering/core-web.md similarity index 100% rename from aws-amplify-gen2/steering/core-web.md rename to aws-amplify/steering/core-web.md diff --git a/aws-amplify-gen2/steering/data-backend.md b/aws-amplify/steering/data-backend.md similarity index 100% rename from aws-amplify-gen2/steering/data-backend.md rename to aws-amplify/steering/data-backend.md diff --git a/aws-amplify-gen2/steering/data-mobile.md b/aws-amplify/steering/data-mobile.md similarity index 100% rename from aws-amplify-gen2/steering/data-mobile.md rename to aws-amplify/steering/data-mobile.md diff --git a/aws-amplify-gen2/steering/data-web.md b/aws-amplify/steering/data-web.md similarity index 100% rename from aws-amplify-gen2/steering/data-web.md rename to aws-amplify/steering/data-web.md diff --git a/aws-amplify-gen2/steering/deployment.md b/aws-amplify/steering/deployment.md similarity index 100% rename from aws-amplify-gen2/steering/deployment.md rename to aws-amplify/steering/deployment.md diff --git a/aws-amplify-gen2/steering/functions-and-api.md b/aws-amplify/steering/functions-and-api.md similarity index 100% rename from aws-amplify-gen2/steering/functions-and-api.md rename to aws-amplify/steering/functions-and-api.md diff --git a/aws-amplify-gen2/steering/scaffolding.md b/aws-amplify/steering/scaffolding.md similarity index 100% rename from aws-amplify-gen2/steering/scaffolding.md rename to aws-amplify/steering/scaffolding.md diff --git a/aws-amplify-gen2/steering/storage-backend.md b/aws-amplify/steering/storage-backend.md similarity index 100% rename from aws-amplify-gen2/steering/storage-backend.md rename to aws-amplify/steering/storage-backend.md diff --git a/aws-amplify-gen2/steering/storage-mobile.md b/aws-amplify/steering/storage-mobile.md similarity index 100% rename from aws-amplify-gen2/steering/storage-mobile.md rename to aws-amplify/steering/storage-mobile.md diff --git a/aws-amplify-gen2/steering/storage-web.md b/aws-amplify/steering/storage-web.md similarity index 100% rename from aws-amplify-gen2/steering/storage-web.md rename to aws-amplify/steering/storage-web.md From d010c100bd19e4640696bdbb7b9fe62b2867f4f2 Mon Sep 17 00:00:00 2001 From: "H. Furkan Bozkurt" Date: Tue, 5 May 2026 19:00:05 +0000 Subject: [PATCH 05/10] chore: sync aws-amplify steering to latest source --- aws-amplify/steering/auth-backend.md | 2 ++ aws-amplify/steering/data-backend.md | 2 ++ aws-amplify/steering/storage-web.md | 2 ++ 3 files changed, 6 insertions(+) diff --git a/aws-amplify/steering/auth-backend.md b/aws-amplify/steering/auth-backend.md index 7f1a127..2bc9f46 100644 --- a/aws-amplify/steering/auth-backend.md +++ b/aws-amplify/steering/auth-backend.md @@ -197,6 +197,8 @@ export const data = defineData({ }); ``` +> **Security:** Guest access grants unauthenticated users IAM-authorized access. For production, explicitly evaluate whether guest access is needed and prefer `allow.authenticated()` as the default. If guest access is required, scope it to read-only on non-sensitive models only. + To **disable** guest access, use a CDK override in `backend.ts`: ```typescript diff --git a/aws-amplify/steering/data-backend.md b/aws-amplify/steering/data-backend.md index fb1b8aa..05fa0d5 100644 --- a/aws-amplify/steering/data-backend.md +++ b/aws-amplify/steering/data-backend.md @@ -62,6 +62,8 @@ a.model({ /* fields */ }).authorization(allow => [ ]) ``` +> **Security note:** `allow.guest()` permits unauthenticated access. Only use for intentionally public, non-sensitive data. Prefer `allow.authenticated()` or `allow.owner()` for sensitive resources. + Per-field authorization overrides model-level rules: ```typescript diff --git a/aws-amplify/steering/storage-web.md b/aws-amplify/steering/storage-web.md index 0ddb715..e52876e 100644 --- a/aws-amplify/steering/storage-web.md +++ b/aws-amplify/steering/storage-web.md @@ -16,6 +16,8 @@ All imports from `'aws-amplify/storage'`. | Remove | `await remove({ path })` | | Copy | `await copy({ source: { path }, destination: { path } })` | +> **Security:** Amplify Gen2 enables S3 server-side encryption (SSE-S3) by default. For sensitive data, consider configuring SSE-KMS with a customer-managed key via CDK overrides. + `uploadData` returns a control object: `.pause()`, `.resume()`, `.cancel()`, `.result` (Promise). Progress: `options.onProgress: ({ transferredBytes, totalBytes }) => …`. Custom bucket: `options: { bucket: 'nameFromDefineStorage' }` or `{ bucket: { bucketName, region } }`. Raw ARN does **NOT** work. From 896cd6d3af0bcc9c7487265064a3e25860029dd3 Mon Sep 17 00:00:00 2001 From: "H. Furkan Bozkurt" Date: Tue, 5 May 2026 20:01:25 +0000 Subject: [PATCH 06/10] chore: address remaining AutoSDE security guidelines --- aws-amplify/steering/data-backend.md | 2 +- aws-amplify/steering/deployment.md | 2 ++ aws-amplify/steering/storage-web.md | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/aws-amplify/steering/data-backend.md b/aws-amplify/steering/data-backend.md index 05fa0d5..fcb2faf 100644 --- a/aws-amplify/steering/data-backend.md +++ b/aws-amplify/steering/data-backend.md @@ -62,7 +62,7 @@ a.model({ /* fields */ }).authorization(allow => [ ]) ``` -> **Security note:** `allow.guest()` permits unauthenticated access. Only use for intentionally public, non-sensitive data. Prefer `allow.authenticated()` or `allow.owner()` for sensitive resources. +> **Security note:** `allow.guest()` permits unauthenticated access. Only use for intentionally public, non-sensitive data. Prefer `allow.authenticated()` or `allow.owner()` for sensitive resources. See [Amplify authorization best practices](https://docs.amplify.aws/react/build-a-backend/data/customize-authz/) and [Amazon Cognito Identity Pool security](https://docs.aws.amazon.com/cognito/latest/developerguide/identity-pools.html) for guidance on choosing the right authorization strategy. Per-field authorization overrides model-level rules: diff --git a/aws-amplify/steering/deployment.md b/aws-amplify/steering/deployment.md index baee251..5fc415b 100644 --- a/aws-amplify/steering/deployment.md +++ b/aws-amplify/steering/deployment.md @@ -182,6 +182,8 @@ aws amplify update-app --app-id "$APP_ID" \ > (sandbox) or `npx ampx secret set --branch` (production) which stores in > SSM SecureString. +> **Note:** Under the hood, Amplify Gen2 `secret()` references are backed by AWS Systems Manager Parameter Store (SecureString parameters). Review access policies on the `/amplify/` parameter path in your account to ensure only authorized roles can read production secrets. + Reference secrets in functions using `secret()` — see [functions-and-api.md](functions-and-api.md) for the pattern. diff --git a/aws-amplify/steering/storage-web.md b/aws-amplify/steering/storage-web.md index e52876e..baf3e02 100644 --- a/aws-amplify/steering/storage-web.md +++ b/aws-amplify/steering/storage-web.md @@ -16,7 +16,7 @@ All imports from `'aws-amplify/storage'`. | Remove | `await remove({ path })` | | Copy | `await copy({ source: { path }, destination: { path } })` | -> **Security:** Amplify Gen2 enables S3 server-side encryption (SSE-S3) by default. For sensitive data, consider configuring SSE-KMS with a customer-managed key via CDK overrides. +> **Security:** Amplify Gen2 enables S3 server-side encryption (SSE-S3) by default. For sensitive data, consider configuring SSE-KMS with a customer-managed key via CDK overrides. Amplify also enforces HTTPS-only access to S3 buckets by default; if using custom bucket configurations, add a bucket policy with `"aws:SecureTransport": "false"` → Deny to ensure encryption in transit. `uploadData` returns a control object: `.pause()`, `.resume()`, `.cancel()`, `.result` (Promise). Progress: `options.onProgress: ({ transferredBytes, totalBytes }) => …`. From c2e3cf795ea6b6727f57d1e0f10231e3751d93b5 Mon Sep 17 00:00:00 2001 From: "H. Furkan Bozkurt" Date: Tue, 5 May 2026 20:16:55 +0000 Subject: [PATCH 07/10] chore: address AutoSDE iteration 3 --- aws-amplify/steering/ai.md | 2 ++ aws-amplify/steering/data-backend.md | 2 +- aws-amplify/steering/deployment.md | 7 +++---- aws-amplify/steering/storage-mobile.md | 2 ++ 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/aws-amplify/steering/ai.md b/aws-amplify/steering/ai.md index 59bb9f4..98fbac4 100644 --- a/aws-amplify/steering/ai.md +++ b/aws-amplify/steering/ai.md @@ -67,6 +67,8 @@ const schema = a.schema({ These constraints are asymmetric and frequently confused. Getting them wrong causes the CDK synthesis to fail with a non-obvious TypeError. +> **Security:** Conversation history sent to Amazon Bedrock may contain PII. Do not log full request/response payloads in production. Enable CloudWatch Logs encryption (KMS) and set appropriate retention policies for any logs that may capture inference data. + ### Backend Integration AI conversation and generation routes are part of your data schema. Import into `amplify/backend.ts`: diff --git a/aws-amplify/steering/data-backend.md b/aws-amplify/steering/data-backend.md index fcb2faf..1239a54 100644 --- a/aws-amplify/steering/data-backend.md +++ b/aws-amplify/steering/data-backend.md @@ -62,7 +62,7 @@ a.model({ /* fields */ }).authorization(allow => [ ]) ``` -> **Security note:** `allow.guest()` permits unauthenticated access. Only use for intentionally public, non-sensitive data. Prefer `allow.authenticated()` or `allow.owner()` for sensitive resources. See [Amplify authorization best practices](https://docs.amplify.aws/react/build-a-backend/data/customize-authz/) and [Amazon Cognito Identity Pool security](https://docs.aws.amazon.com/cognito/latest/developerguide/identity-pools.html) for guidance on choosing the right authorization strategy. +> **Security note:** `allow.guest()` and `allow.publicApiKey()` both permit unauthenticated access. Only use for intentionally public, non-sensitive data. Prefer `allow.authenticated()` or `allow.owner()` for sensitive resources. See [Amplify authorization best practices](https://docs.amplify.aws/react/build-a-backend/data/customize-authz/) and [Amazon Cognito Identity Pool security](https://docs.aws.amazon.com/cognito/latest/developerguide/identity-pools.html) for guidance on choosing the right authorization strategy. Per-field authorization overrides model-level rules: diff --git a/aws-amplify/steering/deployment.md b/aws-amplify/steering/deployment.md index 5fc415b..fcbdd0c 100644 --- a/aws-amplify/steering/deployment.md +++ b/aws-amplify/steering/deployment.md @@ -156,11 +156,10 @@ aws amplify start-job --app-id "$APP_ID" --branch-name main --job-type RELEASE **Sandbox:** Set secrets via CLI: ```bash -echo "" | npx ampx sandbox secret set MY_API_KEY +npx ampx sandbox secret set MY_API_KEY ``` -You **MUST** pipe the value via stdin — without the pipe, the command -prompts interactively. +> **Security:** Avoid passing secret values as CLI arguments or via `echo` — these appear in shell history and `/proc`. Instead, use `ampx sandbox secret set MY_SECRET` which prompts for input interactively, or pipe from a secure source: `aws ssm get-parameter --name /path/to/secret --with-decryption --query Parameter.Value --output text | ampx sandbox secret set MY_SECRET --from-stdin` This stores the secret for your personal sandbox environment. **Branch environments (production):** Set secrets via the `ampx` CLI: @@ -181,7 +180,7 @@ aws amplify update-app --app-id "$APP_ID" \ > For sensitive values (API keys, tokens), use `npx ampx sandbox secret set` > (sandbox) or `npx ampx secret set --branch` (production) which stores in > SSM SecureString. - +> > **Note:** Under the hood, Amplify Gen2 `secret()` references are backed by AWS Systems Manager Parameter Store (SecureString parameters). Review access policies on the `/amplify/` parameter path in your account to ensure only authorized roles can read production secrets. Reference secrets in functions using `secret()` — see diff --git a/aws-amplify/steering/storage-mobile.md b/aws-amplify/steering/storage-mobile.md index 46a75f0..c432b1d 100644 --- a/aws-amplify/steering/storage-mobile.md +++ b/aws-amplify/steering/storage-mobile.md @@ -15,6 +15,8 @@ Imports: `amplify_flutter` + `amplify_storage_s3`. All paths wrapped with `Stora | Presigned URL | `Amplify.Storage.getUrl(path: const StoragePath.fromString('public/file.jpg'))` | | Remove | `Amplify.Storage.remove(path: const StoragePath.fromString('public/file.jpg'))` | +> **Security:** Amplify Gen2 enables S3 server-side encryption (SSE-S3) by default. All transfers use HTTPS (TLS in transit). For sensitive data, configure SSE-KMS with a customer-managed key via CDK overrides. + Upload progress — use the `onProgress` callback parameter: ```dart From cf21b924e3303b8ad070b68a3f2b01d0b8d8ba2d Mon Sep 17 00:00:00 2001 From: "H. Furkan Bozkurt" Date: Wed, 6 May 2026 08:13:52 +0000 Subject: [PATCH 08/10] chore: sync aws-amplify skill from preamp --- aws-amplify/steering/deployment.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws-amplify/steering/deployment.md b/aws-amplify/steering/deployment.md index fcbdd0c..57e6b21 100644 --- a/aws-amplify/steering/deployment.md +++ b/aws-amplify/steering/deployment.md @@ -159,7 +159,7 @@ aws amplify start-job --app-id "$APP_ID" --branch-name main --job-type RELEASE npx ampx sandbox secret set MY_API_KEY ``` -> **Security:** Avoid passing secret values as CLI arguments or via `echo` — these appear in shell history and `/proc`. Instead, use `ampx sandbox secret set MY_SECRET` which prompts for input interactively, or pipe from a secure source: `aws ssm get-parameter --name /path/to/secret --with-decryption --query Parameter.Value --output text | ampx sandbox secret set MY_SECRET --from-stdin` +> **Security:** Avoid passing secret values as CLI arguments or via `echo` — these appear in shell history and `/proc`. Instead, use `npx ampx sandbox secret set MY_SECRET` which prompts for input interactively, or pipe from a secure source: `aws ssm get-parameter --name /path/to/secret --with-decryption --query Parameter.Value --output text | npx ampx sandbox secret set MY_SECRET --from-stdin` This stores the secret for your personal sandbox environment. **Branch environments (production):** Set secrets via the `ampx` CLI: From 1ec641e28e3e66ff0aa6ea5935178ffcbea33565 Mon Sep 17 00:00:00 2001 From: "H. Furkan Bozkurt" Date: Wed, 6 May 2026 12:19:54 +0000 Subject: [PATCH 09/10] chore: always ask for auth method per review feedback --- aws-amplify/POWER.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aws-amplify/POWER.md b/aws-amplify/POWER.md index f0fc9a9..3a97f28 100644 --- a/aws-amplify/POWER.md +++ b/aws-amplify/POWER.md @@ -65,8 +65,8 @@ When the user does not specify tooling or strategy: specifies Pages Router. - **React Native:** Ask the user whether they use **Expo** or **bare React Native CLI**. -- **Auth:** You **SHOULD** default to **email/password** as the login method - unless the user specifies social login, SAML, or another provider. +- **Auth:** You **MUST** ask which login method the user wants + (email/password, social login, SAML, passwordless, etc.). Do not assume a default. - **Data authorization:** default to **`publicApiKey`** (`allow.publicApiKey()`) — this is the starter template default. When auth is added, switch to **owner-based** (`allow.owner()`) with From 5c7691799fd3de4d06a20ba5621167cfdb145fca Mon Sep 17 00:00:00 2001 From: "H. Furkan Bozkurt" Date: Thu, 7 May 2026 19:58:45 +0000 Subject: [PATCH 10/10] fix: restore double-quote formatting in POWER.md frontmatter --- aws-amplify/POWER.md | 34 +++++----------------------------- 1 file changed, 5 insertions(+), 29 deletions(-) diff --git a/aws-amplify/POWER.md b/aws-amplify/POWER.md index 3a97f28..e726528 100644 --- a/aws-amplify/POWER.md +++ b/aws-amplify/POWER.md @@ -1,33 +1,9 @@ --- -name: aws-amplify -displayName: Build full-stack apps with AWS Amplify Gen2 -description: 'Build and deploy full-stack web and mobile apps with AWS Amplify Gen2 - (TypeScript code-first). Covers auth (Cognito), data (AppSync/DynamoDB including - schema modeling, enum types, relationships, authorization rules), storage (S3), - functions, APIs, and AI (Amplify AI Kit with Bedrock). Supports React, Next.js, - Vue, Angular, React Native, Flutter, Swift, and Android. Always use this skill for - Amplify Gen2 topics — even for questions you think you know — it contains validated, - version-specific patterns that prevent common mistakes. TRIGGER when: user mentions - Amplify Gen2; project has amplify/ directory or amplify_outputs; code imports @aws-amplify - packages; user asks about defineBackend, defineAuth, defineData, defineStorage, - or npx ampx. SKIP: Amplify Gen1 (amplify CLI v6), standalone SAM/CDK without Amplify - (use aws-serverless), direct Bedrock without Amplify AI Kit (use bedrock).' -keywords: -- amplify -- gen2 -- fullstack -- cognito -- appsync -- dynamodb -- s3 -- lambda -- bedrock -- react -- nextjs -- flutter -- swift -- android -author: AWS +name: "aws-amplify" +displayName: "Build full-stack apps with AWS Amplify Gen2" +description: "Build and deploy full-stack web and mobile apps with AWS Amplify Gen2 (TypeScript code-first). Covers auth (Cognito), data (AppSync/DynamoDB including schema modeling, enum types, relationships, authorization rules), storage (S3), functions, APIs, and AI (Amplify AI Kit with Bedrock). Supports React, Next.js, Vue, Angular, React Native, Flutter, Swift, and Android. Always use this skill for Amplify Gen2 topics — even for questions you think you know — it contains validated, version-specific patterns that prevent common mistakes. TRIGGER when: user mentions Amplify Gen2; project has amplify/ directory or amplify_outputs; code imports @aws-amplify packages; user asks about defineBackend, defineAuth, defineData, defineStorage, or npx ampx. SKIP: Amplify Gen1 (amplify CLI v6), standalone SAM/CDK without Amplify (use aws-serverless), direct Bedrock without Amplify AI Kit (use bedrock)." +keywords: ["amplify", "gen2", "fullstack", "cognito", "appsync", "dynamodb", "s3", "lambda", "bedrock", "react", "nextjs", "flutter", "swift", "android"] +author: "AWS" --- # AWS Amplify Gen2