惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V2EX - 技术
V2EX - 技术
P
Privacy International News Feed
Security Latest
Security Latest
H
Hacker News: Front Page
T
Tenable Blog
The Hacker News
The Hacker News
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
S
Security @ Cisco Blogs
Project Zero
Project Zero
O
OpenAI News
AI
AI
Spread Privacy
Spread Privacy
C
CERT Recently Published Vulnerability Notes
The Last Watchdog
The Last Watchdog
G
GRAHAM CLULEY
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Scott Helme
Scott Helme
Application and Cybersecurity Blog
Application and Cybersecurity Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
C
CXSECURITY Database RSS Feed - CXSecurity.com
NISL@THU
NISL@THU
A
Arctic Wolf
T
Threat Research - Cisco Blogs
PCI Perspectives
PCI Perspectives
N
News and Events Feed by Topic
C
Cyber Attacks, Cyber Crime and Cyber Security
C
Cybersecurity and Infrastructure Security Agency CISA
Simon Willison's Weblog
Simon Willison's Weblog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Know Your Adversary
Know Your Adversary
Google Online Security Blog
Google Online Security Blog
罗磊的独立博客
L
LINUX DO - 最新话题
U
Unit 42
S
Security Affairs
有赞技术团队
有赞技术团队
WordPress大学
WordPress大学
博客园 - 【当耐特】
T
The Exploit Database - CXSecurity.com
S
Schneier on Security
月光博客
月光博客
Engineering at Meta
Engineering at Meta
腾讯CDC
F
Full Disclosure
Cyberwarzone
Cyberwarzone
S
SegmentFault 最新的问题
Recorded Future
Recorded Future
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
博客园 - 司徒正美
The Cloudflare Blog

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
I Tried AWS Blocks on a Real Amplify Gen2 Project — Local DynamoDB, No AWS Account, 1-Second Loops
Kohei Aoki · 2026-06-24 · via DEV Community

Intro

I've been running Amplify Gen2 in production for a while now, and the single most annoying part is this: every time I want to check something, I have to deploy. Tweak the backend, push it up with ampx sandbox, wait for it to apply, check, tweak again. Do that a dozen times a day and it quietly grinds you down.

Then on 2026-06-16, AWS Blocks showed up in public preview. It's a set of backend "Building Blocks" — auth, database, file storage, real-time, async jobs, AI agents — that you compose together, and the headline pitch is that you can develop entirely locally, with no AWS account.

The thing is, I read the preview announcement and it didn't actually answer the questions I cared about. Three of them, specifically:

  • Is this something I migrate to from Amplify? Or something I add to my existing Amplify Gen2 app?
  • Can I create a DynamoDB table locally? (this is the one I really wanted to know)
  • How much faster does the dev loop actually get?

Staring at the announcement wasn't going to tell me, so I dropped it into a live production project — an Amplify Gen2 app with 40+ models — inside an isolated git worktree and actually tried it. I ran DynamoDB locally and pushed it all the way through cdk synth to verify.

Spoiler for the impatient: it's additive, not a migration, DynamoDB does run locally, and my verify loop went from "minutes of deploy round-trips" to "a 1-second local refresh." Below is the log. It's a preview, so I'm leaving the parts where I got stuck in.

Versions at the time of testing: @aws-blocks/create-blocks-app@0.1.7 / @aws-blocks/blocks@0.1.5. It's a preview, so expect this to move.

First question: is this a different thing from Amplify?

The official line is refreshingly blunt: "Blocks is not replacing Amplify. It's additive." There are two ways to use it:

  • Add individual Blocks to an Amplify Gen2 app → you keep deploying through Amplify like always, you just get more parts.
  • Use it standalone → full ownership, local-first, develop with no AWS account at all.

Same Blocks in both cases, no rewriting when you move between paths. It's CDK constructs all the way down, so when you run out of road you can drop to raw CDK.

