Defining entities
Database entities can be managed in the same structure as well. With support to generated the query basics and query traversal helpers.
Getting started
import { TypeCreator } from "@compas/code-gen";
const T = new TypeCreator("database");
T.object("user")
.keys({
name: T.string(),
})
.enableQueries({});
// Results in
type DatabaseUser = {
id: string; // UUID primary key
name: string;
};
// All kinds of types can be used as fields.
T.object("post")
.keys({
title: T.string(),
createdBy: T.uuid(),
lastLikedAt: T.date(),
published: T.bool(),
metadata: T.object().keys({
hasCoverImage: T.bool(),
}),
})
.enableQueries({});
INFO
The generated output for entities will always end up in the $output/database/
directory. You can still use any other group name you like, but it is advised to use new TypeCreator("database")
for small to medium sized projects, grouping all entities declarations in a single file.
.enableQueries
options
import { TypeCreator } from "@compas/code-gen";
const T = new TypeCreator("database");
T.object("user")
.keys({
name: T.string(),
})
.enableQueries({
schema: "private",
}); // Use a schema name in the generated queries.
T.object("user")
.keys({
// Supply your own primary key
id: T.number().primary(),
name: T.string(),
})
.enableQueries({});
// Results in
type DatabaseUser = {
id: number;
name: string;
};
T.object("user")
.keys({
name: T.string(),
})
.enableQueries({
// Automatically managed `createdAt` and `updatedAt` fields
withDates: true,
});
// Results in
type DatabaseUser = {
id: string; // uuid primary key
name: string;
createdAt: Date;
updatedAt: Date;
};
Searchable
Fields need to be set .searchable()
explicitly, so they can be used in the generated filter and order options.
Relations
Compas supports one-to-one and many-to-one relations between entities. Many-to-many relations are not supported but can be achieved with multiple many-to-one relations and an explicit join entity.
T.object("user")
.keys({
// ...
})
.enableQueries({})
.relations(
// The virtual 1-M side of the relation ship
T.oneToMany("authoredPosts", T.reference("database", "post")),
);
T.object("userSettings")
.keys({
// ...
})
.enableQueries({})
.relations(
// Owning side of the 1-1 relation, the virtual side is automatically created
T.oneToOne("user", T.reference("database", "user"), "settings"),
);
T.object("post")
.keys({
// Explicit primary key
id: T.number().primary(), // ...
})
.enableQueries({})
.relations(
// Owning side of the M-1 relation
T.manyToOne("author", T.reference("database", "user"), "authoredPosts"),
);
T.object("postSettings")
.keys({
// ...
})
.enableQueries({})
.relations(
// Relations can be optional as well
T.oneToOne("post", T.reference("database", "post"), "settings").optional(),
);
// Results in the following types
type DatabaseUser = {
id: string; // ...
};
type DatabaseUserSettings = {
id: string;
user: string; // Primary key of the 'user' entity
// ...
};
type DatabasePost = {
id: number;
author: string; // Primary key of the 'user' entity
};
type DatabasePostSettings = {
id: string;
post: number; // Primary key of the 'post' entity
};
// If the target supports query traversal and select entity inclusion you get the most out of
// it; Note that each entity could reference it's relations. So with a single query you can
// get the user, with their settings, all their posts and for each post the post settings.
type QueryResultDatabaseUser = {
id: string;
// The optionally included 'userSettings'
settings?: QueryResultDatabaseUserSettings;
// The optionally included 'posts'
authoredPosts?: QueryResultDatabasePost[];
};
type QueryResultDatabaseUserSettings = {
id: string;
// If the user is not joined, the primary string is still returned
user: string | QueryResultDatabaseUser;
};
type QueryResultDatabasePost = {
id: number;
settings?: QueryResultDatabasePostSettings;
author: string | QueryResultDatabaseUser;
};
type QueryResultDatabasePostSettings = {
id: string;
post: number | QueryResultDatabasePost;
};
Soft deletes
Entity support also includes a basic soft-delete support
T.object("user")
.keys({
name: T.string(),
})
.enableQueries({
withDates: true,
// Adds `deletedAt?: Date`
withSoftDeletes: true,
});
All generated queries will ignore soft deletes by default, except when specifically included. The exception is for generated delete queries. These will not ignore already soft deleted records, and always hard delete them.