TypeScript-Types aus DynamoDB generieren
In Postgres introspektierst du information_schema und generierst Types daraus.
DynamoDB hat kein Äquivalent: DynamoDB speichert überhaupt kein Item-Schema.
Die einzigen Attribute, die der Service kennt, sind die in Keys. DescribeTables
AttributeDefinitions ist explizit über den eigenen Scope: jeder Eintrag
„describes one attribute in the table and index key schema“
(AWS API reference)
— deine anderen fünfzig Attribute sind nirgends erfasst.
„TypeScript-Types aus DynamoDB generieren“ heißt also immer eines von dreien: die Shape deklarieren, sie aus einem Schema ableiten, das du in Code authorst, oder sie aus den Items inferieren, die tatsächlich existieren.
Wie bekomme ich TypeScript-Types für eine DynamoDB-Tabelle?
Es gibt keine API, die die Item-Shape einer Tabelle zurückgibt — DescribeTable
kennt nur die Key-Attribute. Deine Optionen: ein interface handschreiben und
an der Boundary validieren (ein Zod-Schema macht Types und Runtime-Check zu einem
Artefakt), eine schema-first Library nutzen, bei der das Schema, das du authorst,
die Types liefert, oder die Shape aus echten Items inferieren — per Script oder
mit einem Tool wie DynoTable, das die Tabelle scannt und ein
TypeScript-Interface, Zod-Schema oder JSON Schema exportiert.
- Methode 1: Interface handschreiben (+ Zod an der Boundary)
- Methode 2: Schema-first Libraries (Types aus einem Schema, das du authorst)
- Methode 3: Aus den Daten selbst inferieren
Methode 1: Interface handschreiben + an der Boundary validieren
Das AWS SDK kann deine Items nicht für dich typen. Der v3 Document Client liefert
Items als untyped Records — jedes GetCommand/QueryCommand-Result ist
effektiv Record<string, unknown>, bis du etwas anderes assertest. Ein nacktes
as Order-Cast kompiliert und lügt zur Runtime — deshalb paart eine strikte
Version das Interface mit einem Runtime-Check:
import {z} from 'zod';
const Order = z.object({
PK: z.string(), // ORDER#<id>
SK: z.string(), // META
status: z.enum(['open', 'shipped', 'cancelled']),
total: z.number(),
couponCode: z.string().optional() // sparse attribute
});
type Order = z.infer<typeof Order>;
const {Item} = await doc.send(new GetCommand({TableName: 'Orders', Key: key}));
const order = Order.parse(Item); // typed AND verifiedEin Schema, zwei Jobs: z.infer gibt den Static Type, parse fängt das Item,
das nicht passt — in einem schemalosen Store ein wann, kein ob. Der Haken
ist ebenso klar: das Schema dokumentiert deine Absicht, nicht deine Tabelle.
Nichts hindert einen alten Writer, total als String gespeichert zu haben, und
handgeschriebene Types driften still, während sich die Daten entwickeln.
Arbeitest du mit Raw-(Non-Document-Client-)API-Output, denk an die Wire-Shape:
type-tagged DynamoDB-JSON ({"S": "..."}, {"N": "123"}) — siehe
Marshalling, und nutze den
DynamoDB JSON Converter, um ein Sample zwischen
Wire und Plain zu flippen, während du das Schema schreibst.
Methode 2: Schema-first Libraries
Toolkits wie ElectroDB und DynamoDB-Toolbox greifen das Drift-Problem von der Write-Seite an: du authorst ein Entity-Schema in Code, und die Library leitet die TypeScript-Types ab und erzwingt die Shape bei jedem Read und Write, den sie ausführt. Das ist die stärkste verfügbare Garantie — aber beachte die Richtung: du schreibst das Schema; die Library entdeckt es nicht. Eine bestehende Tabelle anzubinden heißt trotzdem, die Item-Shapes selbst reverse-engineeren, und Items außerhalb der Library liegen außerhalb ihrer Garantien. Sie glänzen bei Greenfield-Single-Table Designs, wo jede Entity von Tag eins durch das Toolkit geht.
Methode 3: Types aus echten Items inferieren
Bei einer bestehenden Tabelle ist die Ground Truth die Daten. Sample scannen, Shapes unionen:
const seen = new Map<string, Set<string>>(); // attr -> observed types
let count = 0;
let key: Record<string, unknown> | undefined;
do {
const page = await doc.send(new ScanCommand({TableName: 'Orders', ExclusiveStartKey: key}));
for (const item of page.Items ?? []) {
count++;
for (const [attr, value] of Object.entries(item)) {
const t = Array.isArray(value) ? 'array' : typeof value;
(seen.get(attr) ?? seen.set(attr, new Set()).get(attr)!).add(t);
}
}
key = page.LastEvaluatedKey;
} while (key && count < 5000);
// emit: attribute -> type union, optional if seen in < count itemsFallstricke, die die naive Version sofort trifft:
- Sparse Attributes. DynamoDB-Items in einer Tabelle können unterschiedliche
Attribute haben; ein Attribut auf 80 % der Items ist
optional, nicht fehlend. Per-Attribute-Frequency tracken, nicht nur Presence. - Gemischte Entities. In einem Single-Table Design
teilen sich
USER#- undORDER#-Items die Tabelle — ein gemergtes Interface für beide ist nutzlos. Sample am Type Attribute partitionieren und einen Type pro Entity emittieren. - Type Collisions. Dasselbe Attribut hier als
N, dort alsSist ein echter (und häufiger) Data Bug — als Union surfacer, nicht still eines wählen. Das volle Tag-Set steht in Data Types. - Ein Sample ist ein Sample. Attribute, die nur auf seltenen Items liegen, fehlen vielleicht in den ersten 5.000 — und der Scan kostet trotzdem Read Capacity (Query vs Scan).
One-Click-Inference in DynoTable
Dieses Inference-Script — Sampling, Frequency-Tracking, Nested Paths, Per-Entity- Split — steckt in DynoTables Tabellen-Dialog:
- Tabelle öffnen, Einstellungen-Button in der Tab-Toolbar, Bereich
Indexierung, dann Tabelle indexieren.
DynoTable sammelt die Tabelle mit Live-Progress und erfasst die gefundenen
Attribute — inklusive nested per Dotted Path wie
commonData.status— mit Type und ob required oder optional über die gescannten Rows. Der Scan ist capped, ein Attribut nur auf seltenen Items kann fehlen; siehe Tabellenübersicht und -indexierung. - Exportieren und Format wählen:
- TypeScript — ein
interface. - Zod — ein
z.object(...)-Schema (Standard-Schema-kompatibel). - JSON Schema — Draft 2020-12.
- TypeScript — ein
- In die Zwischenablage kopieren oder als Datei speichern.