The easy mistake here — and I made it at first — is assuming "there's a standalone mode, therefore this is a thing you move off Amplify onto." Nope. Standalone is just another option; it's not about throwing away your existing Amplify setup. But I don't trust marketing copy on faith, so let's put it on a real project and check.

Trying it on a real project (auto-detect)

The guinea pig: a pnpm workspace monorepo with apps/* and packages/*. The Amplify Gen2 backend lives in packages/gen2-shared-backend/amplify/backend.ts, and amplify/data/resource.ts has 40+ DynamoDB models defined with a.model() (User / Workspace / Project / BusinessModel / LeanCanvas …). This thing runs in production, so obviously I can't have it mangled.

To keep the real tree clean, I cut an isolated worktree before touching anything.

git worktree add ./<repo>.worktrees/aws-blocks-test --detach staging
cd ./<repo>.worktrees/aws-blocks-test/packages/gen2-shared-backend

Then I ran the CLI from where amplify/backend.ts lives. The AWS Blocks CLI looks at the directory it's run in and auto-detects the mode (Amplify detected / existing project / empty directory).

pnpm dlx @aws-blocks/create-blocks-app@latest . -y

Output:

🔍 Detected Amplify Gen 2 project (amplify/backend.ts found)

  CREATE  aws-blocks/           (Blocks backend workspace)
  CREATE  amplify/blocks.ts     (wires Blocks into Amplify backend)
  MODIFY  amplify/backend.ts    (adds import for blocks.ts)
  MODIFY  package.json          (adds workspace, deps, scripts)
  MODIFY  .gitignore            (adds Blocks entries)

Correctly recognized as Amplify Gen2. What I was nervous about was what happens to my existing 40 models, auth, storage, and pile of Lambda resolvers. I looked at the backend.ts diff and exhaled.

-const backend = defineBackend({
+export const backend = defineBackend({
   auth,
   data,
   storage,
   // ... existing resolvers, untouched ...
 });

 backend.addOutput({ /* existing */ });
+
+// Blocks integration — adds Building Blocks to your Amplify backend
+import { initBlocks } from './blocks.js';
+await initBlocks(backend);

This is an append, not a rewrite. It changed const to export const and added one initBlocks(backend) line at the end. Not a single line of the existing definitions in that 548-line backend.ts changed. Looking at the generated amplify/blocks.ts, Blocks gets wired in as a separate nested stack, and it passes Amplify's Cognito config (User Pool ID / Client ID) into the Blocks-side Lambda as environment variables.

// amplify/blocks.ts (generated, excerpt)
export async function initBlocks(backend: any) {
  const blocksStack = backend.createStack('blocks');
  const blocks = await createBlocksBackend(blocksStack, sandboxMode);

  // Reuse Amplify's Cognito for Blocks' bearer-token verification
  if (backend.auth?.resources?.cfnResources) {
    const { cfnUserPool, cfnUserPoolClient } = backend.auth.resources.cfnResources;
    blocks.handler.addEnvironment('COGNITO_USER_POOL_ID', cfnUserPool.ref);
    blocks.handler.addEnvironment('COGNITO_CLIENT_ID', cfnUserPoolClient.ref);
  }
  // ...
}

So "additive, not a migration" checks out. The existing Amplify Data (i.e. your existing DynamoDB tables) stays exactly where it is, and the Blocks stack sits next to it. Auth reuses Amplify's Cognito. No moving day.

Gotcha: install blows up on a pnpm monorepo

It wasn't all smooth, though. After generating files, the CLI runs npm install internally on its own — and that fails.

npm error code EUNSUPPORTEDPROTOCOL
npm error Unsupported URL Type "workspace:": workspace:*

The cause: my test project is a pnpm workspace and its dependencies use pnpm's own workspace:* protocol. The AWS Blocks CLI is hardcoded to npm install, and on top of that it adds an npm-style workspaces array to the generated package.json. That's a double conflict with a pnpm monorepo.

