Orta6 dakikalık okuma

Bir DynamoDB Tablosundan TypeScript Türleri Nasıl Üretilir

Postgres'te information_schema'yı iç gözlemler ve ondan türler üretirdiniz. DynamoDB'nin bir eşdeğeri yoktur — ve bu eksik bir özellik değil, veri modelinin kendisidir: DynamoDB hiçbir öğe şeması saklamaz. Hizmetin bildiği tek öznitelikler, anahtarlarda kullanılanlardır. DescribeTable'ın AttributeDefinitions'ı kendi kapsamı hakkında açıktır: her giriş, "tablo ve dizin anahtar şemasındaki bir özniteliği tanımlar" (AWS API referansı) — tablonuzun diğer elli özniteliği hiçbir yerde kayıtlı değildir.

Dolayısıyla "DynamoDB'den TypeScript türleri üretmek" her zaman üç şeyden biri anlamına gelir: şekli kendiniz bildirin, kodda yazdığınız bir şemadan türetin veya gerçekten var olan öğelerden çıkarsayın.

Bir DynamoDB tablosu için TypeScript türlerini nasıl alırım?

Bir tablonun öğe şeklini döndüren bir API yoktur — DescribeTable yalnızca anahtar öznitelikleri bilir. Seçenekleriniz: elle bir interface yazıp sınırda doğrulayın (bir Zod şeması, türleri ve çalışma zamanı denetimini tek bir yapıt yapar), yazdığınız şemanın türleri ürettiği şema-öncelikli bir kütüphane kullanın veya şekli gerçek öğelerden çıkarsayın — betikle ya da tabloyu tarayıp bir TypeScript arayüzü, Zod şeması veya JSON Schema dışa aktaran DynoTable gibi bir araçla.

Yöntem 1: arayüzü elle yazın + sınırda doğrulayın

AWS SDK öğelerinizi sizin için türleyemez. v3 Document istemcisi öğeleri türsüz kayıtlar olarak döndürür — her GetCommand / QueryCommand sonucu, siz aksini iddia edene kadar fiilen Record<string, unknown>'dır. Çıplak bir as Order dönüşümü sorunsuz derlenir ve çalışma zamanında yalan söyler; bu yüzden sağlam sürüm, arayüzü bir çalışma zamanı denetimiyle eşleştirir:

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 verified

Tek şema, iki iş: z.infer size statik türü verir, parse ona uymayan öğeyi yakalar — ki şemasız bir depoda bu bir eğer değil, bir ne zaman meselesidir. Püf noktası da aynı ölçüde açık: şema, tablonuzu değil, niyetinizi belgeler. Hiçbir şey eski bir yazarın total'ı bir dize olarak saklamış olmasını engellemez ve elle yazılmış türler, veri evrildikçe sessizce sapar.

Ham (Document-istemcisi olmayan) API çıktısıyla çalışıyorsanız, tel biçiminin tür etiketli DynamoDB-JSON olduğunu hatırlayın ({"S": "..."}, {"N": "123"}) — bkz. marshalling — ve şemayı yazarken bir örneği tel biçimi ile düz biçim arasında çevirmek için DynamoDB JSON dönüştürücüsünü kullanın.

Yöntem 2: şema-öncelikli kütüphaneler

ElectroDB ve DynamoDB-Toolbox gibi araç takımları, sapma sorununa yazma tarafından saldırır: kodda bir varlık şeması yazarsınız ve kütüphane, TypeScript türlerini türetir ve gerçekleştirdiği her okuma ve yazmada şekli zorlar. Bu, mevcut en güçlü garantidir — ama yöne dikkat edin: şemayı siz yazarsınız; kütüphane onu keşfetmez. Birini mevcut bir tabloya doğrultmak, yine önce öğe şekillerini kendiniz tersine mühendislikle çıkarmanız anlamına gelir ve kütüphanenin dışında yazılan öğeler garantilerinin dışındadır. Her varlığın ilk günden araç takımından geçtiği yeşil alan tek tablo tasarımlarında parlarlar.

Yöntem 3: türleri gerçek öğelerden çıkarsayın

Mevcut bir tablo için, zemin gerçeği veridir. Bir örneklem tarayın, şekillerin birleşimini alın:

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 items

Naif sürümün hemen çarptığı gerçek dünya tuzakları:

  • Seyrek öznitelikler. Bir tablodaki DynamoDB öğeleri farklı özniteliklere sahip olabilir; öğelerin %80'inde bulunan bir öznitelik optional'dır, eksik değil. Yalnızca varlığı değil, öznitelik başına sıklığı izleyin.
  • Karışık varlıklar. Bir tek tablo tasarımında USER# ve ORDER# öğeleri tabloyu paylaşır — ikisi için tek bir birleştirilmiş arayüz işe yaramaz. Örneklemi tür özniteliğine göre bölümleyin ve varlık başına bir tür yayımlayın.
  • Tür çakışmaları. Aynı özniteliğin burada N, şurada S olarak saklanması gerçek (ve yaygın) bir veri hatasıdır — sessizce birini seçmek yerine bunu bir birleşim olarak yüzeye çıkarın. Tam etiket kümesi veri türleri bölümündedir.
  • Bir örneklem, bir örneklemdir. Yalnızca nadir öğelerde görünen öznitelikler ilk 5.000'inizde olmayabilir — ve tarama her halükârda okuma kapasitesine mal olur (query ve scan).

