API-Referenz: Typen¶
Diese Anleitung dokumentiert alle TypeScript-Typen, die von Dynamite ORM exportiert werden und die Erstellung von Modellen mit vollständiger Type-Safety ermöglichen.
Inhaltsverzeichnis¶
- Attribut-Markierungstypen
- CreationOptional\<T>
- NonAttribute\<T>
- Inferenz-Typen
- InferAttributes\<T>
- FilterableAttributes\<T>
- Beziehungstypen
- HasMany\<T>
- BelongsTo\<T>
- Abfrage-Typen
- QueryOperator
- QueryOptions\<T>
- QueryFilters\<T>
- WhereOptions\<T>
- IncludeRelationOptions
- Metadaten-Typen
- Column
- RelationMetadata
- ValidatorEntry
- SerializeConfig
Attribut-Markierungstypen¶
CreationOptional\<T>¶
Markiert ein Feld als optional während der Erstellung, aber vorhanden nach dem Speichern. Verwenden Sie dies für Felder mit Standardwerten, automatisch generierte oder automatisch berechnete Felder.
Syntax:
Merkmale: - Feld ist optional beim Aufruf von Model.create() - Feld ist in der Instanz nach dem Speichern vorhanden - Ideal für automatisch generierte IDs, Zeitstempel und Standardwerte
Beispiele:
import { Table, PrimaryKey, Default, CreatedAt, UpdatedAt, CreationOptional } from '@arcaelas/dynamite';
class User extends Table<User> {
// Automatisch generierte ID - IMMER CreationOptional verwenden
@PrimaryKey()
declare id: CreationOptional<string>;
// Erforderliches Feld während der Erstellung
@NotNull()
declare email: string;
// Feld mit Standardwert - CreationOptional verwenden
@Default(() => 'customer')
declare role: CreationOptional<string>;
// Boolean mit Standardwert - CreationOptional verwenden
@Default(() => true)
declare active: CreationOptional<boolean>;
// Numerisch mit Standardwert - CreationOptional verwenden
@Default(() => 0)
declare balance: CreationOptional<number>;
// Automatisch generierte Zeitstempel - IMMER CreationOptional verwenden
@CreatedAt()
declare createdAt: CreationOptional<string>;
@UpdatedAt()
declare updatedAt: CreationOptional<string>;
}
// Während der Erstellung: nur erforderliche Felder
const user = await User.create({
email: 'user@test.com'
// id, role, active, balance, createdAt, updatedAt sind optional
});
// Nach dem Speichern: alle Felder sind vorhanden
console.log(user.id); // string (automatisch generiert)
console.log(user.role); // 'customer' (Standardwert)
console.log(user.active); // true (Standardwert)
console.log(user.balance); // 0 (Standardwert)
console.log(user.createdAt); // string (automatisch generierter Zeitstempel)
console.log(user.updatedAt); // string (automatisch generierter Zeitstempel)
Verwendungsregel:
Verwenden Sie CreationOptional<T> für: 1. Felder mit Decorator @PrimaryKey() (automatisch generierte IDs) 2. Felder mit Decorator @Default() (Standardwerte) 3. Felder mit Decorator @CreatedAt() oder @UpdatedAt() (Zeitstempel) 4. Alle automatisch vom System berechneten Felder
NonAttribute\<T>¶
Markiert ein Feld als nicht persistent in der Datenbank. Verwenden Sie dies für berechnete Eigenschaften, Getter, Methoden oder temporäre Daten.
Syntax:
Merkmale: - Feld wird NICHT in DynamoDB gespeichert - Feld wird NICHT in Abfragen einbezogen - Ideal für berechnete Eigenschaften, Getter und Instanzmethoden - Wird automatisch von InferAttributes<T> ausgeschlossen
Beispiele:
import { Table, PrimaryKey, NotNull, NonAttribute } from '@arcaelas/dynamite';
class User extends Table<User> {
@PrimaryKey()
declare id: string;
@NotNull()
declare first_name: string;
@NotNull()
declare last_name: string;
@NotNull()
declare birth_date: string;
// Berechnete Eigenschaft - wird NICHT persistiert
declare full_name: NonAttribute<string>;
// Berechnete Eigenschaft - wird NICHT persistiert
declare age: NonAttribute<number>;
// Instanzmethode - wird NICHT persistiert
declare get_display_name: NonAttribute<() => string>;
constructor(data: any) {
super(data);
// Berechnete Eigenschaften berechnen
this.full_name = `${this.first_name} ${this.last_name}`;
const birth = new Date(this.birth_date);
const today = new Date();
this.age = today.getFullYear() - birth.getFullYear();
// Instanzmethode definieren
this.get_display_name = () => {
return `${this.full_name} (${this.age} Jahre)`;
};
}
}
const user = await User.create({
id: 'user-1',
first_name: 'Juan',
last_name: 'Pérez',
birth_date: '1990-05-15'
});
// Berechnete Eigenschaften verfügbar
console.log(user.full_name); // 'Juan Pérez'
console.log(user.age); // 34
console.log(user.get_display_name()); // 'Juan Pérez (34 Jahre)'
// Aber sie werden NICHT in der Datenbank gespeichert
// Nur persistiert werden: id, first_name, last_name, birth_date
Anwendungsfälle:
class Product extends Table<Product> {
@PrimaryKey()
declare id: string;
@NotNull()
declare name: string;
@NotNull()
declare price: number;
@Default(() => 0)
declare discount: CreationOptional<number>;
// Berechneter Endpreis - NonAttribute
declare final_price: NonAttribute<number>;
// Indikator ob im Angebot - NonAttribute
declare is_on_sale: NonAttribute<boolean>;
// Methode zum Anwenden von Rabatten - NonAttribute
declare apply_discount: NonAttribute<(additional: number) => number>;
constructor(data: any) {
super(data);
this.final_price = this.price * (1 - (this.discount ?? 0) / 100);
this.is_on_sale = (this.discount ?? 0) > 0;
this.apply_discount = (additional: number) => {
const total_discount = (this.discount ?? 0) + additional;
return this.price * (1 - total_discount / 100);
};
}
}
const product = await Product.create({
id: 'prod-1',
name: 'Laptop',
price: 1000,
discount: 10
});
console.log(product.final_price); // 900 (berechnet)
console.log(product.is_on_sale); // true (berechnet)
console.log(product.apply_discount(5)); // 850 (Methode)
Inferenz-Typen¶
InferAttributes\<T>¶
Leitet automatisch die persistenten Attribute eines Modells ab und schließt Beziehungen, Methoden und NonAttribute-Felder aus.
Syntax:
Merkmale: - Schließt automatisch Beziehungen aus (HasMany, BelongsTo) - Schließt automatisch NonAttribute-Felder aus - Schließt automatisch Methoden aus - Enthält nur Felder, die in DynamoDB persistiert werden - Wird intern von where(), create(), update() verwendet
Beispiele:
import { Table, PrimaryKey, NotNull, HasMany, BelongsTo, NonAttribute, InferAttributes } from '@arcaelas/dynamite';
class User extends Table<User> {
@PrimaryKey()
declare id: string;
@NotNull()
declare email: string;
@NotNull()
declare name: string;
@Default(() => 'customer')
declare role: CreationOptional<string>;
// Beziehung - ist KEIN persistentes Attribut
@HasMany(() => Order, 'user_id')
declare orders: any;
// Berechnetes Feld - ist KEIN persistentes Attribut
declare display_name: NonAttribute<string>;
}
// InferAttributes schließt 'orders' und 'display_name' aus
type UserAttributes = InferAttributes<User>;
// Entspricht:
// {
// id: string;
// email: string;
// name: string;
// role: string | undefined;
// }
// Verwendung in Abfragen
const users = await User.where({
role: 'customer',
// orders: {} // ❌ Fehler: 'orders' ist kein filterbares Attribut
// display_name: 'X' // ❌ Fehler: 'display_name' ist kein filterbares Attribut
});
// Verwendung in Updates
await User.update(
{
name: 'Neuer Name',
// orders: [] // ❌ Fehler: Beziehung kann nicht aktualisiert werden
// display_name: 'X' // ❌ Fehler: NonAttribute kann nicht aktualisiert werden
},
{ id: 'user-1' }
);
Interne Verwendung:
// Dynamite verwendet InferAttributes intern
class Table<T> {
// Konstruktor akzeptiert nur persistente Attribute
constructor(data: InferAttributes<T>) { }
// where() akzeptiert nur persistente Attribute als Filter
static async where<M extends Table>(
this: { new (data: InferAttributes<M>): M },
filters: Partial<InferAttributes<M>>
): Promise<M[]> { }
// update() erlaubt nur die Aktualisierung persistenter Attribute
static async update<M extends Table>(
this: { new (data: InferAttributes<M>): M },
updates: Partial<InferAttributes<M>>,
filters: Partial<InferAttributes<M>>
): Promise<number> { }
}
FilterableAttributes\<T>¶
Alias von InferAttributes<T>, der Attribute darstellt, die in Abfragefiltern verwendet werden können.
Syntax:
Merkmale: - Ist äquivalent zu InferAttributes<T> - Wird in WhereOptions<T> zur Validierung von Filtern verwendet - Semantischer bei Verwendung im Kontext von Abfragen
Beispiele:
import { Table, PrimaryKey, NotNull, FilterableAttributes } from '@arcaelas/dynamite';
class Product extends Table<Product> {
@PrimaryKey()
declare id: string;
@NotNull()
declare name: string;
@NotNull()
declare price: number;
@Default(() => 0)
declare stock: CreationOptional<number>;
@HasMany(() => Review, 'product_id')
declare reviews: any;
}
// FilterableAttributes enthält nur persistente Felder
type ProductFilters = FilterableAttributes<Product>;
// Entspricht:
// {
// id: string;
// name: string;
// price: number;
// stock: number | undefined;
// }
// Verwendung in Abfragen mit Type-Safety
const filters: Partial<ProductFilters> = {
price: 100,
stock: 50,
// reviews: [] // ❌ Fehler: 'reviews' ist nicht filterbar
};
const products = await Product.where(filters);
Beziehungstypen¶
HasMany\<T>¶
Repräsentiert eine Eins-zu-Viele-Beziehung, bei der das aktuelle Modell mehrere Instanzen des verknüpften Modells hat.
Syntax:
Merkmale: - Gibt Array von Instanzen des verknüpften Modells zurück - Wird über die Option include in Abfragen geladen - Unterstützt Filter, Limits und Sortierung in der Beziehung - Implementiert automatisches Lazy Loading
Grundlegende Beispiele:
import { Table, PrimaryKey, NotNull, HasMany } from '@arcaelas/dynamite';
class User extends Table<User> {
@PrimaryKey()
declare id: string;
@NotNull()
declare name: string;
// Ein Benutzer hat viele Bestellungen
@HasMany(() => Order, 'user_id')
declare orders: any;
// Ein Benutzer hat viele Bewertungen
@HasMany(() => Review, 'user_id')
declare reviews: any;
}
class Order extends Table<Order> {
@PrimaryKey()
declare id: string;
@NotNull()
declare user_id: string;
@NotNull()
declare total: number;
// Eine Bestellung hat viele Artikel
@HasMany(() => OrderItem, 'order_id')
declare items: any;
}
class OrderItem extends Table<OrderItem> {
@PrimaryKey()
declare id: string;
@NotNull()
declare order_id: string;
@NotNull()
declare product_id: string;
@NotNull()
declare quantity: number;
}
class Review extends Table<Review> {
@PrimaryKey()
declare id: string;
@NotNull()
declare user_id: string;
@NotNull()
declare product_id: string;
@NotNull()
declare rating: number;
}
Verwendungsbeispiele:
// 1. Einfaches Include
const users = await User.where({}, {
include: {
orders: {}
}
});
users.forEach(user => {
console.log(user.name);
console.log(user.orders); // Array von Order[]
});
// 2. Include mit Filtern
const users_with_pending = await User.where({}, {
include: {
orders: {
where: { status: 'pending' },
limit: 10,
order: 'DESC'
}
}
});
// 3. Include mit mehreren Beziehungen
const users_full = await User.where({ id: 'user-1' }, {
include: {
orders: {
include: {
items: {} // Verschachtelte Beziehung
}
},
reviews: {
where: { rating: 5 }
}
}
});
console.log(users_full[0].orders); // Order[]
console.log(users_full[0].orders[0].items); // OrderItem[]
console.log(users_full[0].reviews); // Review[]
// 4. Include mit selektiven Attributen
const users_minimal = await User.where({}, {
attributes: ['id', 'name'],
include: {
orders: {
attributes: ['id', 'total', 'status']
}
}
});
HasMany-Beziehungsoptionen:
interface IncludeOptions {
where?: Record<string, any>; // Filter für die Beziehung
attributes?: string[]; // Auszuwählende Felder
limit?: number; // Limit der Ergebnisse
skip?: number; // Offset für Paginierung
order?: 'ASC' | 'DESC'; // Sortierung
include?: Record<string, any>; // Verschachtelte Beziehungen
}
BelongsTo\<T>¶
Repräsentiert eine Viele-zu-Eins-Beziehung, bei der das aktuelle Modell zu einer Instanz des verknüpften Modells gehört.
Syntax:
Merkmale: - Gibt eine Instanz des verknüpften Modells oder null zurück - Wird über die Option include in Abfragen geladen - Verwendet den lokalen Schlüssel (Foreign Key), um den verknüpften Datensatz zu finden - Unterstützt verschachtelte Beziehungen
Grundlegende Beispiele:
import { Table, PrimaryKey, NotNull, BelongsTo } from '@arcaelas/dynamite';
class Order extends Table<Order> {
@PrimaryKey()
declare id: string;
@NotNull()
declare user_id: string;
@NotNull()
declare total: number;
// Eine Bestellung gehört zu einem Benutzer
@BelongsTo(() => User, 'user_id')
declare user: any;
}
class Product extends Table<Product> {
@PrimaryKey()
declare id: string;
@NotNull()
declare category_id: string;
@NotNull()
declare name: string;
// Ein Produkt gehört zu einer Kategorie
@BelongsTo(() => Category, 'category_id')
declare category: any;
}
class OrderItem extends Table<OrderItem> {
@PrimaryKey()
declare id: string;
@NotNull()
declare order_id: string;
@NotNull()
declare product_id: string;
// Ein Artikel gehört zu einer Bestellung
@BelongsTo(() => Order, 'order_id')
declare order: any;
// Ein Artikel gehört zu einem Produkt
@BelongsTo(() => Product, 'product_id')
declare product: any;
}
class Category extends Table<Category> {
@PrimaryKey()
declare id: string;
@NotNull()
declare name: string;
}
Verwendungsbeispiele:
// 1. Einfaches Include
const orders = await Order.where({}, {
include: {
user: {}
}
});
orders.forEach(order => {
console.log(order.total);
console.log(order.user.name); // User | null
});
// 2. Include mit verschachtelten Beziehungen
const products = await Product.where({}, {
include: {
category: {}
}
});
products.forEach(product => {
console.log(product.name);
if (product.category) {
console.log(product.category.name);
}
});
// 3. Include mit mehreren BelongsTo
const items = await OrderItem.where({}, {
include: {
order: {
include: {
user: {} // Verschachtelte Beziehung
}
},
product: {
include: {
category: {} // Verschachtelte Beziehung
}
}
}
});
items.forEach(item => {
console.log(item.order.user.name); // Benutzer der Bestellung
console.log(item.product.category.name); // Kategorie des Produkts
});
// 4. Include mit Filtern in übergeordneter Beziehung
const recent_orders = await Order.where(
{ status: 'delivered' },
{
include: {
user: {
where: { active: true }
}
}
}
);
Umgang mit Nullwerten:
const products = await Product.where({}, {
include: {
category: {}
}
});
products.forEach(product => {
// BelongsTo kann null zurückgeben, wenn die Beziehung nicht existiert
if (product.category) {
console.log(product.category.name);
} else {
console.log('Produkt ohne Kategorie');
}
});
Abfrage-Typen¶
QueryOperator¶
Verfügbare Operatoren für Filter in where-Abfragen.
Syntax:
type QueryOperator =
| '=' // Gleich
| '!=' // Ungleich
| '<' // Kleiner als
| '<=' // Kleiner oder gleich
| '>' // Größer als
| '>=' // Größer oder gleich
| 'in' // In Array
| 'not-in' // Nicht in Array
| 'contains' // Enthält (Strings)
| 'begins-with' // Beginnt mit (Strings)
Beispiele:
import { Table, PrimaryKey, NotNull } from '@arcaelas/dynamite';
class User extends Table<User> {
@PrimaryKey()
declare id: string;
@NotNull()
declare name: string;
@NotNull()
declare email: string;
@NotNull()
declare age: number;
@NotNull()
declare role: string;
@Default(() => true)
declare active: CreationOptional<boolean>;
}
// Operator '=' (implizit)
const admins = await User.where('role', 'admin');
const admins2 = await User.where('role', '=', 'admin');
// Operator '!='
const non_admins = await User.where('role', '!=', 'admin');
// Numerische Operatoren
const adults = await User.where('age', '>=', 18);
const young = await User.where('age', '<', 30);
const specific_age = await User.where('age', '>', 25);
const age_limit = await User.where('age', '<=', 65);
// Operator 'in'
const staff = await User.where('role', 'in', ['admin', 'employee']);
const staff2 = await User.where('role', ['admin', 'employee']); // Abkürzung
// Operator 'not-in'
const customers = await User.where('role', 'not-in', ['admin', 'employee']);
// Operator 'contains' (Strings)
const gmail_users = await User.where('email', 'contains', '@gmail.com');
const name_with_a = await User.where('name', 'contains', 'a');
// Operator 'begins-with' (Strings)
const admins_by_email = await User.where('email', 'begins-with', 'admin@');
const a_names = await User.where('name', 'begins-with', 'A');
Kombination von Operatoren:
// Mehrere Bedingungen mit Objekt
const active_admins = await User.where({
role: 'admin',
active: true
});
// Operatoren in Kette
const young_admins = await User.where('age', '<', 30);
const active_young_admins = young_admins.filter(u => u.active);
// Operatoren mit Optionen
const paginated = await User.where(
'age',
'>=',
18,
{
limit: 10,
skip: 20,
order: 'DESC'
}
);
QueryOptions\<T>¶
Query-Optionen für where(), first(), last(), etc. Dieser Typ definiert Paginierung, Sortierung, Attributauswahl und Includes.
Syntax:
interface QueryOptions<T> {
order?: "ASC" | "DESC";
skip?: number;
limit?: number;
attributes?: (keyof InferAttributes<T>)[];
include?: Record<string, IncludeRelationOptions | true>;
}
Eigenschaften:
| Eigenschaft | Typ | Beschreibung |
|---|---|---|
order | 'ASC' \| 'DESC' | Sortierung nach Sort Key |
skip | number | Offset für Paginierung |
limit | number | Limit der Ergebnisse |
attributes | (keyof InferAttributes<T>)[] | Auszuwählende Felder |
include | Record<string, IncludeRelationOptions \| true> | Einzuschließende Beziehungen |
Beispiele:
import { Table, PrimaryKey, HasMany, QueryOptions } from '@arcaelas/dynamite';
class User extends Table<User> {
@PrimaryKey()
declare id: string;
declare name: string;
declare email: string;
@HasMany(() => Order, 'user_id')
declare orders: any;
}
// Grundlegende Verwendung mit Paginierung
const options: QueryOptions<User> = {
limit: 10,
skip: 20,
order: 'DESC'
};
const users = await User.where({ role: 'customer' }, options);
// Mit Attributauswahl
const options2: QueryOptions<User> = {
attributes: ['id', 'name', 'email'],
limit: 50
};
const minimal_users = await User.where({}, options2);
// Mit Includes
const options3: QueryOptions<User> = {
include: {
orders: {
where: { status: 'pending' },
limit: 5,
order: 'DESC'
}
}
};
const users_with_orders = await User.where({}, options3);
QueryFilters\<T>¶
Alias von QueryOptions<T> ohne das where-Feld. Nützlich wenn Filter als erstes Argument übergeben werden.
Syntax:
Merkmale: - Ist WhereOptions<T> ohne die where-Eigenschaft - Verwendet wenn Filter separat als erstes Argument übergeben werden - Semantischer in Helper-Funktionen
Beispiele:
import { Table, PrimaryKey, HasMany, QueryFilters } from '@arcaelas/dynamite';
class User extends Table<User> {
@PrimaryKey()
declare id: string;
declare name: string;
@HasMany(() => Order, 'user_id')
declare orders: any;
}
// Filter getrennt von Optionen
const filters = { role: 'admin' };
const options: QueryFilters<User> = {
limit: 10,
skip: 0,
order: 'DESC',
attributes: ['id', 'name'],
include: {
orders: {}
}
};
const users = await User.where(filters, options);
// Nützlich in generischen Helper-Funktionen
async function find_paginated<T extends Table<T>>(
Model: { where: Function },
filters: Record<string, any>,
page: number,
page_size: number
): Promise<T[]> {
const options: QueryFilters<T> = {
limit: page_size,
skip: page * page_size,
order: 'ASC'
};
return Model.where(filters, options);
}
Hinweis:
WhereOptionsWithoutWhere<T>ist ein deprecated Alias vonQueryFilters<T>. VerwendeQueryFilters<T>in neuem Code.
WhereOptions\<T>¶
Vollständige Optionen für where-Abfragen, einschließlich Filter, Paginierung, Sortierung und Includes.
Syntax:
interface WhereOptions<T> {
where?: Partial<FilterableAttributes<T>>;
skip?: number;
limit?: number;
order?: 'ASC' | 'DESC';
attributes?: (keyof FilterableAttributes<T>)[];
include?: {
[K in keyof T]?: T[K] extends HasMany<any> | BelongsTo<any>
? IncludeOptions | {}
: never;
};
}
Eigenschaften:
| Eigenschaft | Typ | Beschreibung |
|---|---|---|
where | Partial<FilterableAttributes<T>> | Filter für die Abfrage |
skip | number | Offset für Paginierung |
limit | number | Limit der Ergebnisse |
order | 'ASC' \| 'DESC' | Sortierung nach Primärschlüssel |
attributes | (keyof FilterableAttributes<T>)[] | Auszuwählende Felder |
include | object | Einzuschließende Beziehungen |
Beispiele:
import { Table, PrimaryKey, NotNull, HasMany, WhereOptions } from '@arcaelas/dynamite';
class User extends Table<User> {
@PrimaryKey()
declare id: string;
@NotNull()
declare name: string;
@NotNull()
declare email: string;
@NotNull()
declare age: number;
@Default(() => 'customer')
declare role: CreationOptional<string>;
@HasMany(() => Order, 'user_id')
declare orders: any;
}
class Order extends Table<Order> {
@PrimaryKey()
declare id: string;
@NotNull()
declare user_id: string;
@NotNull()
declare total: number;
}
// Beispiel 1: Nur Filter
const options1: WhereOptions<User> = {
where: {
role: 'admin',
age: 30
}
};
const admins = await User.where({}, options1);
// Beispiel 2: Mit Paginierung
const options2: WhereOptions<User> = {
where: {
role: 'customer'
},
limit: 10,
skip: 20,
order: 'DESC'
};
const customers = await User.where({}, options2);
// Beispiel 3: Mit Attributauswahl
const options3: WhereOptions<User> = {
where: {
age: 25
},
attributes: ['id', 'name', 'email']
};
const users = await User.where({}, options3);
// Beispiel 4: Mit Includes
const options4: WhereOptions<User> = {
where: {
role: 'customer'
},
include: {
orders: {
where: { total: 100 },
limit: 5,
order: 'DESC'
}
}
};
const customers_with_orders = await User.where({}, options4);
// Beispiel 5: Vollständig
const options5: WhereOptions<User> = {
where: {
role: 'customer',
age: 30
},
attributes: ['id', 'name', 'email'],
limit: 20,
skip: 0,
order: 'ASC',
include: {
orders: {
attributes: ['id', 'total'],
where: { total: 500 },
limit: 10
}
}
};
const filtered = await User.where({}, options5);
Verwendung mit Operatoren:
// where() akzeptiert Operatoren und Optionen
const users = await User.where(
'age',
'>=',
18,
{
limit: 10,
order: 'DESC',
attributes: ['id', 'name'],
include: {
orders: {}
}
} as WhereOptions<User>
);
IncludeRelationOptions¶
Optionen für eingeschlossene Beziehungen in Abfragen.
Syntax:
interface IncludeRelationOptions {
where?: Record<string, any>;
attributes?: string[];
limit?: number;
skip?: number;
order?: "ASC" | "DESC";
include?: Record<string, IncludeRelationOptions | true>;
}
Eigenschaften:
| Eigenschaft | Typ | Beschreibung |
|---|---|---|
where | Record<string, any> | Filter für die Beziehung |
attributes | string[] | Auszuwählende Felder der Beziehung |
limit | number | Limit der Beziehungsergebnisse |
skip | number | Offset für Beziehungspaginierung |
order | 'ASC' \| 'DESC' | Sortierung der Beziehung |
include | Record<string, IncludeRelationOptions \| true> | Verschachtelte Includes |
Metadaten-Typen¶
Die folgenden Typen werden für Introspektion und Erstellung benutzerdefinierter Decorators verwendet.
Column¶
Konfigurationsmetadaten einer Spalte/Eigenschaft des Modells.
Syntax:
interface Column {
name: string;
nullable?: boolean;
default?: () => any;
index?: boolean;
indexSort?: boolean;
primaryKey?: boolean;
createdAt?: boolean;
updatedAt?: boolean;
softDelete?: boolean;
lazy_validators: Array<(value: any) => true | string>;
serialize?: SerializeConfig;
set: (fn: (current: any, next: any) => any) => void;
get: (fn: (current: any) => any) => void;
}
Verwendung: Dieser Typ wird hauptsächlich zum Erstellen benutzerdefinierter Decorators mit der decorator()-Factory verwendet.
import { decorator, Column } from '@arcaelas/dynamite';
// Benutzerdefinierten Decorator erstellen
const Encrypted = decorator<[key: string]>((col: Column, [key]) => {
col.set((_, value) => encrypt(value, key));
col.get((current) => decrypt(current, key));
});
RelationMetadata¶
Konfigurationsmetadaten einer Beziehung zwischen Modellen.
Syntax:
interface RelationMetadata {
type: "hasMany" | "belongsTo";
targetModel: () => any;
foreignKey: string;
localKey: string;
}
ValidatorEntry¶
Validator-Eintrag für Lazy-Validierung (ausgeführt bei save()).
Syntax:
SerializeConfig¶
Bidirektionale Serialisierungskonfiguration.
Syntax:
Häufige Muster¶
Vollständiges Modell mit allen Typen¶
import {
Table,
PrimaryKey,
NotNull,
Default,
CreatedAt,
UpdatedAt,
HasMany,
BelongsTo,
CreationOptional,
NonAttribute,
InferAttributes,
FilterableAttributes
} from '@arcaelas/dynamite';
class User extends Table<User> {
// Automatisch generierte ID - CreationOptional
@PrimaryKey()
declare id: CreationOptional<string>;
// Erforderliche Felder - keine Markierung
@NotNull()
declare email: string;
@NotNull()
declare name: string;
// Felder mit Standardwerten - CreationOptional
@Default(() => 'customer')
declare role: CreationOptional<string>;
@Default(() => true)
declare active: CreationOptional<boolean>;
@Default(() => 0)
declare balance: CreationOptional<number>;
// Automatisch generierte Zeitstempel - CreationOptional
@CreatedAt()
declare createdAt: CreationOptional<string>;
@UpdatedAt()
declare updatedAt: CreationOptional<string>;
// Berechnete Eigenschaften - NonAttribute
declare full_name: NonAttribute<string>;
declare display_role: NonAttribute<string>;
// Beziehungen - keine spezielle Markierung erforderlich
@HasMany(() => Order, 'user_id')
declare orders: any;
@HasMany(() => Review, 'user_id')
declare reviews: any;
constructor(data: InferAttributes<User>) {
super(data);
this.full_name = `${this.name} <${this.email}>`;
this.display_role = this.role === 'admin' ? 'Administrator' : 'Kunde';
}
}
// Während der Erstellung: nur erforderliche Felder
const user = await User.create({
email: 'user@test.com',
name: 'Juan Pérez'
});
// Nach dem Speichern: alle Felder vorhanden
console.log(user.id); // string (automatisch generiert)
console.log(user.role); // 'customer' (Standard)
console.log(user.active); // true (Standard)
console.log(user.balance); // 0 (Standard)
console.log(user.createdAt); // string (Zeitstempel)
console.log(user.full_name); // 'Juan Pérez <user@test.com>' (berechnet)
console.log(user.display_role); // 'Kunde' (berechnet)
Erweiterte Abfrage mit allen Typen¶
import { WhereOptions, QueryOperator } from '@arcaelas/dynamite';
// Generische Hilfsfunktion mit Type-Safety
async function find_advanced<T extends Table>(
Model: { new (data: InferAttributes<T>): T },
field: keyof FilterableAttributes<T>,
operator: QueryOperator,
value: any,
options?: QueryFilters<T>
): Promise<T[]> {
return Model.where(field as string, operator, value, options);
}
// Verwendung mit vollständiger Inferenz
const admins = await find_advanced(
User,
'role',
'=',
'admin',
{
limit: 10,
attributes: ['id', 'name', 'email'],
include: {
orders: {
where: { status: 'delivered' },
limit: 5
}
}
}
);
Zusammenfassung¶
| Typ | Zweck | Wann verwenden |
|---|---|---|
CreationOptional<T> | Optionale Felder bei Erstellung | Automatisch generierte IDs, Standardwerte, Zeitstempel |
NonAttribute<T> | Nicht persistente Felder | Berechnete Eigenschaften, Getter, Methoden |
InferAttributes<T> | Persistente Attribute | Type-Safety in Abfragen und Updates |
FilterableAttributes<T> | Filterbare Attribute | Type-Safety in where-Klauseln |
HasMany<T> | Eins-zu-Viele-Beziehung | Wenn ein Modell mehrere verknüpfte Instanzen hat |
BelongsTo<T> | Viele-zu-Eins-Beziehung | Wenn ein Modell zu einem anderen gehört |
QueryOperator | Abfrageoperatoren | Erweiterte Filter in where |
QueryOptions<T> | Query-Optionen | Paginierung, Sortierung, Includes |
QueryFilters<T> | Optionen ohne Filter | Abfragen, bei denen Filter separat übergeben werden |
WhereOptions<T> | Vollständige Optionen | Abfragen mit Filtern, Paginierung und Includes |
IncludeRelationOptions | Include-Optionen | Verschachtelte Beziehungen konfigurieren |
Column | Spaltenmetadaten | Benutzerdefinierte Decorators erstellen |
RelationMetadata | Beziehungsmetadaten | Introspektion von Beziehungen |
ValidatorEntry | Validatorfunktion | Benutzerdefinierte Validierungen |
SerializeConfig | Serialisierungskonfiguration | Bidirektionale Transformationen |
Best Practices¶
- Immer
CreationOptional<T>verwenden für: - Felder mit
@PrimaryKey()(automatisch generiert) - Felder mit
@Default()(Standardwerte) -
Felder mit
@CreatedAt()oder@UpdatedAt()(Zeitstempel) -
Immer
NonAttribute<T>verwenden für: - Im Konstruktor berechnete Eigenschaften
- Getter, die von anderen Feldern abgeleitet werden
-
Instanzmethoden
-
Beziehungen:
@HasManyfür Arrays von verknüpften Modellen verwenden@BelongsTofür einzelne Referenzen auf verknüpfte Modelle verwenden-
Keine zusätzlichen Typmarkierungen auf Beziehungen anwenden
-
Type-Safety:
InferAttributes<T>in generischen Funktionen verwendenFilterableAttributes<T>zur Validierung von Filtern verwenden-
WhereOptions<T>für vollständige Abfrageoptionen verwenden -
Performance:
- Nur notwendige Beziehungen mit
includeeinschließen attributesverwenden, um nur erforderliche Felder auszuwählenlimitundskipfür Paginierung anwenden
Für weitere Informationen siehe: - Installationsanleitung - Decorator-Referenz - Table-Referenz