The scaffold itself succeeds, so if you take dependency resolution into your own hands and do it the pnpm way, you can keep going. But it's not the "one npx command and you're done" experience. If you're dropping this into a pnpm monorepo, plan on owning the install yourself. It's a preview, so I'll give it a pass here.

Because of this collision, I decided to do the "actually touch DynamoDB locally" part in a separate standalone clean project. That let me verify the two things independently: the Amplify integration behavior (above) and the local execution proof (below).

The main event: creating DynamoDB locally

I scaffolded a standalone backend template. This one assumes npm, so it goes through cleanly.

pnpm dlx @aws-blocks/create-blocks-app@latest standalone --template backend -y
# → installs 284 packages, done in ~29 seconds

The backend entry point is aws-blocks/index.ts. That's where I add the DynamoDB Block. The AWS Blocks data Blocks are split by use case:

  • KVStore (@aws-blocks/bb-kv-store) = simple key-value (DynamoDB-backed)
  • DistributedTable (@aws-blocks/blocks) = DynamoDB itself. Composite keys, GSIs, range queries — the docs call it "the default for most data."

I want to hit DynamoDB head-on, so DistributedTable it is. The schema is written in zod.

// aws-blocks/index.ts
import { ApiNamespace, Scope, DistributedTable } from '@aws-blocks/blocks';
import { z } from 'zod';

const scope = new Scope('my-app');

// DistributedTable = DynamoDB.
// Local dev: in-process mock / synth & deploy: real DynamoDB. Same code.
const noteSchema = z.object({
  userId: z.string(),
  noteId: z.string(),
  body: z.string(),
  createdAt: z.number(),
});

const notes = new DistributedTable(scope, 'notes', {
  schema: noteSchema,
  key: { partitionKey: 'userId', sortKey: 'noteId' },
});

export const api = new ApiNamespace(scope, 'api', (context) => ({
  async putNote(userId: string, noteId: string, body: string) {
    await notes.put({ userId, noteId, body, createdAt: Date.now() });
    return { ok: true };
  },
  async getNote(userId: string, noteId: string) {
    return await notes.get({ userId, noteId });
  },
  async listNotes(userId: string) {
    return await Array.fromAsync(
      notes.query({ where: { userId: { equals: userId } } }),
    );
  },
}));

Types hold all the way through (tsc --noEmit in ~2.7s). The key types are inferred from the schema, so TypeScript even enforces the key shape on get({ userId, noteId }). No code-gen step. Small thing, but a nice one.

Start the local server:

npm run dev
# Loading backend...
# Deploying local resources...
# 📝 Generating client code...
# AWS Blocks local server running on http://localhost:3001

It says Deploying local resources, but it isn't talking to AWS at all. Look at .blocks-sandbox/config.json and it's "environment": "local", with the API pointed at the local http://localhost:3001/aws-blocks/api (port 3000 was taken by another process, so it helpfully fell back to 3001 on its own).

From here, I hit the DynamoDB Block through the typed client.

getNote(alice,n1): {"userId":"alice","noteId":"n1","body":"first note","createdAt":1782292043870}
listNotes(alice) count: 2
listNotes(bob): [{"userId":"bob","noteId":"n1","body":"bob note",...}]
put×3 + get + query×2 round-trip: 31ms

It works. No AWS account, no deploy, and DynamoDB put / get / partition-key queries all run, with alice and bob properly partition-isolated. Three writes + a read + two queries, a 31ms round-trip. That's the part I'd otherwise be waiting minutes on after a deploy.

"It's just a local mock though, right?" — proving it with synth

The fair thing to be suspicious about: "OK, it runs locally. But if I actually deploy, does it really become DynamoDB?" This is the heart of AWS Blocks — @aws-blocks/blocks's package.json uses conditional exports to resolve the same import to a different thing depending on context.

