AWS Builder Center
DynamoDB Got Native Vector Search. I Verified It, Day One

DynamoDB Got Native Vector Search. I Verified It, Day One

DynamoDB shipped native vector search on 2026-08-05. I checked the real API in the SDKs, built a live matching app, and hit an actual OIDC bug in production.

Cloud Engineer
On 2026-08-05, AWS announced that Amazon DynamoDB now supports native vector search — no more copying data into a separate vector store, no more sync pipeline to babysit. I read the announcement post the same day someone forwarded it to me, and the first thing I noticed was that it had no code samples. Just prose: a List attribute for embeddings, a SearchVectors API, single-digit millisecond latency, 99%+ recall. No parameter names, no SDK version, nothing I could actually run.
So before writing anything, I went and checked whether the API was real.

Checking the SDKs directly, not the blog post

aws dynamodb help on my installed CLI (2.35.15) showed nothing vector-related. boto3 wasn't even installed locally. So I set up a clean virtualenv, installed the latest boto3/botocore, and asked the service model directly instead of guessing:
1
2
3
4
5
import botocore.session
s = botocore.session.get_session()
model = s.get_service_model('dynamodb')
[o for o in model.operation_names if 'vector' in o.lower()]
# ['SearchVectors']
It's there — botocore 1.43.64 has it. I did the same check on the JS side, since the app I wanted to build would run on Vercel:
1
2
3
npm view @aws-sdk/client-dynamodb version # 3.1103.0
find node_modules/@aws-sdk/client-dynamodb -iname "*SearchVectors*"
# SearchVectorsCommand.d.ts is there too
Then I dumped the actual input/output shape from the service model instead of trusting a blog summary:
  • CreateTable gained a VectorIndexes list: IndexName, VectorAttribute.AttributeName, SearchSchema (a HASH element plus optional INLINE_FILTER elements), Projection, Dimensions, DistanceFunction (COSINE / EUCLIDEAN / DOT_PRODUCT).
  • SearchVectors takes TableName, IndexName, SearchVector (a list of AttributeValue, i.e. the same List<Number> shape as a regular item attribute), TopK, and an optional SearchConditionExpression with ExpressionAttributeNames/Values for filtering.
One thing I checked and got a genuinely useful negative result on: AWS::DynamoDB::Table in CloudFormation does not have a VectorIndexes property yet.
1
2
# describe-type output for AWS::DynamoDB::Table, one day after GA
'VectorIndexes' in schema['properties'] # False
If your infra is CloudFormation-first, you can't provision a vector index through it right now — you have to reach for boto3/CLI directly, which is a real limitation worth knowing about before you commit to an IaC plan.

Building something real: Resonance Match

I didn't want to just curl an API and call it verified, so I built a small app: an icebreaker tool for conferences. You type a nickname and a sentence about what you're currently thinking about or building, it embeds your sentence with Bedrock and stores it in DynamoDB, then runs a live SearchVectors query to find the semantically closest other people in the same room, refreshing every six seconds.
Table design:
  • Partition key eventId, sort key attendeeId
  • A vector index scoped to the room: SearchSchema has one HASH element on eventId, so a search never crosses rooms
  • Dimensions: 1024, DistanceFunction: COSINE — matching Bedrock's amazon.titan-embed-text-v2:0, which I confirmed returns 1024-dim vectors with a real invoke-model call before hardcoding anything
Before writing the app, I ran the whole pipeline by hand with three fake attendees:
1
2
3
0.3638 "AI エージェントに承認ゲートをつける仕組みを作っています" (approval gates for AI agents)
0.4453 "生成AIエージェントの権限設計と人間承認フローに興味があります" (permission design + human approval)
0.8907 "週末は登山とキャンプが趣味です" (hiking and camping)
Queried against "I want to talk about autonomous execution and approval design for AI agents," the two approval-gate sentences came back closest, the hiking sentence came back furthest. That's the moment I actually trusted the feature — not the blog post, this printout.
One gotcha in that printout: with COSINE, the score is a distance, not a similarity. 0 means identical, 2 means opposite, and results are sorted ascending — lowest score wins. I initially misread 0.36 as "36% similar" before checking the JS SDK's doc comment, which spells it out explicitly. The conversion I ended up using: similarity% = (1 - score / 2) * 100.

The bug that actually happened in production

Local dev worked immediately — fromNodeProviderChain() picked up my aws login session, and the app matched people correctly on localhost. Production did not.
First deploy: every POST /api/join came back 500. Vercel's logs:
1
Error [CredentialsProviderError]: Could not load credentials from any providers
My code read process.env.VERCEL_OIDC_TOKEN and fell back to fromNodeProviderChain() if it was missing — the same pattern I'd used successfully in an earlier project. I checked whether OIDC Federation was actually on for this project:
1
2
3
curl -s "https://api.vercel.com/v9/projects/resonance-match?teamId=$TEAM" \
-H "Authorization: Bearer $TOKEN" | jq .oidcTokenConfig
# {"enabled": true, "issuerMode": "team"}
Enabled. And yet a debug endpoint I added confirmed VERCEL_OIDC_TOKEN was simply not present in process.env at runtime. The token isn't statically injected the way I assumed — it has to be fetched through Vercel's own SDK. Switching from a hand-rolled fromWebToken() call to the official @vercel/oidc-aws-credentials-provider package fixed it in one line:
1
2
3
4
5
import { awsCredentialsProvider } from "@vercel/oidc-aws-credentials-provider";

const credentials = process.env.AWS_ROLE_ARN
? awsCredentialsProvider({ roleArn: process.env.AWS_ROLE_ARN, roleSessionName: "resonance-match-web" })
: fromNodeProviderChain();
Redeployed, tested with curl first (to rule out anything browser-side), then with two real browser sessions submitting different sentences — both matched correctly, at a 73% similarity score, updating live within the six-second poll window.

What I didn't verify

I want to be direct about the edges of this, since the whole point of the exercise was not overclaiming:
  • No real event, no concurrent load. Two browser tabs is not a conference room. PAY_PER_REQUEST throttling behavior under a real spike is untested.
  • No content moderation on the free-text input. Fine for a personal demo, not fine for an unsupervised public room.
  • INLINE_FILTER — the equality-only filter attribute in SearchSchema — is implemented in the API but I never exercised it. My filtering is just the HASH element on eventId.
  • No cost comparison against the existing OpenSearch zero-ETL path. I don't know yet whether native vector search is actually cheaper at scale, only that it removes a moving part.
  • This is one day of production experience with a feature that is one day past GA. I'd treat anything here as "worked for me, once" rather than a stability claim.
Code is on GitHub , the app is live at resonance-match.vercel.app  — bring a room full of people and a room code, and it'll tell you who in the room is thinking about the same thing you are.
Any opinions in this article are those of the individual author and may not reflect the opinions of AWS.
Enjoyed reading this content? Let the author know!

Your likes, comments, shares, and saves help creators reach more builders.

Loading recommendations

Loading article