Entity
Eine Entity ist entweder eine Class oder ein Object Literal (Interface) und hat immer einen Primary Key.
Die Entity wird mit allen notwendigen Informationen über Type Annotations aus @deepkit/type
versehen. Zum Beispiel wird ein Primary Key definiert sowie verschiedene Felder und deren Validierungs-Constraints. Diese Felder spiegeln die Datenbankstruktur wider, in der Regel eine Tabelle oder eine Collection.
Durch spezielle Type Annotations wie Mapped<'name'>
kann ein Feldname auch auf einen anderen Namen in der Datenbank gemappt werden.
Class
import { entity, PrimaryKey, AutoIncrement, Unique, MinLength, MaxLength } from '@deepkit/type'; @entity.name('user') class User { id: number & PrimaryKey & AutoIncrement = 0; created: Date = new Date; firstName?: string; lastName?: string; constructor( public username: string & Unique & MinLength<2> & MaxLength<16>, public email: string & Unique, ) {} } const database = new Database(new SQLiteDatabaseAdapter(':memory:'), [User]); await database.migrate(); await database.persist(new User('Peter')); const allUsers = await database.query(User).find(); console.log('all users', allUsers);
Interface
import { PrimaryKey, AutoIncrement, Unique, MinLength, MaxLength } from '@deepkit/type'; interface User { id: number & PrimaryKey & AutoIncrement = 0; created: Date = new Date; firstName?: string; lastName?: string; username: string & Unique & MinLength<2> & MaxLength<16>; } const database = new Database(new SQLiteDatabaseAdapter(':memory:')); database.register<User>({name: 'user'}); await database.migrate(); const user: User = {id: 0, created: new Date, username: 'Peter'}; await database.persist(user); const allUsers = await database.query<User>().find(); console.log('all users', allUsers);
Primitive
Primitive Datentypen wie String, Number (bigint) und Boolean werden auf gängige Datenbanktypen gemappt. Es wird nur der TypeScript Type verwendet.
interface User { logins: number; username: string; pro: boolean; }
Primary Key
Jede Entity benötigt genau einen Primary Key. Mehrere Primary Keys werden nicht unterstützt.
Der Basistype eines Primary Keys kann beliebig sein. Häufig wird eine number oder eine UUID verwendet. Für MongoDB wird oft die MongoId oder ObjectID genutzt.
Für Zahlen kann AutoIncrement
verwendet werden.
import { PrimaryKey } from '@deepkit/type'; interface User { id: number & PrimaryKey; }
Auto Increment
Felder, die beim Insert automatisch inkrementiert werden sollen, werden mit dem AutoIncrement
Decorator annotiert. Alle Adapter unterstützen Auto-Increment-Werte. Der MongoDB Adapter verwendet eine zusätzliche Collection, um den Zähler nachzuverfolgen.
Ein Auto-Increment-Feld ist ein automatischer Zähler und kann nur auf einen Primary Key angewendet werden. Die Datenbank stellt automatisch sicher, dass eine ID nur einmal verwendet wird.
import { PrimaryKey, AutoIncrement } from '@deepkit/type'; interface User { id: number & PrimaryKey & AutoIncrement; }
UUID
Felder, die vom Type UUID (v4) sein sollen, werden mit dem Decorator UUID annotiert. Der Runtime Type ist string
und in der Datenbank selbst meist binär. Verwende die Function uuid()
, um eine neue UUID v4 zu erzeugen.
import { uuid, UUID, PrimaryKey } from '@deepkit/type'; class User { id: UUID & PrimaryKey = uuid(); }
MongoDB ObjectID
Felder, die in MongoDB vom Type ObjectID sein sollen, werden mit dem Decorator MongoId
annotiert. Der Runtime Type ist string
und in der Datenbank selbst ObjectId
(binär).
MongoID-Felder erhalten beim Insert automatisch einen neuen Wert. Es ist nicht zwingend erforderlich, den Feldnamen _id
zu verwenden. Er kann beliebig sein.
import { PrimaryKey, MongoId } from '@deepkit/type'; class User { id: MongoId & PrimaryKey = ''; }
Optional / Nullable
Optionale Felder werden als TypeScript Type mit title?: string
oder title: string | null
deklariert. Du solltest nur eine dieser Varianten verwenden, üblicherweise die optionale ?
-Syntax, die mit undefined
funktioniert.
Beide Varianten führen dazu, dass der Datenbanktyp bei allen SQL Adaptern NULLABLE
ist. Der einzige Unterschied zwischen diesen Varianten ist, dass sie zur Runtime unterschiedliche Werte repräsentieren.
Im folgenden Beispiel ist das Feld modified optional und kann zur Runtime daher undefined sein, obwohl es in der Datenbank immer als NULL dargestellt wird.
import { PrimaryKey } from '@deepkit/type'; class User { id: number & PrimaryKey = 0; modified?: Date; }
Dieses Beispiel zeigt, wie der Nullable Type funktioniert. NULL wird sowohl in der Datenbank als auch in der JavaScript-Runtime verwendet. Das ist ausführlicher als modified?: Date
und wird nicht häufig genutzt.
import { PrimaryKey } from '@deepkit/type'; class User { id: number & PrimaryKey = 0; modified: Date | null = null; }
Datenbank-Typ-Mapping
|=== |Laufzeittyp|SQLite|MySQL|Postgres|Mongo
|string|text|longtext|text|string |number|float|double|double precision|int/number |boolean|integer(1)|boolean|boolean|boolean |date|text|datetime|timestamp|datetime |array|text|json|jsonb|array |map|text|json|jsonb|object |map|text|json|jsonb|object |union|text|json|jsonb|T |uuid|blob|binary(16)|uuid|binary |ArrayBuffer/Uint8Array/...|blob|longblob|bytea|binary |===
Mit DatabaseField
ist es möglich, ein Feld auf jeden beliebigen Datenbanktyp zu mappen. Der Type muss ein gültiges SQL-Statement sein, das unverändert an das Migrationssystem weitergegeben wird.
import { DatabaseField } from '@deepkit/type'; interface User { title: string & DatabaseField<{type: 'VARCHAR(244)'}>; }
Um ein Feld für eine spezifische Datenbank zu mappen, können entweder SQLite
, MySQL
oder Postgres
verwendet werden.
SQLite
import { SQLite } from '@deepkit/type'; interface User { title: string & SQLite<{type: 'text'}>; }
MySQL
import { MySQL } from '@deepkit/type'; interface User { title: string & MySQL<{type: 'text'}>; }
Postgres
import { Postgres } from '@deepkit/type'; interface User { title: string & Postgres<{type: 'text'}>; }