".": { "browser": "...client...", "cdk": "...construct...", "default": "...local/lambda..." }

  • Local dev (default) → in-process mock
  • cdk synth (cdk) → CDK construct (i.e. CloudFormation)
  • Browser (browser) → typed RPC client

So I ran cdk synth on the exact same index.ts (it's synth, not deploy, so no AWS credentials needed).

npx cdk synth --quiet

Peeking into the generated CloudFormation template, there it was.

AWS::DynamoDB::Table count: 1
 logicalId: myappnotestable...
   BillingMode: PAY_PER_REQUEST
   KeySchema: userId:HASH, noteId:RANGE

That one line, new DistributedTable(..., { key: { partitionKey: 'userId', sortKey: 'noteId' } }), resolves to an instant mock locally and a real AWS::DynamoDB::Table (HASH=userId / RANGE=noteId, on-demand billing) at synth. I didn't change a single character of the code. That's what "create DynamoDB locally" actually means under the hood.

What about GSIs and LSIs? (where I got tripped up once)

If you're going to use DynamoDB, indexes are obviously next on your mind. DistributedTable has an indexes option, so I tried adding two GSIs — and while I was at it, see if I could make an LSI too.

const notes = new DistributedTable(scope, 'notes', {
  schema: noteSchema, // userId, noteId, status, createdAt, body
  key: { partitionKey: 'userId', sortKey: 'noteId' },
  indexes: {
    // same PK(userId) + different SK(createdAt) → in raw DynamoDB this is an LSI candidate
    byCreatedAt: { partitionKey: 'userId', sortKey: 'createdAt' },
    // different PK(status) → clearly a GSI
    byStatus: { partitionKey: 'status', sortKey: 'createdAt' },
  },
});

Locally it all just worked. byCreatedAt returned createdAt in ascending order (100, 200, 300), and byStatus pulled across statuses fine.

But then I looked at the cdk synth'd CloudFormation and went "wait, huh?" for a second.

GlobalSecondaryIndexes: []
LocalSecondaryIndexes: []
AttributeDefinitions: [userId, noteId]   # no createdAt, no status

The table itself has no GSI and no LSI. My first reaction was "oh no, the indexes don't make it into synth…" — too quick. Digging through the whole template, they were elsewhere. A Custom::CloudFormation::CustomResource (my-app-notes-gsi-resource) had byCreatedAt and byStatus sitting right there in its Indexes property. And there was a whole BlocksGsiManager Lambda set generated alongside it (the Provider framework's onEvent / isComplete / Step Functions waiter).

So GSIs aren't inline CFN — they're managed by a dedicated custom resource plus a GSI Manager Lambda. The reason is in DESIGN.md, and it made sense once I read it:

DynamoDB only allows one GSI change per UpdateTable. Standard CDK's Table can't express multiple GSI changes in a single deploy. So we manage them declaratively with a custom resource.

For production deploys it applies GSIs one at a time, in sequence (just as DynamoDB requires); for sandbox it uses a fast path that drops the whole table and recreates it with all GSIs attached. Given that adding a single GSI can take minutes to hours in production, this is a design that took that reality seriously.

LSIs, on the other hand, aren't supported. indexes is exactly what the name says — Global secondary index only — so even my byCreatedAt (same PK + different SK), which I'd written thinking of it as an LSI candidate, is treated as a GSI. DESIGN.md only ever says "partition key + optional sort key + GSIs," and the GSI Manager only ever calls GlobalSecondaryIndexUpdates. DynamoDB's LSIs can only be created at table-creation time, and since the whole thing commits to the after-the-fact UpdateTable path, there's simply no way to create an LSI. That's the reality.

Supported At deploy time Locally
GSI ✅ Yes Custom Resource + GSI Manager Lambda (sequential UpdateTable) Queries work (in-memory filtering)
LSI ❌ Effectively no No way to generate one (same PK+SK is treated as a GSI too) The index-specified query runs, but it's not an LSI

One caveat. The local indexes are implemented as "filter all records in-memory," so the eventual consistency of a real GSI (local is immediately consistent) and throughput throttling are not reproduced. You can verify access-pattern correctness locally, but check consistency and performance in sandbox — DESIGN.md spells this out too. Worth just following their advice here.

Speed: how the loop actually changed

Measured numbers (standalone, dependencies already installed).

Metric Measured
scaffold + dependency install (cold) ~29s (284 packages)
tsc --noEmit (typecheck) ~2.7s
npm run dev start → local ready ~1.23s
edit one handler line → re-apply (HMR) ~1.03s
DynamoDB local round-trip (put×3+get+query×2) 31ms

What matters isn't the numbers themselves — it's that verifying doesn't need AWS. With normal Amplify Gen2 development, every small backend change means waiting on an ampx sandbox deploy round-trip (tens of seconds to minutes). That's exactly the "annoying" I described up top. AWS Blocks' local-first development swaps that round-trip for a 1-second file refresh.

To be fair: this isn't an apples-to-apples, head-to-head comparison. I didn't benchmark Amplify's ampx sandbox real deploy here (it changes cloud state). The premise of the comparison is the structural difference — "local, seconds" vs "deploy, minutes." For an actual Amplify full-build measurement, my other post ("Speeding up Amplify Gen2 builds with a custom Docker image") has the 9m43s→8m45s numbers.

Aside: it plays well with AI agents

This is off the main thread, but it was honestly my favorite part. AWS Blocks calls itself "agent-native," and the npm packages ship with steering files. Scaffolding drops an AGENTS.md, and per-Block usage docs live at node_modules/@aws-blocks/blocks/docs/<package>.md.

AGENTS.md has rules like:

  • Use Building Blocks for all persistence — never local files, in-memory arrays, or local databases.
  • Read block docs at node_modules/@aws-blocks/blocks/docs/<package-name>.md before using a block.
  • The JSON-RPC transport is invisible — do not construct RPC payloads manually.

Basically it's pre-empting the agent: "always use a Block for persistence, don't fake it with local files or in-memory arrays." And in the GSI story above, the DistributedTable docs even warn about a trap AI tends to fall into: "call data methods inside the handler. At the top level you're in a synth context, so you'll crash with table.get is not a function." Because this etiquette is right there in your node_modules before you write a line, an agent is much less likely to wander off in a weird direction. Combine that with instant local verification and the write-with-AI → run-it-immediately cycle tightens up nicely.

Wrap-up

Conclusions from trying it on a real project.

  • Additive, not a migration. Your Amplify Gen2 backend.ts gets exactly one initBlocks(backend) line, and the existing 40 models / auth / storage / resolvers are untouched. Blocks coexists as a separate nested stack and reuses Amplify's Cognito. Standalone is "another option," not a move.
  • You can create DynamoDB locally. One DistributedTable gives you an in-process mock for local dev (put/get/query in 31ms, no AWS account) and a real AWS::DynamoDB::Table at cdk synth. Same code.
  • GSIs work, but via a custom resource. Not inline CFN — a GSI Manager Lambda applies them sequentially with UpdateTable. LSIs aren't supported. If your table design leans on indexes, knowing this up front saves you a nasty surprise.
  • The loop changes. Dev start 1.2s, handler-edit re-apply 1s. "Can't verify without deploying" becomes "instant local refresh."
  • But there's preview-grade friction. On a pnpm monorepo, the CLI's internal npm install dies on workspace:*, so plan on owning the install.

It's not "ditch Amplify and switch." It's "add local development to Amplify Gen2 and pull verification back from the cloud to your laptop." If I had to describe AWS Blocks in one line, that's it. If heavy Amplify development sounds familiar to you, the fastest thing is probably to scaffold the standalone template and run npm run dev once. And if you've got "here's how I used it" stories around GSI/LSI, I'd love to hear them.

References