agents-inc/skills/src/skills/mobile-storage-watermelondb/SKILL.md
mobile-storage-watermelondb
WatermelonDB reactive local database for React Native - schema, models, decorators, reactive queries, relations, writers/readers, batch operations, migrations, sync
- Source repository stars
- 23
- Declared platforms
- 0
- Static risk flags
- 1
- Last source update
- 2026-08-09
- Source checked
- 2026-08-28
Decision brief
What it does: where it fits
Quick Guide: Use WatermelonDB for offline-first React Native apps with large local datasets. Define schemas with appSchema/tableSchema, models with decorators (@field, @text, @date, @readonly, @relation, @children). All writes MUST go through @writer methods or database.write().…
Not for
- Tasks that require unconfirmed production actions or broad system permissions.
- Environments where the pinned source and install steps cannot be inspected.
Compatibility matrix
Platform support, with evidence labels
| Platform | Status | Evidence | What to check |
|---|---|---|---|
| Codex | Not declared | No explicit evidence | Portability before use |
| Claude Code | Not declared | No explicit evidence | Portability before use |
| Cursor | Not declared | No explicit evidence | Portability before use |
| Gemini CLI | Not declared | No explicit evidence | Portability before use |
Installation
Inspect first. Install second.
The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.
npx skills add https://github.com/agents-inc/skills --skill "src/skills/mobile-storage-watermelondb"Inspect the Agent Skill "mobile-storage-watermelondb" from https://github.com/agents-inc/skills/blob/81d43a51211aca12c85dcc16085fa99014ec548e/src/skills/mobile-storage-watermelondb/SKILL.md at commit 81d43a51211aca12c85dcc16085fa99014ec548e. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.
Workflow
What the source asks the agent to do
- 01
CRITICAL: Before Using This Skill
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering, import type, named constants)
Building offline-first React Native apps with large local datasets (thousands+ records)Defining relational data models with typed fields and relationsConnecting React components to live-updating database queries - 02
Philosophy
WatermelonDB is a reactive, lazy-loading database built on SQLite for React Native apps that need to handle thousands of records without blocking the JS thread. The key insight: nothing is loaded until requested, and all querying runs on a separate native SQLite thread.
Lazy by default -- records are not loaded into JS memory until accessed. A collection with 10,000 records costs nothing until you query it.Reactive -- observe() and withObservables push updates to components automatically when underlying data changes. No manual refetching.Schema-first -- define your database structure with appSchema/tableSchema, then create Model classes that map to those tables via decorators. - 03
Core Patterns
Schemas define the database structure. Column types are string, number, or boolean. Use isOptional: true for nullable columns and isIndexed: true for query-heavy columns.
@field -- raw column value (string/number/boolean), guaranteed to match schema type@text -- like @field but trims whitespace, use for user-editable text@date -- converts stored unix timestamp to JS Date object - 04
Pattern 1: Schema Definition
Schemas define the database structure. Column types are string, number, or boolean. Use isOptional: true for nullable columns and isIndexed: true for query-heavy columns.
Schemas define the database structure. Column types are string, number, or boolean. Use isOptional: true for nullable columns and isIndexed: true for query-heavy columns.Why good: isIndexed on foreign keys speeds up relation queries, dates use number type (unix timestamps), snakecase naming follows conventionNaming conventions: Tables are plural snakecase (posts, blogcomments). Columns are snakecase. FKs use id suffix. Booleans use isprefix. Date columns use\at suffix. - 05
Pattern 2: Model with Field Decorators
Models are classes extending Model that map to schema tables. Decorators bind properties to columns.
@field -- raw column value (string/number/boolean), guaranteed to match schema type@text -- like @field but trims whitespace, use for user-editable text@date -- converts stored unix timestamp to JS Date object
Permission review
Static risk signals and limitations
Network access
The documentation includes network, browsing, or remote request actions.
Query API with `Q.where`, `Q.on`, `Q.sortBy`, `Q.like`, `Q.oneOf`Network access
The documentation includes network, browsing, or remote request actions.
### Pattern 6: Query APIEvidence record
Why each signal appears
| Signal | Value | Evidence type | Meaning |
|---|---|---|---|
| Quality score | 91/100 | Computed | Documentation, specificity, maintenance, and trust rules |
| Repository stars | 23 | Source | Repository attention, not individual Skill quality |
| Compatibility | 0 platforms | Source | Declared in the catalog source record |
| Usage guide | automated source guide | Editorial | Generated or reviewed according to the visible evidence level |
Pinned source
Provenance and original SKILL.md
- Repository
- agents-inc/skills
- Skill path
- src/skills/mobile-storage-watermelondb/SKILL.md
- Commit
- 81d43a51211aca12c85dcc16085fa99014ec548e
- License
- MIT
- Collected
- 2026-08-28
- Default branch
- main
View the original SKILL.md
WatermelonDB Patterns
Quick Guide: Use WatermelonDB for offline-first React Native apps with large local datasets. Define schemas with
appSchema/tableSchema, models with decorators (@field,@text,@date,@readonly,@relation,@children). All writes MUST go through@writermethods ordatabase.write(). Connect components reactively withwithObservablesfrom@nozbe/watermelondb/react. Usebatch()for multi-record operations. Lazy loading means nothing is loaded until requested -- queries run on a native SQLite thread.
<critical_requirements>
CRITICAL: Before Using This Skill
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type, named constants)
(You MUST wrap ALL database modifications in @writer methods or database.write() -- writes outside a writer throw at runtime)
(You MUST keep schema version and migration toVersion in sync -- migrations cannot be newer than the schema version)
(You MUST use @immutableRelation for relations that never change after creation -- it provides extra safety and performance over @relation)
(You MUST use prepareCreate/prepareUpdate/prepareMarkAsDeleted inside batch() -- never await individual operations in a batch)
</critical_requirements>
Auto-detection: WatermelonDB, @nozbe/watermelondb, appSchema, tableSchema, @field, @text, @date, @readonly, @json, @nochange, @writer, @reader, @relation, @immutableRelation, @children, @lazy, withObservables, useDatabase, DatabaseProvider, observe, observeWithColumns, synchronize, pullChanges, pushChanges, schemaMigrations, Q.where, Q.on, database.write, database.batch, markAsDeleted, destroyPermanently
When to use:
- Building offline-first React Native apps with large local datasets (thousands+ records)
- Defining relational data models with typed fields and relations
- Connecting React components to live-updating database queries
- Syncing local data with a remote server via
synchronize() - Migrating database schema across app versions
- Performing bulk operations with
batch()
Key patterns covered:
- Schema definition with
appSchema/tableSchemaand column types - Model classes with field decorators (
@field,@text,@date,@readonly,@json) - Relations (
@relation,@immutableRelation,@children,@lazy) - Writers/readers for safe database mutations and reads
- Reactive components with
withObservablesandobserve()/observeWithColumns() - Query API with
Q.where,Q.on,Q.sortBy,Q.like,Q.oneOf - Batch operations for multi-record create/update/delete
- Schema migrations with
schemaMigrations/addColumns/createTable - Sync protocol with
synchronize(),pullChanges,pushChanges
When NOT to use:
- Simple key-value storage (use a key-value store)
- Apps with small datasets that fit comfortably in memory
- Data that only lives on the server with no offline requirement
- Non-relational storage needs (flat preferences, tokens)
Detailed Resources:
- examples/core.md - Schema, models, decorators, CRUD, queries, reactive components
- examples/sync.md - Sync protocol, migrations, batch operations
- reference.md - API tables, decision framework, decorator reference
Philosophy
WatermelonDB is a reactive, lazy-loading database built on SQLite for React Native apps that need to handle thousands of records without blocking the JS thread. The key insight: nothing is loaded until requested, and all querying runs on a separate native SQLite thread.
Core principles:
- Lazy by default -- records are not loaded into JS memory until accessed. A collection with 10,000 records costs nothing until you query it.
- Reactive --
observe()andwithObservablespush updates to components automatically when underlying data changes. No manual refetching. - Schema-first -- define your database structure with
appSchema/tableSchema, then create Model classes that map to those tables via decorators. - Writers enforce safety -- all mutations must go through
@writerordatabase.write(). This guarantees mutual exclusion -- only one writer runs at a time, preventing race conditions. - Sync-ready -- built-in
synchronize()handles pull/push with conflict resolution, designed for offline-first architectures.
Performance characteristics:
| Scenario | Behavior |
|---|---|
| 10,000 records in a table | Zero JS cost until queried |
| Complex query | Runs on native SQLite thread, resolves instantly |
| List re-rendering | observe() emits only when matching records change |
| Bulk operations | batch() groups into single native transaction |
v0.27+ architecture: All React helpers consolidated under @nozbe/watermelondb/react (replaces @nozbe/with-observables, @nozbe/watermelondb/DatabaseProvider, @nozbe/watermelondb/hooks). v0.28 requires React Native 0.74+ and Node.js 18+.
Core Patterns
Pattern 1: Schema Definition
Schemas define the database structure. Column types are string, number, or boolean. Use isOptional: true for nullable columns and isIndexed: true for query-heavy columns.
import { appSchema, tableSchema } from "@nozbe/watermelondb";
export const schema = appSchema({
version: 1,
tables: [
tableSchema({
name: "posts",
columns: [
{ name: "title", type: "string" },
{ name: "body", type: "string" },
{ name: "subtitle", type: "string", isOptional: true },
{ name: "is_pinned", type: "boolean" },
{ name: "created_at", type: "number" }, // dates stored as timestamps
{ name: "author_id", type: "string", isIndexed: true }, // FK
],
}),
tableSchema({
name: "comments",
columns: [
{ name: "body", type: "string" },
{ name: "post_id", type: "string", isIndexed: true },
{ name: "author_id", type: "string", isIndexed: true },
],
}),
],
});
Why good: isIndexed on foreign keys speeds up relation queries, dates use number type (unix timestamps), snake_case naming follows convention
Naming conventions: Tables are plural snake*case (posts, blog_comments). Columns are snake_case. FKs use _id suffix. Booleans use is*prefix. Date columns use\_at suffix.
See examples/core.md for full schema with all column types.
Pattern 2: Model with Field Decorators
Models are classes extending Model that map to schema tables. Decorators bind properties to columns.
import { Model } from "@nozbe/watermelondb";
import {
field,
text,
date,
readonly,
json,
nochange,
relation,
children,
immutableRelation,
} from "@nozbe/watermelondb/decorators";
const sanitizeTags = (raw: unknown) =>
Array.isArray(raw) ? raw.map(String) : [];
class Post extends Model {
static table = "posts";
static associations = {
comments: { type: "has_many" as const, foreignKey: "post_id" },
};
@text("title") title!: string;
@text("body") body!: string;
@field("is_pinned") isPinned!: boolean;
@date("created_at") createdAt!: Date;
@readonly @date("updated_at") updatedAt!: Date;
@json("tags", sanitizeTags) tags!: string[];
@nochange @field("author_id") authorId!: string;
@immutableRelation("users", "author_id") author!: Relation<User>;
@children("comments") comments!: Query<Comment>;
}
Why good: @text trims whitespace (for user input), @date converts timestamps to Date objects, @readonly prevents any assignment, @nochange prevents modification after creation, @json with sanitizer validates parsed data
Key decorator rules:
@field-- raw column value (string/number/boolean), guaranteed to match schema type@text-- like@fieldbut trims whitespace, use for user-editable text@date-- converts stored unix timestamp to JSDateobject@readonly-- cannot be set at all (server-set fields in sync)@nochange-- can be set increate()but not inupdate()@json(column, sanitizer)-- parses JSON from string column, sanitizer validates the parsed output
See examples/core.md for the complete decorator reference with good/bad examples.
Pattern 3: Relations
Use @relation for mutable to-one relationships, @immutableRelation for to-one that never changes, and @children for to-many (returns a Query).
class Comment extends Model {
static table = "comments";
// Immutable -- a comment's post never changes
@immutableRelation("posts", "post_id") post!: Relation<Post>;
// Mutable -- assignee can be reassigned
@relation("users", "assignee_id") assignee!: Relation<User>;
// To-many -- all replies to this comment
@children("replies") replies!: Query<Reply>;
}
When to use @immutableRelation: When the FK is set once at creation and never changes (comment belongs to post, order belongs to user). Provides extra protection and performance.
When to use @relation: When the FK can be reassigned (task assignee, category).
See examples/core.md for relation API methods (.set(), .id, .fetch(), .observe()) and many-to-many via pivot tables.
Pattern 4: Writers, Readers, and Actions
All database modifications MUST go through a @writer or database.write(). Readers ensure consistent reads with mutual exclusion.
class Post extends Model {
static table = "posts";
@writer async addComment(body: string, author: User) {
return await this.collections.get<Comment>("comments").create((comment) => {
comment.post.set(this);
comment.author.set(author);
comment.body = body;
});
}
@writer async markAsPinned() {
await this.update((post) => {
post.isPinned = true;
});
}
@writer async softDelete() {
await this.markAsDeleted(); // Marks for sync, keeps in DB
}
@reader async fetchActiveComments() {
return await this.comments.extend(Q.where("is_active", true)).fetch();
}
}
Why good: @writer guarantees mutual exclusion (only one writer at a time), @reader prevents writes during multi-step reads, markAsDeleted preserves record for sync
Key rules:
@writermethods can create, update, delete records@readermethods can only read (fetch, count)- Writers/readers are async and return Promises
- Only one writer runs at a time -- others queue
- Use
callWriter()/callReader()to call other action methods from within a writer/reader
See examples/core.md for database.write() standalone usage and nesting rules.
Pattern 5: Reactive Components with withObservables
Connect components to live database data using withObservables from @nozbe/watermelondb/react. Components re-render automatically when observed data changes.
import { withObservables } from "@nozbe/watermelondb/react";
interface PostItemProps {
post: Post;
commentCount: number;
}
function PostItem({ post, commentCount }: PostItemProps) {
return (
<View>
<Text>{post.title}</Text>
<Text>{commentCount} comments</Text>
</View>
);
}
const enhance = withObservables(["post"], ({ post }: { post: Post }) => ({
post: post.observe(),
commentCount: post.comments.observeCount(),
}));
const EnhancedPostItem = enhance(PostItem);
Why good: Component re-renders only when the specific post or its comment count changes, not on any database change
observe() vs observeWithColumns():
observe()-- emits when the record itself changes, or when query results add/remove recordsobserveWithColumns(["column_a", "column_b"])-- also emits when matched records change specified columns (use for sorted lists)
See examples/core.md for DatabaseProvider, useDatabase, sorted lists with observeWithColumns, and composition patterns.
Pattern 6: Query API
Queries are built with Q conditions and executed with fetch(), observe(), fetchCount(), or observeCount().
import { Q } from "@nozbe/watermelondb";
const RECENT_DAYS = 7;
const cutoff = Date.now() - RECENT_DAYS * 24 * 60 * 60 * 1000;
// Basic conditions
const recentPosts = await database
.get<Post>("posts")
.query(
Q.where("created_at", Q.gt(cutoff)),
Q.where("is_pinned", true),
Q.sortBy("created_at", Q.desc),
Q.take(20),
)
.fetch();
// Cross-table JOIN with Q.on
const postsWithActiveComments = await database
.get<Post>("posts")
.query(Q.on("comments", "is_active", true))
.fetch();
Key operators: Q.eq, Q.notEq, Q.gt, Q.gte, Q.lt, Q.lte, Q.between, Q.oneOf, Q.notIn, Q.like, Q.notLike, Q.and, Q.or, Q.on, Q.sortBy, Q.take, Q.skip
Gotcha: Q.like uses % for wildcards and is case-insensitive. Always use Q.sanitizeLikeString() on user input to escape special characters.
See examples/core.md for the full query API with complex conditions and text search.
Pattern 7: Batch Operations
Use batch() to group multiple operations into a single native transaction. Use prepare* methods (not awaited individual operations).
@writer async importPosts(rawPosts: RawPost[]) {
const postsCollection = this.collections.get<Post>("posts");
const prepared = rawPosts.map((raw) =>
postsCollection.prepareCreate((post) => {
post.title = raw.title;
post.body = raw.body;
}),
);
await this.batch(...prepared);
}
Why good: Single native transaction is atomic and much faster than individual creates. Falsy values in batch() are ignored (useful for conditional operations).
Prepare methods: collection.prepareCreate(), record.prepareUpdate(), record.prepareMarkAsDeleted(), record.prepareDestroyPermanently()
See examples/sync.md for batch patterns with mixed create/update/delete.
Pattern 8: Schema Migrations
Evolve your database schema across app versions. Each migration step increments toVersion and applies changes.
import {
schemaMigrations,
addColumns,
createTable,
} from "@nozbe/watermelondb/Schema/migrations";
export const migrations = schemaMigrations({
migrations: [
{
toVersion: 2,
steps: [
addColumns({
table: "posts",
columns: [{ name: "subtitle", type: "string", isOptional: true }],
}),
],
},
{
toVersion: 3,
steps: [
createTable({
name: "tags",
columns: [
{ name: "name", type: "string" },
{ name: "post_id", type: "string", isIndexed: true },
],
}),
],
},
],
});
Critical rule: Schema version must equal the highest migration toVersion. If schema is version 3, you need migrations up to toVersion: 3.
See examples/sync.md for migration strategies and the relationship between schema version and sync.
Pattern 9: Sync with synchronize()
Built-in sync engine for offline-first architectures. Implement pullChanges and pushChanges to connect to your backend.
import { synchronize } from "@nozbe/watermelondb/sync";
async function syncDatabase(database: Database) {
await synchronize({
database,
pullChanges: async ({ lastPulledAt, schemaVersion, migration }) => {
const response = await fetch(
`https://api.example.com/sync/pull?last=${lastPulledAt}&schema=${schemaVersion}`,
);
const { changes, timestamp } = await response.json();
return { changes, timestamp };
},
pushChanges: async ({ changes, lastPulledAt }) => {
await fetch("https://api.example.com/sync/push", {
method: "POST",
body: JSON.stringify({ changes, lastPulledAt }),
});
},
migrationsEnabledAtVersion: 1,
});
}
Key constraints:
pullChangesreturns{ changes, timestamp }wherechangeshas{ created: [], updated: [], deleted: [] }per tablepushChangesreceives local changes in the same format- Server must provide a consistent snapshot (use transactions or read locks)
migrationsEnabledAtVersionenables schema-aware sync
See examples/sync.md for the complete sync protocol, conflict resolution, and error handling.
<decision_framework>
Decision Framework
What kind of local data do you need?
|
+-> Simple key-value pairs (preferences, tokens)?
| +-> Use a key-value store (not WatermelonDB)
|
+-> Relational data with queries?
| +-> Small dataset (<100 records) with no offline sync?
| | +-> Consider simpler storage first
| +-> Large dataset (1000+ records) or offline-first?
| +-> WatermelonDB
|
+-> Need offline sync with a server?
| +-> WatermelonDB with synchronize()
|
+-> Only server data, always online?
+-> Use your data fetching solution (not WatermelonDB)
When to Use Each API
| Scenario | API |
|---|---|
| Define database structure | appSchema/tableSchema |
| Map columns to properties | @field, @text, @date, @json |
| Prevent field modification | @readonly (never set), @nochange (set once) |
| One-to-one relation (fixed) | @immutableRelation |
| One-to-one relation (mutable) | @relation |
| One-to-many relation | @children |
| Create/update/delete records | @writer method or database.write() |
| Consistent multi-step reads | @reader method or database.read() |
| Bulk create/update/delete | batch() with prepare* methods |
| Reactive component data | withObservables + observe() |
| Reactive sorted list | observeWithColumns(["sort_column"]) |
| Access database in component | useDatabase() from @nozbe/watermelondb/react |
| Evolve schema across versions | schemaMigrations + addColumns/createTable |
| Sync with remote server | synchronize() with pullChanges/pushChanges |
</decision_framework>
<red_flags>
RED FLAGS
High Priority Issues:
- Modifying records outside a
@writerordatabase.write()-- throws at runtime, all mutations require a writer context - Schema version and migration
toVersionout of sync -- causes database corruption or failed migrations - Using
await collection.create()insidebatch()-- usecollection.prepareCreate()(no await) for batch operations - Missing
static associationson Model classes -- relations andQ.onqueries will not work without declared associations - Importing from
@nozbe/with-observables(v0.27+ moved everything to@nozbe/watermelondb/react)
Medium Priority Issues:
- Using
@relationwhen the FK never changes after creation -- use@immutableRelationfor safety and performance - Not indexing foreign key columns (
isIndexed: true) -- relation queries become slow on large tables - Calling
destroyPermanently()on synced records -- usemarkAsDeleted()so deletions sync to the server - Storing large blobs (>1MB) in WatermelonDB -- SQLite is not optimized for large binary data, use the filesystem
- Not using
Q.sanitizeLikeString()on user input inQ.like()queries -- special characters break the query
Gotchas & Edge Cases:
- Column defaults: string defaults to
"", number to0, boolean tofalse-- useisOptional: trueifnullis a valid state @jsonfields cannot be queried or counted by their contents -- they are opaque string columns@datestores unix timestamps (milliseconds) in anumbercolumn but returns a JSDateobject -- schema column must benumbertypeobserve()on a query emits when records are added/removed but NOT when existing records change fields -- useobserveWithColumns()for field-level reactivitymarkAsDeleted()keeps the record in the local database (flagged for sync) --destroyPermanently()actually removes itcallWriter()/callReader()are required to call other@writer/@readermethods from within a writer/reader -- direct calls throw- Many-to-many relationships require a pivot table with
@immutableRelationon both sides Q.gt(0)excludesnullvalues -- useQ.weakGt(0)if nulls should be included- The
idcolumn is auto-generated (string UUID) -- never declare it in your schema _statusand_changedcolumns are reserved for the sync engine -- never use these names- v0.28 requires React Native 0.74+ and Node.js 18+
</red_flags>
<critical_reminders>
CRITICAL REMINDERS
All code must follow project conventions in CLAUDE.md
(You MUST wrap ALL database modifications in @writer methods or database.write() -- writes outside a writer throw at runtime)
(You MUST keep schema version and migration toVersion in sync -- migrations cannot be newer than the schema version)
(You MUST use @immutableRelation for relations that never change after creation -- it provides extra safety and performance over @relation)
(You MUST use prepareCreate/prepareUpdate/prepareMarkAsDeleted inside batch() -- never await individual operations in a batch)
Failure to follow these rules will cause runtime crashes, data corruption, or silent sync failures.
</critical_reminders>
Frequently asked questions
What to verify before installation and use
What does the mobile-storage-watermelondb source document cover?
Quick Guide: Use WatermelonDB for offline-first React Native apps with large local datasets. Define schemas with appSchema/tableSchema, models with decorators (@field, @text, @date, @readonly, @relation, @children). All writes MUST go through @writer methods or database.write().…
How do I install mobile-storage-watermelondb?
The source record exposes this install command: npx skills add https://github.com/agents-inc/skills --skill "src/skills/mobile-storage-watermelondb". Inspect the command and pinned source before running it.
Which permission-related actions were detected?
Static rules flagged network in the source; the page lists the matching lines and excerpts.
Alternatives
Compare before choosing
UiPath/skills
uipath-coded-apps
UiPath Coded Apps — scaffold, build, run, and deploy Coded Web Apps and Coded Action Apps: React/TypeScript apps that call UiPath Cloud APIs via the `@uipath/uipath-typescript` SDK and ship to Automation Cloud (push/pull to Studio Web, pack, publish, deploy, OAuth-PKCE). Also generates live analytics & governance dashboards from a plain-language request, wired to tenant data via the Insights real-time API, with edit and deploy flows. For RPA→uipath-rpa, Python agents→uipath-agents, Maestro flows
fcakyon/claude-codex-settings
vercel-react-view-transitions
Guide for implementing smooth, native-feeling animations using React's View Transition API (`<ViewTransition>` component, `addTransitionType`, and CSS view transition pseudo-elements). Use this skill whenever the user wants to add page transitions, animate route changes, create shared element animations, animate enter/exit of components, animate list reorder, implement directional (forward/back) navigation animations, or integrate view transitions in Next.js. Also use when the user mentions view
theBGuy/GitDesktop
vercel-react-view-transitions
Guide for implementing smooth, native-feeling animations using React's View Transition API (`<ViewTransition>` component, `addTransitionType`, and CSS view transition pseudo-elements). Use this skill whenever the user wants to add page transitions, animate route changes, create shared element animations, animate enter/exit of components, animate list reorder, implement directional (forward/back) navigation animations, or integrate view transitions in Next.js. Also use when the user mentions view
awslabs/agent-plugins
amplify-workflow
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 patt