Jedes generierte Schema beginnt mit dem Hinweis, dass es aus den gesampelten Items inferiert wurde — ein starker Startpunkt, kein autoritativer Vertrag. Optionalität spiegelt, wie oft jedes Attribut beim Indexing vorkam; Primary-Key- Attribute sind immer required. Indexing verursacht normale DynamoDB-Read-Kosten, und Neu indexieren aktualisiert das Bild, nachdem sich deine Daten geändert haben.
FAQ
Kann ich Types aus DescribeTable generieren?
Nur für die Key-Attribute. AttributeDefinitions deckt Table- und Index-Key-
Schema ab — sonst nichts über deine Items ist vom Service gespeichert, also gibt
es kein Server-Side-Schema zum Introspektieren.
Was ist der beste Weg, eine bestehende Production-Tabelle zu typen? Zuerst inferieren, dann härten: echte Items sampeln (Script oder DynoTables indexierter Export) für die tatsächliche Shape, reviewen und zu einem hand-owned Zod-Schema oder einer schema-first Library-Entity promoten, damit zukünftiger Drift an der Boundary auffällt.
Wie gehe ich mit mehreren Entity-Types in einer Tabelle um? Ein Type pro Entity, nie ein gemergter Type. Sample am Type Attribute (oder Key-Prefix) splitten und ein separates Interface pro Entity generieren — die discriminated Union davon ist dein Table Type.
Warum sagen meine generierten Types, ein required Field sei optional? Weil irgendein gesampeltes Item es nicht hatte. In einem schemalosen Store ist Optionalität eine Observation, keine Deklaration — prüfe, ob diese Items Legacy- Rows zum Backfill sind (siehe Migrations) oder ein echt optionales Attribut.
Decken die Types DynamoDB-Sets und Binary ab? Ein Converter muss Plain-JSON-Repräsentationen wählen: Sets werden Arrays, Binary ein encoded String — dieselben Mapping-Quirks wie in Marshalling. Ein Sample durch den DynamoDB JSON Converter round-trippen, um zu sehen, wie deine Attribute auf jeder Seite aussehen.
Hör auf, die Shape deiner Tabelle zu raten — DynoTable herunterladen, Tabelle indexieren und TypeScript, Zod oder JSON Schema mit einem Klick exportieren.


