Erste Schritte mit Dynamite¶
Willkommen bei Dynamite! Diese Anleitung führt Sie durch alles, was Sie wissen müssen, um mit diesem modernen, Decorator-basierten ORM für DynamoDB Anwendungen zu erstellen.
Voraussetzungen¶
Bevor Sie beginnen, stellen Sie sicher, dass Sie haben: - Node.js 16+ installiert - Grundkenntnisse in TypeScript - AWS-Konto (oder DynamoDB Local für die Entwicklung)
Installation¶
npm install @arcaelas/dynamite
# Peer-Abhängigkeiten (falls noch nicht installiert)
npm install @aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb
Konfiguration¶
Definieren Sie zuerst Ihre Modelle (siehe Schritt 1), dann konfigurieren Sie Ihre DynamoDB-Verbindung:
import { Dynamite } from "@arcaelas/dynamite";
// Für lokale Entwicklung
const dynamite = new Dynamite({
region: "us-east-1",
endpoint: "http://localhost:8000",
tables: [User, Order], // Ihre Modellklassen
credentials: {
accessKeyId: "test",
secretAccessKey: "test"
}
});
dynamite.connect();
await dynamite.sync();
// Für AWS-Produktion
const dynamite = new Dynamite({
region: "us-east-1",
tables: [User, Order],
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!
}
});
dynamite.connect();
await dynamite.sync();
Schritt 1: Ihr erstes Modell¶
Lassen Sie uns ein einfaches Benutzermodell erstellen. In Dynamite sind Modelle Klassen, die Table erweitern und Decorators verwenden, um ihre Struktur zu definieren.
import { Table, PrimaryKey, Default, CreationOptional } from "@arcaelas/dynamite";
class User extends Table<User> {
// Primärschlüssel mit automatisch generierter UUID
@PrimaryKey()
@Default(() => crypto.randomUUID())
declare id: CreationOptional<string>;
// Erforderliches Feld während der Erstellung
declare name: string;
// Optionales Feld mit Standardwert
@Default(() => "customer")
declare role: CreationOptional<string>;
}
Schlüsselkonzepte: - @PrimaryKey() markiert den Primärschlüssel (Partition Key in DynamoDB) - @Default() bietet automatische Standardwerte - CreationOptional<T> macht Felder während der Erstellung optional, aber in Instanzen erforderlich - declare ist TypeScript-Syntax für Klasseneigenschaften
Schritt 2: Datensätze erstellen¶
Es gibt mehrere Möglichkeiten, Datensätze in Dynamite zu erstellen:
Mit der create()-Methode¶
// Erstellen mit nur erforderlichen Feldern
const user1 = await User.create({
name: "John Doe"
// id und role sind optional (automatisch generiert/standardmäßig)
});
console.log(user1.id); // "550e8400-e29b-41d4-a716-446655440000"
console.log(user1.name); // "John Doe"
console.log(user1.role); // "customer"
// Erstellen mit allen Feldern
const user2 = await User.create({
id: "custom-id",
name: "Jane Smith",
role: "admin"
});
Mehrere Datensätze erstellen¶
const users = await Promise.all([
User.create({ name: "Alice" }),
User.create({ name: "Bob" }),
User.create({ name: "Charlie" })
]);
console.log(`${users.length} Benutzer erstellt`);
Schritt 3: Datensätze lesen¶
Dynamite bietet mehrere Methoden zum Abfragen Ihrer Daten:
Alle Datensätze abrufen¶
const all_users = await User.where({});
console.log(`Gesamtanzahl der Benutzer: ${all_users.length}`);
Nach Feldern filtern¶
// Nach exakter Übereinstimmung filtern
const admins = await User.where({ role: "admin" });
// Nach mehreren Bedingungen filtern
const admin_johns = await User.where({
name: "John Doe",
role: "admin"
});
Ersten oder letzten Datensatz abrufen¶
// Ersten Benutzer abrufen
const first_user = await User.first({});
// Ersten Administrator abrufen
const first_admin = await User.first({ role: "admin" });
// Letzten Benutzer abrufen
const last_user = await User.last({});
Erweiterte Abfragen mit Operatoren¶
// Größer oder gleich
const premium_users = await User.where("id", ">=", "user-100");
// String enthält
const gmail_users = await User.where("name", "contains", "gmail");
// In Array
const special_roles = await User.where("role", "in", ["admin", "premium", "vip"]);
// Nicht gleich
const non_customers = await User.where("role", "!=", "customer");
Abfrage mit Optionen¶
// Ergebnisse begrenzen
const first_10_users = await User.where({}, { limit: 10 });
// Paginierung (skip und limit)
const page_2_users = await User.where({}, {
limit: 10,
skip: 10
});
// Sortierreihenfolge
const sorted_users = await User.where({}, { order: "DESC" });
// Spezifische Attribute auswählen
const user_names = await User.where({}, {
attributes: ["id", "name"]
});
Schritt 4: Datensätze aktualisieren¶
Sie können Datensätze mit Instanz- oder statischen Methoden aktualisieren:
Mit der Instanz-save()-Methode¶
// Benutzer abrufen
const user = await User.first({ name: "John Doe" });
if (user) {
// Eigenschaften ändern
user.name = "John Smith";
user.role = "premium";
// Änderungen speichern
await user.save();
console.log("Benutzer erfolgreich aktualisiert");
}
Mit der Instanz-update()-Methode¶
const user = await User.first({ name: "John Doe" });
if (user) {
// Mehrere Felder gleichzeitig aktualisieren
await user.update({
name: "John Smith",
role: "premium"
});
}
Mit der statischen update()-Methode¶
// Nach Filter aktualisieren - gibt Anzahl aktualisierter Datensätze zurück
await User.update(
{ name: "John Smith", role: "premium" }, // Aktualisierungen
{ id: "user-123" } // Filter
);
Batch-Aktualisierungen¶
const users = await User.where({ role: "customer" });
// Alle Kunden auf Premium aktualisieren
await Promise.all(users.map(user => {
user.role = "premium";
return user.save();
}));
Schritt 5: Datensätze löschen¶
Löschen Sie Datensätze mit Instanz- oder statischen Methoden:
Mit der Instanz-destroy()-Methode¶
const user = await User.first({ name: "John Doe" });
if (user) {
await user.destroy();
console.log("Benutzer gelöscht");
}
Mit der statischen delete()-Methode¶
Batch-Löschung¶
const inactive_users = await User.where({ active: false });
// Alle inaktiven Benutzer löschen
await Promise.all(inactive_users.map(user => user.destroy()));
Schritt 6: Zeitstempel hinzufügen¶
Zeitstempel verfolgen, wann Datensätze erstellt und aktualisiert werden. Verwenden Sie die Decorators @CreatedAt und @UpdatedAt:
import {
Table,
PrimaryKey,
Default,
CreatedAt,
UpdatedAt,
CreationOptional
} from "@arcaelas/dynamite";
class User extends Table<User> {
@PrimaryKey()
@Default(() => crypto.randomUUID())
declare id: CreationOptional<string>;
declare name: string;
@Default(() => "customer")
declare role: CreationOptional<string>;
// Automatisch bei Erstellung gesetzt
@CreatedAt()
declare created_at: CreationOptional<string>;
// Automatisch beim Speichern aktualisiert
@UpdatedAt()
declare updated_at: CreationOptional<string>;
}
// Verwendung
const user = await User.create({ name: "John Doe" });
console.log(user.created_at); // "2024-01-15T10:30:00.000Z"
console.log(user.updated_at); // "2024-01-15T10:30:00.000Z"
// Benutzer aktualisieren
user.name = "John Smith";
await user.save();
console.log(user.updated_at); // "2024-01-15T10:35:00.000Z" (aktualisiert!)
Schritt 7: Vollständiges Arbeitsbeispiel¶
Hier ist ein vollständiges Beispiel, das alles zusammenbringt - ein einfaches Aufgabenverwaltungssystem:
import {
Table,
PrimaryKey,
Default,
CreatedAt,
UpdatedAt,
Validate,
Mutate,
NotNull,
CreationOptional,
NonAttribute,
Dynamite
} from "@arcaelas/dynamite";
// Task-Modell zuerst definieren
class Task extends Table<Task> {
// Automatisch generierte ID
@PrimaryKey()
@Default(() => crypto.randomUUID())
declare id: CreationOptional<string>;
// Erforderlicher Titel mit Validierung
@NotNull()
@Mutate((value) => (value as string).trim())
@Validate((value) => (value as string).length >= 3 || "Titel muss mindestens 3 Zeichen lang sein")
declare title: string;
// Optionale Beschreibung
@Default(() => "")
declare description: CreationOptional<string>;
// Status mit Standardwert
@Default(() => "pending")
@Validate((value) => ["pending", "in_progress", "completed"].includes(value as string) || "Ungültiger Status")
declare status: CreationOptional<string>;
// Priorität mit Validierung
@Default(() => 1)
@Validate((value) => (value as number) >= 1 && (value as number) <= 5 || "Priorität muss zwischen 1 und 5 liegen")
declare priority: CreationOptional<number>;
// Zeitstempel
@CreatedAt()
declare created_at: CreationOptional<string>;
@UpdatedAt()
declare updated_at: CreationOptional<string>;
// Berechnete Eigenschaft (nicht in Datenbank gespeichert)
declare display_title: NonAttribute<string>;
constructor(data?: any) {
super(data);
// Berechnete Eigenschaft definieren
Object.defineProperty(this, 'display_title', {
get: () => `[${this.status.toUpperCase()}] ${this.title}`,
enumerable: true
});
}
}
// DynamoDB konfigurieren und verbinden
const dynamite = new Dynamite({
region: "us-east-1",
endpoint: "http://localhost:8000",
tables: [Task],
credentials: {
accessKeyId: "test",
secretAccessKey: "test"
}
});
dynamite.connect();
await dynamite.sync();
// Hauptanwendung
async function main() {
console.log("=== Aufgabenverwaltungssystem ===\n");
// 1. Aufgaben erstellen
console.log("1. Aufgaben erstellen...");
const task1 = await Task.create({
title: "Dokumentation schreiben",
description: "Die Erste-Schritte-Anleitung vervollständigen",
priority: 3
});
console.log(`Erstellt: ${task1.display_title}`);
const task2 = await Task.create({
title: "Fehler in API beheben",
priority: 5
});
console.log(`Erstellt: ${task2.display_title}`);
const task3 = await Task.create({
title: "Pull Request überprüfen",
priority: 2
});
console.log(`Erstellt: ${task3.display_title}\n`);
// 2. Alle Aufgaben abrufen
console.log("2. Alle Aufgaben auflisten...");
const all_tasks = await Task.where({});
all_tasks.forEach(task => {
console.log(` - ${task.title} (Priorität: ${task.priority})`);
});
console.log();
// 3. Aufgaben nach Status filtern
console.log("3. Ausstehende Aufgaben filtern...");
const pending_tasks = await Task.where({ status: "pending" });
console.log(`${pending_tasks.length} ausstehende Aufgaben gefunden\n`);
// Finale Anzahl
const final_count = await Task.where({});
console.log(`=== Endgültige Aufgabenanzahl: ${final_count.length} ===`);
}
// Anwendung ausführen
main().catch(console.error);
Das Beispiel verstehen¶
Lassen Sie uns die Schlüsselteile aufschlüsseln:
Modelldefinition¶
class Task extends Table<Task> {
@PrimaryKey()
@Default(() => crypto.randomUUID())
declare id: CreationOptional<string>;
// ...
}
Table<Task> für ORM-Funktionalität - Decorators definieren Feldverhalten - CreationOptional macht Felder während der Erstellung optional Datenvalidierung¶
@Validate((value) => (value as string).length >= 3 || "Titel muss mindestens 3 Zeichen lang sein")
declare title: string;
true oder Fehlermeldungszeichenfolge zurück Datentransformation¶
- Transformiert Daten vor dem Speichern - Nützlich für Normalisierung (trim, lowercase, etc.)Berechnete Eigenschaften¶
declare display_title: NonAttribute<string>;
constructor(data?: any) {
super(data);
Object.defineProperty(this, 'display_title', {
get: () => `[${this.status.toUpperCase()}] ${this.title}`,
enumerable: true
});
}
NonAttribute schließt von Datenbank aus - Dynamisch aus anderen Feldern berechnet - Nicht gespeichert, bei Zugriff neu berechnet Nächste Schritte¶
Jetzt, da Sie die Grundlagen verstehen, erkunden Sie diese erweiterten Themen:
Kernkonzepte¶
Lernen Sie die grundlegenden Konzepte und Architektur kennen: - Kernkonzepte - Tiefer Einblick in Decorators, Modelle und Beziehungen
Erweiterte Funktionen¶
- Beziehungen - Definieren Sie Eins-zu-Viele- und Viele-zu-Eins-Beziehungen
- Komplexe Abfragen - Erweiterte Filterung und Abfrageerstellung
- Datenvalidierung - Benutzerdefinierte Validatoren und Transformationen
- TypeScript-Typen - Vollständige Typsicherheit mit
CreationOptionalundNonAttribute
Best Practices¶
- Definieren Sie immer einen
@PrimaryKey() - Verwenden Sie
CreationOptionalfür Felder mit@Default,@CreatedAt,@UpdatedAt - Verwenden Sie
NonAttributefür berechnete Eigenschaften - Validieren Sie Benutzereingaben mit
@Validate - Transformieren Sie Daten mit
@Mutatevor der Validierung - Verwenden Sie spezifische Attributauswahl, um Datenübertragung zu reduzieren
- Behandeln Sie Fehler elegant mit Try-Catch-Blöcken
Zusätzliche Ressourcen¶
- API-Referenz - Vollständige API-Dokumentation
- Beispiele - Weitere Codebeispiele
- GitHub Issues - Häufige Probleme und Lösungen
Schnellreferenz¶
Wesentliche Decorators¶
| Decorator | Zweck | Beispiel |
|---|---|---|
@PrimaryKey() | Primärschlüssel | @PrimaryKey() declare id: string |
@Default(fn) | Standardwert | @Default(() => uuid()) declare id: string |
@CreatedAt() | Auto-Zeitstempel bei Erstellung | @CreatedAt() declare created_at: string |
@UpdatedAt() | Auto-Zeitstempel bei Aktualisierung | @UpdatedAt() declare updated_at: string |
@Validate(fn) | Validierung | @Validate((v) => v.length > 0) declare name: string |
@Mutate(fn) | Daten transformieren | @Mutate((v) => v.trim()) declare email: string |
@NotNull() | Nicht-Null-Prüfung | @NotNull() declare email: string |
Wesentliche Typen¶
| Typ | Zweck | Verwendung |
|---|---|---|
CreationOptional<T> | Optional bei Erstellung | Felder mit @Default, @CreatedAt, @UpdatedAt |
NonAttribute<T> | Nicht in DB gespeichert | Berechnete Eigenschaften, Getter, Methoden |
CRUD-Operationen¶
// Erstellen
const user = await User.create({ name: "John" });
// Lesen
const users = await User.where({ active: true });
const user = await User.first({ id: "123" });
// Aktualisieren
user.name = "Jane";
await user.save();
// oder
await User.update({ name: "Jane" }, { id: "123" });
// Löschen
await user.destroy();
// oder
await User.delete({ id: "123" });
Hilfe erhalten¶
Wenn Sie auf Probleme stoßen: 1. Überprüfen Sie die API-Referenz 2. Suchen Sie in bestehenden GitHub-Issues 3. Erstellen Sie ein neues Issue mit einem minimalen reproduzierbaren Beispiel
Viel Spaß beim Programmieren mit Dynamite!