@antelopejs/interface-database-decorators 0.1.5 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -2
- package/dist/antelope.test.js +4 -3
- package/dist/antelope.test.js.map +1 -1
- package/dist/common.d.ts +2 -1
- package/dist/common.js +5 -1
- package/dist/common.js.map +1 -1
- package/dist/database.js +12 -8
- package/dist/database.js.map +1 -1
- package/dist/model.d.ts +2 -2
- package/dist/model.js +10 -10
- package/dist/model.js.map +1 -1
- package/dist/modifiers/autodate.js.map +1 -1
- package/dist/modifiers/common.js.map +1 -1
- package/dist/modifiers/encryption.js +1 -1
- package/dist/modifiers/encryption.js.map +1 -1
- package/dist/modifiers/hash.js +1 -1
- package/dist/modifiers/hash.js.map +1 -1
- package/dist/modifiers/localization.js.map +1 -1
- package/dist/relation.js.map +1 -1
- package/dist/schema.js.map +1 -1
- package/dist/table.d.ts +21 -5
- package/dist/table.js +8 -8
- package/dist/table.js.map +1 -1
- package/dist/tests/common.test.js +1 -1
- package/dist/tests/common.test.js.map +1 -1
- package/dist/tests/database.test.js +80 -2
- package/dist/tests/database.test.js.map +1 -1
- package/dist/tests/integration.test.js +4 -4
- package/dist/tests/integration.test.js.map +1 -1
- package/dist/tests/model.test.js +4 -4
- package/dist/tests/model.test.js.map +1 -1
- package/dist/tests/modifiers/autodate.test.js +3 -3
- package/dist/tests/modifiers/autodate.test.js.map +1 -1
- package/dist/tests/modifiers/common.test.js +2 -2
- package/dist/tests/modifiers/common.test.js.map +1 -1
- package/dist/tests/modifiers/encryption.test.js +2 -2
- package/dist/tests/modifiers/encryption.test.js.map +1 -1
- package/dist/tests/modifiers/hash.test.js +2 -2
- package/dist/tests/modifiers/hash.test.js.map +1 -1
- package/dist/tests/modifiers/localization.test.js +2 -2
- package/dist/tests/modifiers/localization.test.js.map +1 -1
- package/dist/tests/relation.test.js +2 -2
- package/dist/tests/relation.test.js.map +1 -1
- package/dist/tests/schema.test.js +2 -2
- package/dist/tests/schema.test.js.map +1 -1
- package/dist/tests/table.test.js +11 -11
- package/dist/tests/table.test.js.map +1 -1
- package/docs/1.introduction.md +22 -0
- package/docs/2.table-definitions.md +311 -0
- package/docs/3.table-modifiers.md +403 -0
- package/docs/4.data-models.md +183 -0
- package/docs/5.parameter-decoration.md +166 -0
- package/package.json +73 -59
- package/skills/database-decorators-interface/SKILL.md +143 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# Parameter Decoration
|
|
2
|
+
|
|
3
|
+
The Database Decorators package provides the `Model` decorator for injecting data model instances into API controller methods. This decorator integrates with the `@antelopejs/interface-api` package's parameter provider system.
|
|
4
|
+
|
|
5
|
+
## The Model Decorator
|
|
6
|
+
|
|
7
|
+
The `Model` decorator injects a cached model instance. It can be used as either a parameter decorator or a property decorator.
|
|
8
|
+
|
|
9
|
+
### Static Instance ID
|
|
10
|
+
|
|
11
|
+
Pass a fixed instance ID to bind the model to a specific schema instance:
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { Controller, Get } from "@antelopejs/interface-api";
|
|
15
|
+
import { Model } from "@antelopejs/interface-database-decorators";
|
|
16
|
+
import { UserModel } from "./user.model";
|
|
17
|
+
|
|
18
|
+
class UsersController extends Controller("/users") {
|
|
19
|
+
@Get()
|
|
20
|
+
async listUsers(
|
|
21
|
+
@Model(UserModel, "main") userModel: InstanceType<typeof UserModel>,
|
|
22
|
+
) {
|
|
23
|
+
return await userModel.getAll();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Dynamic Instance ID
|
|
29
|
+
|
|
30
|
+
Pass a callback function to determine the instance ID at request time. The callback receives the request context:
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
import { Controller, Get, Parameter } from "@antelopejs/interface-api";
|
|
34
|
+
import { Model } from "@antelopejs/interface-database-decorators";
|
|
35
|
+
import { UserModel } from "./user.model";
|
|
36
|
+
|
|
37
|
+
class MultitenantController extends Controller("/tenants") {
|
|
38
|
+
@Get("/:tenantId/users")
|
|
39
|
+
async getTenantUsers(
|
|
40
|
+
@Parameter("tenantId") tenantId: string,
|
|
41
|
+
@Model(UserModel, (ctx) => `tenant-${ctx.params.tenantId}`)
|
|
42
|
+
userModel: InstanceType<typeof UserModel>,
|
|
43
|
+
) {
|
|
44
|
+
return await userModel.getAll();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Property Decoration
|
|
50
|
+
|
|
51
|
+
The `Model` decorator also works as a property decorator. The model is initialized when the controller handles a request:
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
import { Controller, Get, Parameter } from "@antelopejs/interface-api";
|
|
55
|
+
import { Model } from "@antelopejs/interface-database-decorators";
|
|
56
|
+
import { UserModel } from "./models";
|
|
57
|
+
|
|
58
|
+
class UsersController extends Controller("/users") {
|
|
59
|
+
@Model(UserModel, "main")
|
|
60
|
+
private userModel!: InstanceType<typeof UserModel>;
|
|
61
|
+
|
|
62
|
+
@Get()
|
|
63
|
+
async listUsers() {
|
|
64
|
+
return await this.userModel.getAll();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
@Get("/:id")
|
|
68
|
+
async getUser(@Parameter("id") id: string) {
|
|
69
|
+
return await this.userModel.get(id);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Model Caching
|
|
75
|
+
|
|
76
|
+
The `Model` decorator uses `GetModel` internally, which caches model instances by class and instance ID. Multiple decorators referencing the same model class and instance ID share a single instance:
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
import { GetModel } from "@antelopejs/interface-database-decorators";
|
|
80
|
+
|
|
81
|
+
// These return the same instance
|
|
82
|
+
const model1 = GetModel(UserModel, "main");
|
|
83
|
+
const model2 = GetModel(UserModel, "main");
|
|
84
|
+
|
|
85
|
+
// This returns a different instance
|
|
86
|
+
const model3 = GetModel(UserModel, "other");
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Combine with API Decorators
|
|
90
|
+
|
|
91
|
+
Model decorators compose naturally with other API decorators for building complete CRUD controllers:
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
import {
|
|
95
|
+
Controller,
|
|
96
|
+
Get,
|
|
97
|
+
Post,
|
|
98
|
+
Put,
|
|
99
|
+
Delete,
|
|
100
|
+
RawBody,
|
|
101
|
+
Parameter,
|
|
102
|
+
} from "@antelopejs/interface-api";
|
|
103
|
+
import { Model } from "@antelopejs/interface-database-decorators";
|
|
104
|
+
import { UserModel } from "./user.model";
|
|
105
|
+
|
|
106
|
+
class UsersController extends Controller("/users") {
|
|
107
|
+
@Get()
|
|
108
|
+
async list(@Model(UserModel, "main") model: InstanceType<typeof UserModel>) {
|
|
109
|
+
return await model.getAll();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
@Get("/:id")
|
|
113
|
+
async get(
|
|
114
|
+
@Parameter("id") id: string,
|
|
115
|
+
@Model(UserModel, "main") model: InstanceType<typeof UserModel>,
|
|
116
|
+
) {
|
|
117
|
+
return await model.get(id);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
@Post()
|
|
121
|
+
async create(
|
|
122
|
+
@RawBody() body: Buffer,
|
|
123
|
+
@Model(UserModel, "main") model: InstanceType<typeof UserModel>,
|
|
124
|
+
) {
|
|
125
|
+
const data = JSON.parse(body.toString());
|
|
126
|
+
return await model.insert(data);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
@Put("/:id")
|
|
130
|
+
async update(
|
|
131
|
+
@Parameter("id") id: string,
|
|
132
|
+
@RawBody() body: Buffer,
|
|
133
|
+
@Model(UserModel, "main") model: InstanceType<typeof UserModel>,
|
|
134
|
+
) {
|
|
135
|
+
const data = JSON.parse(body.toString());
|
|
136
|
+
return await model.update(id, data);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
@Delete("/:id")
|
|
140
|
+
async remove(
|
|
141
|
+
@Parameter("id") id: string,
|
|
142
|
+
@Model(UserModel, "main") model: InstanceType<typeof UserModel>,
|
|
143
|
+
) {
|
|
144
|
+
return await model.delete(id);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Custom Parameter Providers
|
|
150
|
+
|
|
151
|
+
For more complex injection logic, create custom parameter providers using the core API:
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
import { SetParameterProvider } from "@antelopejs/interface-api";
|
|
155
|
+
import { GetModel } from "@antelopejs/interface-database-decorators";
|
|
156
|
+
import { MakeParameterAndPropertyDecorator } from "@antelopejs/interface-core/decorators";
|
|
157
|
+
|
|
158
|
+
const CustomModel = MakeParameterAndPropertyDecorator(
|
|
159
|
+
(target, key, index, modelClass, resolver) => {
|
|
160
|
+
SetParameterProvider(target, key, index, (ctx) => {
|
|
161
|
+
const instanceId = resolver(ctx);
|
|
162
|
+
return GetModel(modelClass, instanceId);
|
|
163
|
+
});
|
|
164
|
+
},
|
|
165
|
+
);
|
|
166
|
+
```
|
package/package.json
CHANGED
|
@@ -1,25 +1,63 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@antelopejs/interface-database-decorators",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "AntelopeJS interface for database schema decorators and model definitions",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"antelopejs",
|
|
7
|
-
"interface",
|
|
8
7
|
"database",
|
|
9
8
|
"decorators",
|
|
9
|
+
"interface",
|
|
10
10
|
"orm"
|
|
11
11
|
],
|
|
12
|
+
"license": "Apache-2.0",
|
|
12
13
|
"repository": {
|
|
13
14
|
"type": "git",
|
|
14
15
|
"url": "git+https://github.com/AntelopeJS/interface-database-decorators.git"
|
|
15
16
|
},
|
|
16
|
-
"license": "Apache-2.0",
|
|
17
|
-
"packageManager": "pnpm@10.6.5",
|
|
18
|
-
"main": "dist/index.js",
|
|
19
|
-
"types": "dist/index.d.ts",
|
|
20
17
|
"files": [
|
|
21
|
-
"dist"
|
|
18
|
+
"dist",
|
|
19
|
+
"docs",
|
|
20
|
+
"skills"
|
|
22
21
|
],
|
|
22
|
+
"main": "dist/index.js",
|
|
23
|
+
"types": "dist/index.d.ts",
|
|
24
|
+
"typesVersions": {
|
|
25
|
+
"*": {
|
|
26
|
+
"common": [
|
|
27
|
+
"dist/common.d.ts"
|
|
28
|
+
],
|
|
29
|
+
"database": [
|
|
30
|
+
"dist/database.d.ts"
|
|
31
|
+
],
|
|
32
|
+
"model": [
|
|
33
|
+
"dist/model.d.ts"
|
|
34
|
+
],
|
|
35
|
+
"relation": [
|
|
36
|
+
"dist/relation.d.ts"
|
|
37
|
+
],
|
|
38
|
+
"schema": [
|
|
39
|
+
"dist/schema.d.ts"
|
|
40
|
+
],
|
|
41
|
+
"table": [
|
|
42
|
+
"dist/table.d.ts"
|
|
43
|
+
],
|
|
44
|
+
"modifiers/autodate": [
|
|
45
|
+
"dist/modifiers/autodate.d.ts"
|
|
46
|
+
],
|
|
47
|
+
"modifiers/common": [
|
|
48
|
+
"dist/modifiers/common.d.ts"
|
|
49
|
+
],
|
|
50
|
+
"modifiers/encryption": [
|
|
51
|
+
"dist/modifiers/encryption.d.ts"
|
|
52
|
+
],
|
|
53
|
+
"modifiers/hash": [
|
|
54
|
+
"dist/modifiers/hash.d.ts"
|
|
55
|
+
],
|
|
56
|
+
"modifiers/localization": [
|
|
57
|
+
"dist/modifiers/localization.d.ts"
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
},
|
|
23
61
|
"exports": {
|
|
24
62
|
".": {
|
|
25
63
|
"types": "./dist/index.d.ts",
|
|
@@ -71,83 +109,59 @@
|
|
|
71
109
|
},
|
|
72
110
|
"./package.json": "./package.json"
|
|
73
111
|
},
|
|
112
|
+
"publishConfig": {
|
|
113
|
+
"access": "public",
|
|
114
|
+
"provenance": true
|
|
115
|
+
},
|
|
74
116
|
"scripts": {
|
|
75
117
|
"build": "rimraf dist && tsc",
|
|
76
|
-
"format": "
|
|
77
|
-
"lint:fix": "
|
|
78
|
-
"lint": "
|
|
118
|
+
"format": "oxfmt .",
|
|
119
|
+
"lint:fix": "oxlint --fix",
|
|
120
|
+
"lint": "oxlint",
|
|
79
121
|
"prepack": "pnpm run build",
|
|
80
122
|
"release": "pnpm run lint && pnpm run prepack && release-it",
|
|
81
|
-
"test": "pnpm run build && ajs module test ."
|
|
123
|
+
"test": "pnpm run build && ajs module test .",
|
|
124
|
+
"format:check": "oxfmt --check .",
|
|
125
|
+
"knip": "knip"
|
|
82
126
|
},
|
|
83
127
|
"dependencies": {
|
|
84
128
|
"randomstring": "^1.3.1",
|
|
85
129
|
"reflect-metadata": "^0.2.2"
|
|
86
130
|
},
|
|
87
131
|
"devDependencies": {
|
|
88
|
-
"@antelopejs/
|
|
89
|
-
"@antelopejs/interface-
|
|
90
|
-
"@antelopejs/interface-
|
|
91
|
-
"@
|
|
132
|
+
"@antelopejs/core": "^1.9.1",
|
|
133
|
+
"@antelopejs/interface-api": "^0.0.13",
|
|
134
|
+
"@antelopejs/interface-core": "^0.1.1",
|
|
135
|
+
"@antelopejs/interface-database": "^0.1.8",
|
|
136
|
+
"@antelopejs/tooling-configs": "^0.0.6",
|
|
92
137
|
"@types/chai": "^4.3.20",
|
|
93
138
|
"@types/mocha": "^10.0.10",
|
|
94
139
|
"@types/node": "^22.19.15",
|
|
95
140
|
"@types/randomstring": "^1.3.0",
|
|
96
141
|
"@types/semver": "^7.7.1",
|
|
97
142
|
"chai": "^4.5.0",
|
|
143
|
+
"eslint-plugin-perfectionist": "^5.11.0",
|
|
144
|
+
"knip": "^6.34.0",
|
|
98
145
|
"mongodb-memory-server-core": "^10.4.3",
|
|
146
|
+
"oxfmt": "^0.66.0",
|
|
147
|
+
"oxlint": "1.81.0",
|
|
148
|
+
"oxlint-tsgolint": "^7.0.2001",
|
|
99
149
|
"release-it": "^19.2.4",
|
|
100
150
|
"release-it-changelogen": "^0.1.0",
|
|
101
151
|
"rimraf": "^6.1.3",
|
|
102
152
|
"typescript": "^5.9.3"
|
|
103
153
|
},
|
|
104
154
|
"peerDependencies": {
|
|
105
|
-
"@antelopejs/interface-api": ">=0.0.
|
|
106
|
-
"@antelopejs/interface-core": ">=0.
|
|
107
|
-
"@antelopejs/interface-database": ">=0.1.
|
|
108
|
-
},
|
|
109
|
-
"publishConfig": {
|
|
110
|
-
"access": "public"
|
|
111
|
-
},
|
|
112
|
-
"typesVersions": {
|
|
113
|
-
"*": {
|
|
114
|
-
"common": [
|
|
115
|
-
"dist/common.d.ts"
|
|
116
|
-
],
|
|
117
|
-
"database": [
|
|
118
|
-
"dist/database.d.ts"
|
|
119
|
-
],
|
|
120
|
-
"model": [
|
|
121
|
-
"dist/model.d.ts"
|
|
122
|
-
],
|
|
123
|
-
"relation": [
|
|
124
|
-
"dist/relation.d.ts"
|
|
125
|
-
],
|
|
126
|
-
"schema": [
|
|
127
|
-
"dist/schema.d.ts"
|
|
128
|
-
],
|
|
129
|
-
"table": [
|
|
130
|
-
"dist/table.d.ts"
|
|
131
|
-
],
|
|
132
|
-
"modifiers/autodate": [
|
|
133
|
-
"dist/modifiers/autodate.d.ts"
|
|
134
|
-
],
|
|
135
|
-
"modifiers/common": [
|
|
136
|
-
"dist/modifiers/common.d.ts"
|
|
137
|
-
],
|
|
138
|
-
"modifiers/encryption": [
|
|
139
|
-
"dist/modifiers/encryption.d.ts"
|
|
140
|
-
],
|
|
141
|
-
"modifiers/hash": [
|
|
142
|
-
"dist/modifiers/hash.d.ts"
|
|
143
|
-
],
|
|
144
|
-
"modifiers/localization": [
|
|
145
|
-
"dist/modifiers/localization.d.ts"
|
|
146
|
-
]
|
|
147
|
-
}
|
|
155
|
+
"@antelopejs/interface-api": ">=0.0.13 <1.0.0",
|
|
156
|
+
"@antelopejs/interface-core": ">=0.1.1 <1.0.0",
|
|
157
|
+
"@antelopejs/interface-database": ">=0.1.8 <1.0.0"
|
|
148
158
|
},
|
|
159
|
+
"packageManager": "pnpm@10.6.5",
|
|
149
160
|
"antelopeJs": {
|
|
150
161
|
"standalone": true,
|
|
151
|
-
"test": "src/antelope.test.ts"
|
|
162
|
+
"test": "src/antelope.test.ts",
|
|
163
|
+
"skills": [
|
|
164
|
+
"./skills"
|
|
165
|
+
]
|
|
152
166
|
}
|
|
153
167
|
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: database-decorators-interface
|
|
3
|
+
description: Provides decorator-based database table definitions for AntelopeJS - a Table base class with RegisterTable/Index/Field/Fixture/Relation decorators, field modifiers (Encrypted, Hashed, Localized, CreationTime, UpdateTime), BasicDataModel CRUD models with validation, RegisterSchema provisioning, and the Model decorator for injecting models into API controllers. Use when code imports @antelopejs/interface-database-decorators (root or /table, /schema, /database, /model, /relation, /common, /modifiers/* subpaths), or when asked to define a database table or data model class, add encrypted/hashed/localized/timestamp fields, seed fixture data, register a schema, or inject a data model into an interface-api controller.
|
|
4
|
+
category: antelopejs-interface
|
|
5
|
+
tags: [database, decorators, orm, models, antelopejs]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Database Decorators Interface
|
|
9
|
+
|
|
10
|
+
Consumer-side decorator layer on top of `@antelopejs/interface-database` (AQL). This package itself has no proxy
|
|
11
|
+
points and no provider side — you never `ImplementInterface` it. All actual database access flows through
|
|
12
|
+
`Schema`/`Table` from `@antelopejs/interface-database`, which must be implemented by a database module (e.g. MongoDB).
|
|
13
|
+
Peer interfaces required: `@antelopejs/interface-core`, `@antelopejs/interface-api` (only for the `Model` controller decorator), `@antelopejs/interface-database`.
|
|
14
|
+
|
|
15
|
+
## Imports
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import {
|
|
19
|
+
Table,
|
|
20
|
+
Field,
|
|
21
|
+
Index,
|
|
22
|
+
Fixture,
|
|
23
|
+
} from "@antelopejs/interface-database-decorators/table";
|
|
24
|
+
import {
|
|
25
|
+
RegisterTable,
|
|
26
|
+
getTablesForSchema,
|
|
27
|
+
} from "@antelopejs/interface-database-decorators/schema";
|
|
28
|
+
import { RegisterSchema } from "@antelopejs/interface-database-decorators/database";
|
|
29
|
+
import {
|
|
30
|
+
BasicDataModel,
|
|
31
|
+
GetModel,
|
|
32
|
+
Model,
|
|
33
|
+
} from "@antelopejs/interface-database-decorators/model";
|
|
34
|
+
import { Relation } from "@antelopejs/interface-database-decorators/relation";
|
|
35
|
+
import {
|
|
36
|
+
DatumStaticMetadata,
|
|
37
|
+
getMetadata,
|
|
38
|
+
} from "@antelopejs/interface-database-decorators/common";
|
|
39
|
+
import {
|
|
40
|
+
CreationTime,
|
|
41
|
+
UpdateTime,
|
|
42
|
+
} from "@antelopejs/interface-database-decorators/modifiers/autodate";
|
|
43
|
+
import {
|
|
44
|
+
Encrypted,
|
|
45
|
+
EncryptionModifier,
|
|
46
|
+
} from "@antelopejs/interface-database-decorators/modifiers/encryption";
|
|
47
|
+
import {
|
|
48
|
+
Hashed,
|
|
49
|
+
HashModifier,
|
|
50
|
+
} from "@antelopejs/interface-database-decorators/modifiers/hash";
|
|
51
|
+
import {
|
|
52
|
+
Localized,
|
|
53
|
+
LocalizationModifier,
|
|
54
|
+
} from "@antelopejs/interface-database-decorators/modifiers/localization";
|
|
55
|
+
import {
|
|
56
|
+
Modifier,
|
|
57
|
+
OneWayModifier,
|
|
58
|
+
TwoWayModifier,
|
|
59
|
+
attachModifier,
|
|
60
|
+
toPlainData,
|
|
61
|
+
} from "@antelopejs/interface-database-decorators/modifiers/common";
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The package root re-exports everything above and also imports `reflect-metadata` (see gotchas).
|
|
65
|
+
|
|
66
|
+
## Minimal usage
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
@RegisterTable("recipes", "cookbook") // (tableName, schemaName)
|
|
70
|
+
class Recipe extends Table {
|
|
71
|
+
// _id: string is inherited as the primary key
|
|
72
|
+
@Index()
|
|
73
|
+
@Field("string")
|
|
74
|
+
declare slug: string;
|
|
75
|
+
@Field("string")
|
|
76
|
+
declare tagline: string;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const RecipeModel = BasicDataModel(Recipe); // table name taken from @RegisterTable
|
|
80
|
+
await RegisterSchema("cookbook"); // once at startup: provisions all tables registered for "cookbook", runs fixtures
|
|
81
|
+
|
|
82
|
+
const recipes = GetModel(RecipeModel); // cached per (model class, instanceId)
|
|
83
|
+
await recipes.insert(
|
|
84
|
+
{ slug: "grilled-halloumi", tagline: "Smoky skewers" },
|
|
85
|
+
{ validate: true },
|
|
86
|
+
);
|
|
87
|
+
const bySlug = await recipes.getBy("slug", "grilled-halloumi");
|
|
88
|
+
const one = await recipes.get(bySlug[0]._id);
|
|
89
|
+
await recipes.update(one._id, { tagline: "Smoky halloumi skewers" });
|
|
90
|
+
await recipes.delete(one._id);
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Controller injection (works as parameter or property decorator; second arg is a static `InstanceId` or a
|
|
94
|
+
`(ctx: RequestContext) => InstanceId | undefined` callback — returning `undefined` falls back to the default schema instance):
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
class RecipesController extends Controller("/recipes") {
|
|
98
|
+
@Get()
|
|
99
|
+
async list(@Model(RecipeModel) recipes: InstanceType<typeof RecipeModel>) {
|
|
100
|
+
return recipes.getAll();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Field modifiers
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
class Chef extends Table.with(
|
|
109
|
+
HashModifier,
|
|
110
|
+
EncryptionModifier,
|
|
111
|
+
LocalizationModifier,
|
|
112
|
+
) {
|
|
113
|
+
@Hashed() declare passphrase: string; // one-way; test via instance.testHash("passphrase", value)
|
|
114
|
+
@Encrypted({ secretKey }) declare supplierCode: string; // transparent encrypt/decrypt (autolock + autounlock)
|
|
115
|
+
@Localized() declare motto: string; // read AND write via instance.localize("en", ["motto"])
|
|
116
|
+
@CreationTime() declare hiredAt: Date; // set on insert, stripped from updates
|
|
117
|
+
@UpdateTime() declare lastActiveAt: Date; // refreshed on insert and update
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Gotchas
|
|
122
|
+
|
|
123
|
+
- Only the package root imports `reflect-metadata`; if you import only subpaths, ensure `reflect-metadata`
|
|
124
|
+
is loaded once before decorators run (e.g. `import "@antelopejs/interface-database-decorators"`).
|
|
125
|
+
- `Hashed`, `Encrypted`, and `Localized` require the matching modifier class in `Table.with(...)`; `CreationTime`/`UpdateTime` are event-only and need no mixin.
|
|
126
|
+
- Modifier ordering matters: `attachModifier` throws if you stack a modifier after a one-way modifier (e.g. anything after `@Hashed` on the same field).
|
|
127
|
+
- Hashed fields cannot be read back or queried directly — only equality-tested with `testHash`.
|
|
128
|
+
- Localized fields must be written through a localized instance (`instance.localize(locale).field = ...`);
|
|
129
|
+
values assigned before `localize()` are held in floating state and silently dropped on insert/update.
|
|
130
|
+
- `@Relation({ to: () => Target })` is declarative metadata only: no foreign-key enforcement, consumed by introspection tooling via `RelationStaticMetadata`.
|
|
131
|
+
- `Model.validate` (and `{ validate: true }` on insert/update) only checks fields whose `@Field` type is an
|
|
132
|
+
io-ts-style codec with `.decode`; plain string field tokens are skipped. Update validates with
|
|
133
|
+
`{ partial: true }` semantics.
|
|
134
|
+
- `update(obj)` without an explicit id asserts that the object carries the primary key (`_id` by default).
|
|
135
|
+
- Indexes are scoped to the schema instance. Add `@Index({ crossInstance: true })` only for indexes also queried
|
|
136
|
+
across all instances (`CROSS_INSTANCE`); it never changes results, only speed. On a group, one member setting it
|
|
137
|
+
applies it to the whole group.
|
|
138
|
+
- `@Fixture` data is only inserted by `RegisterSchema` when the table is empty (count === 0).
|
|
139
|
+
- `GetModel` caches by model class + instanceId; the same pair always returns the same instance.
|
|
140
|
+
- Serialization of modified instances to plain JSON goes through `toPlainData` (auto-attached as `toJSON`).
|
|
141
|
+
|
|
142
|
+
Deeper reference: this package's `docs/` chapters — Introduction, Table Definitions, Table Modifiers,
|
|
143
|
+
Data Models, Parameter Decoration — and the shipped `.d.ts` files. Do not duplicate them here.
|