DynoTable'da tek tıkla çıkarsama

O çıkarsama betiği — örnekleme, sıklık izleme, iç içe yollar, varlık başına bölme — DynoTable'ın Tablo istatistikleri paneline yerleşiktir:

  1. Bir tablo açın, Stats düğmesine (çubuk grafik simgesi) basın ve Index table'a tıklayın. DynoTable, tabloyu canlı ilerlemeyle bir kez tarar ve öğelerinizin taşıdığı her özniteliği keşfeder — commonData.status gibi noktalı yolla iç içe olanlar dahil — türüyle ve taranan satırlar boyunca zorunlu mu isteğe bağlı mı olduğuyla birlikte.
  2. Export'a tıklayın ve bir biçim seçin:
    • TypeScript — bir interface.
    • Zod — bir z.object(...) şeması (Standard-Schema uyumlu).
    • JSON Schema — draft 2020-12.
  3. Panoya kopyalayın veya bir dosyaya kaydedin.
DynoTable'ın Tablo istatistikleri paneli: türleri ve zorunlu/isteğe bağlı bayraklarıyla dizinlenmiş alan listesi ve şema Export düğmesi.
DynoTable'ın Tablo istatistikleri paneli: türleri ve zorunlu/isteğe bağlı bayraklarıyla dizinlenmiş alan listesi ve şema Export düğmesi.

Dışa aktarım ne olduğu konusunda dürüsttür: üretilen her şema, örneklenen öğelerden çıkarsandığını belirten bir notla açılır — güçlü bir başlangıç noktası, yetkili bir sözleşme değil. İsteğe bağlılık, her özniteliğin dizinleme sırasında ne sıklıkla göründüğünü yansıtır ve birincil anahtar öznitelikleri her zaman zorunlu olarak işaretlenir. Dizinleme, normal DynamoDB okuma maliyetlerine yol açar ve Reindex, veriniz değiştikten sonra resmi tazeler.

SSS

DescribeTable'dan türler üretebilir miyim? Yalnızca anahtar öznitelikler için. AttributeDefinitions, tablo ve dizin anahtar şemasını kapsar — öğeleriniz hakkında başka hiçbir şey hizmet tarafından saklanmaz, dolayısıyla iç gözlemlenecek sunucu tarafı bir şema yoktur.

Mevcut bir üretim tablosunu türlemenin en iyi yolu nedir? Önce çıkarsayın, sonra sağlamlaştırın: gerçek şekli elde etmek için gerçek öğeleri örnekleyin (betik veya DynoTable'ın dizinlenmiş dışa aktarımı), gözden geçirin ve gelecekteki sapma sınırda yakalansın diye onu elinizde tuttuğunuz bir Zod şemasına veya şema-öncelikli bir kütüphane varlığına terfi ettirin.

Tek tabloda birden çok varlık türünü nasıl ele alırım? Varlık başına bir tür, asla tek bir birleştirilmiş tür değil. Örneklemi tür özniteliğinize (veya anahtar önekine) göre bölün ve her biri için ayrı bir arayüz üretin — bunların ayrımlı birleşimi, tablonuzun türüdür.

Üretilen türlerim zorunlu bir alanı neden isteğe bağlı diyor? Çünkü örneklenen bir öğede o alan yoktu. Şemasız bir depoda isteğe bağlılık bir bildirim değil, bir gözlemdir — o öğelerin geriye doldurulacak eski satırlar mı (bkz. geçişler) yoksa gerçekten isteğe bağlı bir öznitelik mi olduğunu kontrol edin.

Türler DynamoDB kümelerini ve ikili verileri kapsıyor mu? Bir dönüştürücü düz-JSON temsilleri seçmek zorundadır: kümeler dizilere, ikili veri kodlanmış bir dizeye dönüşür — marshalling bölümünde ele alınan aynı eşleme tuhaflıkları. Özniteliklerinizin her iki tarafta tam olarak neye benzediğini görmek için bir örneği DynamoDB JSON dönüştürücüsünden gidiş-dönüş geçirin.

Tablonuzun şeklini tahmin etmeyi bırakın — DynoTable'ı indirin, tabloyu dizinleyin ve tek tıkla bir TypeScript, Zod veya JSON Schema dışa aktarın.

Güncellendi