Generar tipos TypeScript desde DynamoDB
En Postgres introspeccionarías information_schema y generarías tipos a partir de ahí.
DynamoDB no tiene equivalente: DynamoDB no guarda ningún schema de item. Los únicos atributos que el
servicio conoce son los que se usan en las claves. Los AttributeDefinitions de DescribeTable
son explícitos sobre su propio alcance: cada entrada "describes
one attribute in the table and index key schema"
(referencia de la API AWS) —
los otros cincuenta atributos de tu tabla simplemente no están registrados en ninguna parte.
Así que "generar tipos TypeScript desde DynamoDB" siempre significa una de tres cosas: declarar la forma tú mismo, derivarla de un schema que autoras en código, o inferirla de los items que existen de verdad.
¿Cómo obtengo tipos TypeScript para una tabla de DynamoDB?
No hay una API que devuelva la forma de los items de una tabla — DescribeTable solo conoce
los atributos de clave. Tus opciones: escribir a mano una interface y validar en el
boundary (un schema Zod hace de los tipos y el check de runtime un solo
artefacto), usar una librería schema-first donde el schema que autoras produce los
tipos, o inferir la forma desde items reales — por script, o con una tool como
DynoTable que escanea la tabla y exporta una interface TypeScript,
un schema Zod o un JSON Schema.
- Método 1: escribir la interface a mano (+ Zod en el boundary)
- Método 2: librerías schema-first (tipos desde un schema que autoras)
- Método 3: inferir desde los datos mismos
Método 1: escribir la interface a mano + validar en el boundary
El SDK de AWS no puede tipar tus items por ti. El Document client v3 devuelve
items como records sin tipo — cada resultado de GetCommand / QueryCommand es
efectivamente Record<string, unknown> hasta que tú afirmes lo contrario. Un cast
as Order a pelo compila bien y miente en runtime, por eso la versión estricta
empareja la interface con un check de runtime:
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 verifiedUn schema, dos trabajos: z.infer te da el tipo estático, parse atrapa el
item que no encaja — que en un store sin schema es un cuándo, no un
si. El catch es igual de claro: el schema documenta tu intención, no
tu tabla. Nada impide que un writer viejo haya guardado total como
string, y los tipos escritos a mano derivan en silencio mientras los datos evolucionan.
Si trabajas desde la salida cruda de la API (sin Document client), recuerda que la forma wire
es DynamoDB-JSON con tags de tipo ({"S": "..."}, {"N": "123"}) — mira
marshalling, y usa el
convertidor DynamoDB JSON para pasar una muestra
entre wire y forma plana mientras escribes el schema.
Método 2: librerías schema-first
Toolkits como ElectroDB y DynamoDB-Toolbox atacan el problema del drift desde el lado de escritura: autoras un schema de entidad en código, y la librería deriva los tipos TypeScript y impone la forma en cada lectura y escritura que realiza. Esa es la garantía más fuerte disponible — pero fíjate en la dirección: tú escribes el schema; la librería no lo descubre. Apuntar una a una tabla existente sigue significando hacer ingeniería inversa de las formas de item tú primero, y los items escritos fuera de la librería quedan fuera de sus garantías. Brillan en single-table designs greenfield donde cada entidad pasa por el toolkit desde el día uno.
Método 3: inferir los tipos desde items reales
Para una tabla existente, la verdad de terreno son los datos. Escanea una muestra, une las formas:
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 itemsPitfalls del mundo real que la versión ingenua choca al instante:
- Atributos sparse. Los items de DynamoDB en una tabla pueden tener distintos
atributos; un atributo presente en el 80% de los items es
optional, no missing. Sigue la frecuencia por atributo, no solo la presencia. - Entidades mezcladas. En un single-table design,
los items
USER#yORDER#comparten la tabla — una interface fusionada para ambos es inútil. Particiona la muestra por el atributo type y emite un tipo por entidad. - Colisiones de tipo. El mismo atributo guardado como
Naquí ySallá es un bug de datos real (y común) — sácalo como unión en vez de elegir uno en silencio. El set completo de tags está en data types. - Una muestra es una muestra. Atributos que solo aparecen en items raros pueden no estar en tus primeros 5.000 — y el scan cuesta capacidad de lectura de todos modos (query vs scan).
Inferencia en un click en DynoTable
Ese script de inferencia — muestreo, tracking de frecuencia, paths anidados, el split por entidad — está metido en el diálogo de tabla de DynoTable:
- Abre una tabla, pulsa el botón Ajustes de la barra de herramientas de la
pestaña, ve a la sección Indexación y haz click en Indexar tabla. DynoTable muestrea la tabla con progreso en vivo y registra
los atributos que encuentra — incluidos los anidados por path con puntos, como
commonData.status— con el tipo de cada uno y si era required u optional a través de las filas escaneadas. El scan tiene tope, así que un atributo que solo aparece en items raros puede faltar; mira Resumen e indexación de la tabla. - Haz click en Exportar y elige un formato:
- TypeScript — una
interface. - Zod — un schema
z.object(...)(compatible con Standard-Schema). - JSON Schema — draft 2020-12.
- TypeScript — una
- Cópialo al clipboard o guárdalo en un archivo.

Cada schema generado abre con una nota de que fue inferido de los items muestreados — un punto de partida sólido, no un contrato autoritativo. La optionalidad refleja con qué frecuencia apareció cada atributo al indexar, y los atributos de clave primaria siempre se marcan required. Indexar incurre en costes normales de lectura de DynamoDB, y Reindexar refresca el cuadro tras cambiar tus datos.
FAQ
¿Puedo generar tipos desde DescribeTable?
Solo para los atributos de clave. AttributeDefinitions cubre el key schema de la tabla y
los índices — nada más sobre tus items lo guarda el servicio,
así que no hay schema server-side que introspeccionar.
¿Cuál es la mejor forma de tipar una tabla de producción existente? Infiere primero, endurece después: muestrea los items reales (script o export indexado de DynoTable) para obtener la forma real, revísala, y promueve a un schema Zod que poseas o a una entidad de librería schema-first para que el drift futuro se atrape en el boundary.
¿Cómo manejo varios tipos de entidad en una tabla? Un tipo por entidad, nunca un tipo fusionado. Parte la muestra por tu atributo type (o prefijo de clave) y genera una interface separada para cada uno — la unión discriminada de esas es el tipo de tu tabla.
¿Por qué mis tipos generados dicen que un campo required es optional? Porque algún item muestreado no lo tenía. En un store sin schema la optionalidad es una observación, no una declaración — comprueba si esos items son filas legacy a backfillear (mira migraciones) o un atributo genuinamente optional.
¿Los tipos cubren sets y binary de DynamoDB? Un convertidor tiene que elegir representaciones plain-JSON: los sets pasan a arrays y binary a un string encoded — las mismas rarezas de mapping cubiertas en marshalling. Haz round-trip de una muestra por el convertidor DynamoDB JSON para ver exactamente cómo se ven tus atributos a cada lado.
Deja de adivinar la forma de tu tabla — descarga DynoTable, indexa la tabla y exporta un TypeScript, Zod o JSON Schema en un click.


