AWS Amplify
AWS Amplify BaaS with AppSync GraphQL, Cognito auth, S3 storage, and Lambda functions
You are an expert in AWS Amplify for rapid backend development, including AppSync (GraphQL), Cognito authentication, S3 storage, Lambda functions, and Amplify Hosting.
## Key Points
- Use `npx ampx sandbox` during development — it creates an isolated cloud sandbox per developer, avoiding conflicts in shared environments.
- Define authorization rules on every model using the `.authorization()` chain; combine `allow.owner()` with `allow.publicApiKey().to(['read'])` for common blog-like patterns.
- Use Amplify Gen 2's typed client (`generateClient<Schema>()`) for full type safety from schema to frontend.
- Leverage `a.belongsTo()` and `a.hasMany()` relationships to get efficient GraphQL resolvers with DynamoDB single-table design.
- Store secrets using Amplify's secret management (`npx ampx sandbox secret set`) rather than hardcoding in resource files.
- **Missing authorization rules**: Models without `.authorization()` are inaccessible by default — unlike some other BaaS platforms, Amplify defaults to deny.
- **Gen 1 vs. Gen 2 confusion**: Gen 2 uses TypeScript definitions (no `amplify push` CLI prompts); do not mix Gen 1 CLI commands with a Gen 2 project.
- **Cold starts**: Lambda functions behind AppSync experience cold starts; use provisioned concurrency for latency-sensitive resolvers.
- **Large file uploads**: Use the `uploadData` API which handles multipart uploads automatically; do not try to send large files through AppSync mutations.
## Quick Example
```typescript
// src/main.ts
import { Amplify } from 'aws-amplify';
import outputs from '../amplify_outputs.json';
Amplify.configure(outputs);
```skilldb get baas-skills/AWS AmplifyFull skill: 330 linesAWS Amplify — Backend as a Service
You are an expert in AWS Amplify for rapid backend development, including AppSync (GraphQL), Cognito authentication, S3 storage, Lambda functions, and Amplify Hosting.
Core Philosophy
Overview
AWS Amplify is a set of tools and services for building full-stack applications on AWS. Amplify Gen 2 uses a TypeScript-first, code-based approach to define backends. It provisions AWS AppSync (managed GraphQL), Amazon Cognito (authentication), Amazon S3 (storage), AWS Lambda (functions), and Amazon DynamoDB (database) — all configured through TypeScript code rather than CLI wizards. Amplify Hosting provides CI/CD and global CDN deployment for frontends.
Setup & Configuration
Project Setup (Gen 2)
# Create a new Amplify project
npm create amplify@latest
# Or add to existing project
npm install aws-amplify @aws-amplify/backend
npx ampx sandbox # start local cloud sandbox
Client Configuration
// src/main.ts
import { Amplify } from 'aws-amplify';
import outputs from '../amplify_outputs.json';
Amplify.configure(outputs);
Backend Definition Structure
amplify/
auth/
resource.ts # Cognito configuration
data/
resource.ts # AppSync + DynamoDB schema
storage/
resource.ts # S3 configuration
functions/
my-function/
resource.ts # Lambda configuration
handler.ts # Lambda code
backend.ts # Combines all resources
Backend Entry Point
// amplify/backend.ts
import { defineBackend } from '@aws-amplify/backend';
import { auth } from './auth/resource';
import { data } from './data/resource';
import { storage } from './storage/resource';
defineBackend({
auth,
data,
storage,
});
Core Patterns
Authentication (Cognito)
// amplify/auth/resource.ts
import { defineAuth } from '@aws-amplify/backend';
export const auth = defineAuth({
loginWith: {
email: true,
externalProviders: {
google: {
clientId: 'google-client-id',
clientSecret: 'google-client-secret',
},
callbackUrls: ['http://localhost:3000/'],
logoutUrls: ['http://localhost:3000/'],
},
},
userAttributes: {
preferredUsername: { required: true },
},
});
// Client-side auth
import { signUp, signIn, signOut, getCurrentUser, fetchAuthSession } from 'aws-amplify/auth';
// Sign up
await signUp({
username: 'user@example.com',
password: 'secure-password',
options: {
userAttributes: { preferred_username: 'alice' },
},
});
// Sign in
const { isSignedIn } = await signIn({
username: 'user@example.com',
password: 'secure-password',
});
// Get current user
const { username, userId } = await getCurrentUser();
// Get session/tokens
const { tokens } = await fetchAuthSession();
console.log(tokens?.idToken?.toString());
// Sign out
await signOut();
// React: Authenticator component
import { Authenticator } from '@aws-amplify/ui-react';
import '@aws-amplify/ui-react/styles.css';
function App() {
return (
<Authenticator>
{({ signOut, user }) => (
<div>
<p>Welcome {user.username}</p>
<button onClick={signOut}>Sign out</button>
</div>
)}
</Authenticator>
);
}
Data (AppSync + DynamoDB)
// amplify/data/resource.ts
import { defineData, a, type ClientSchema } from '@aws-amplify/backend';
const schema = a.schema({
Post: a.model({
title: a.string().required(),
body: a.string().required(),
published: a.boolean().default(false),
author: a.belongsTo('User', 'authorId'),
authorId: a.id(),
comments: a.hasMany('Comment', 'postId'),
}).authorization((allow) => [
allow.publicApiKey().to(['read']),
allow.owner(),
]),
Comment: a.model({
body: a.string().required(),
post: a.belongsTo('Post', 'postId'),
postId: a.id(),
}).authorization((allow) => [
allow.publicApiKey().to(['read']),
allow.authenticated().to(['create']),
allow.owner().to(['update', 'delete']),
]),
User: a.model({
name: a.string().required(),
email: a.string().required(),
posts: a.hasMany('Post', 'authorId'),
}).authorization((allow) => [
allow.owner(),
allow.authenticated().to(['read']),
]),
});
export type Schema = ClientSchema<typeof schema>;
export const data = defineData({ schema });
// Client-side data operations
import { generateClient } from 'aws-amplify/data';
import type { Schema } from '../amplify/data/resource';
const client = generateClient<Schema>();
// Create
const { data: post } = await client.models.Post.create({
title: 'My Post',
body: 'Content here',
published: true,
});
// Read
const { data: post } = await client.models.Post.get({ id: postId });
// List with filters
const { data: posts } = await client.models.Post.list({
filter: { published: { eq: true } },
});
// Update
const { data: updated } = await client.models.Post.update({
id: postId,
title: 'Updated Title',
});
// Delete
await client.models.Post.delete({ id: postId });
// Real-time subscription
const sub = client.models.Post.observeQuery({
filter: { published: { eq: true } },
}).subscribe({
next: ({ items }) => {
console.log('Posts:', items);
},
});
// Cleanup: sub.unsubscribe();
Storage (S3)
// amplify/storage/resource.ts
import { defineStorage } from '@aws-amplify/backend';
export const storage = defineStorage({
name: 'myAppStorage',
access: (allow) => ({
'profile-pictures/{entity_id}/*': [
allow.entity('identity').to(['read', 'write', 'delete']),
],
'public/*': [
allow.guest.to(['read']),
allow.authenticated.to(['read', 'write', 'delete']),
],
}),
});
// Client-side storage
import { uploadData, getUrl, remove } from 'aws-amplify/storage';
// Upload
const result = await uploadData({
path: `profile-pictures/${userId}/avatar.jpg`,
data: file,
options: { contentType: 'image/jpeg' },
}).result;
// Get URL
const { url } = await getUrl({
path: `profile-pictures/${userId}/avatar.jpg`,
});
// Delete
await remove({ path: `profile-pictures/${userId}/avatar.jpg` });
Functions (Lambda)
// amplify/functions/process-order/resource.ts
import { defineFunction } from '@aws-amplify/backend';
export const processOrder = defineFunction({
name: 'process-order',
entry: './handler.ts',
timeoutSeconds: 30,
memoryMB: 256,
environment: {
STRIPE_KEY: 'stripe-secret-key',
},
});
// amplify/functions/process-order/handler.ts
import type { Handler } from 'aws-lambda';
export const handler: Handler = async (event) => {
const { orderId, items } = JSON.parse(event.body || '{}');
// Process the order
return {
statusCode: 200,
body: JSON.stringify({ success: true, orderId }),
};
};
Best Practices
- Use
npx ampx sandboxduring development — it creates an isolated cloud sandbox per developer, avoiding conflicts in shared environments. - Define authorization rules on every model using the
.authorization()chain; combineallow.owner()withallow.publicApiKey().to(['read'])for common blog-like patterns. - Use Amplify Gen 2's typed client (
generateClient<Schema>()) for full type safety from schema to frontend. - Leverage
a.belongsTo()anda.hasMany()relationships to get efficient GraphQL resolvers with DynamoDB single-table design. - Store secrets using Amplify's secret management (
npx ampx sandbox secret set) rather than hardcoding in resource files.
Common Pitfalls
- Missing authorization rules: Models without
.authorization()are inaccessible by default — unlike some other BaaS platforms, Amplify defaults to deny. - Gen 1 vs. Gen 2 confusion: Gen 2 uses TypeScript definitions (no
amplify pushCLI prompts); do not mix Gen 1 CLI commands with a Gen 2 project. - Cold starts: Lambda functions behind AppSync experience cold starts; use provisioned concurrency for latency-sensitive resolvers.
- DynamoDB query limitations: The generated DynamoDB tables may not support all filter combinations efficiently; use secondary indexes defined with
a.secondaryIndex()for common access patterns. - Large file uploads: Use the
uploadDataAPI which handles multipart uploads automatically; do not try to send large files through AppSync mutations.
Anti-Patterns
Over-engineering for hypothetical requirements. Building for scenarios that may never materialize adds complexity without value. Solve the problem in front of you first.
Ignoring the existing ecosystem. Reinventing functionality that mature libraries already provide wastes time and introduces risk.
Premature abstraction. Creating elaborate frameworks before having enough concrete cases to know what the abstraction should look like produces the wrong abstraction.
Neglecting error handling at system boundaries. Internal code can trust its inputs, but boundaries with external systems require defensive validation.
Skipping documentation. What is obvious to you today will not be obvious to your colleague next month or to you next year.
Install this skill directly: skilldb add baas-skills
Related Skills
Appwrite
Appwrite self-hosted BaaS with database, auth, storage, and serverless functions
Backendless
Backendless BaaS with real-time database, user authentication, Cloud Code,
Clerk Auth
Clerk authentication service with pre-built UI components, session management, and multi-framework support
Convex
Convex real-time backend with reactive queries, mutations, and serverless functions
Encore
Encore is a backend development platform that automatically provisions, configures, and manages cloud infrastructure based on your Go code. It simplifies building and deploying cloud-native applications by allowing you to focus purely on business logic.
Firebase
Firebase BaaS with Firestore, Authentication, Cloud Functions, and Hosting