@antelopejs/interface-data-api 0.1.6 → 0.1.8

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.
@@ -0,0 +1,315 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ const node_path_1 = __importDefault(require("node:path"));
16
+ const interface_api_1 = require("@antelopejs/interface-api");
17
+ const interface_data_api_1 = require("@antelopejs/interface-data-api");
18
+ const components_1 = require("@antelopejs/interface-data-api/components");
19
+ const metadata_1 = require("@antelopejs/interface-data-api/metadata");
20
+ const interface_database_1 = require("@antelopejs/interface-database");
21
+ const interface_database_decorators_1 = require("@antelopejs/interface-database-decorators");
22
+ const chai_1 = require("chai");
23
+ const utils_1 = require("../utils");
24
+ const currentTestName = node_path_1.default
25
+ .basename(__filename)
26
+ .replace(/\.test\.(ts|js)$/, "");
27
+ const authorTableName = `authors-${currentTestName}`;
28
+ const bookTableName = `books-${currentTestName}`;
29
+ const schemaName = "default";
30
+ let Author = class Author extends interface_database_decorators_1.Table {
31
+ };
32
+ __decorate([
33
+ (0, interface_database_decorators_1.Field)("string"),
34
+ __metadata("design:type", String)
35
+ ], Author.prototype, "name", void 0);
36
+ __decorate([
37
+ (0, interface_database_decorators_1.Field)("string"),
38
+ __metadata("design:type", String)
39
+ ], Author.prototype, "email", void 0);
40
+ Author = __decorate([
41
+ (0, interface_database_decorators_1.RegisterTable)(authorTableName, schemaName)
42
+ ], Author);
43
+ class AuthorModel extends (0, interface_database_decorators_1.BasicDataModel)(Author, authorTableName) {
44
+ }
45
+ let Book = class Book extends interface_database_decorators_1.Table {
46
+ };
47
+ __decorate([
48
+ (0, interface_database_decorators_1.Field)("string"),
49
+ __metadata("design:type", String)
50
+ ], Book.prototype, "authorId", void 0);
51
+ __decorate([
52
+ (0, interface_database_decorators_1.Field)("string"),
53
+ __metadata("design:type", String)
54
+ ], Book.prototype, "title", void 0);
55
+ Book = __decorate([
56
+ (0, interface_database_decorators_1.RegisterTable)(bookTableName, schemaName)
57
+ ], Book);
58
+ class BookModel extends (0, interface_database_decorators_1.BasicDataModel)(Book, bookTableName) {
59
+ }
60
+ let _DeferredJoinedTestAPI = class _DeferredJoinedTestAPI extends (0, interface_data_api_1.DataController)(Book, {
61
+ list: interface_data_api_1.DefaultRoutes.List,
62
+ }, (0, interface_api_1.Controller)(`/${currentTestName}`)) {
63
+ };
64
+ __decorate([
65
+ (0, metadata_1.ModelReference)(),
66
+ (0, interface_database_decorators_1.Model)(BookModel),
67
+ __metadata("design:type", BookModel)
68
+ ], _DeferredJoinedTestAPI.prototype, "bookModel", void 0);
69
+ __decorate([
70
+ (0, metadata_1.Listable)(),
71
+ (0, metadata_1.Access)(metadata_1.AccessMode.ReadOnly),
72
+ __metadata("design:type", String)
73
+ ], _DeferredJoinedTestAPI.prototype, "_id", void 0);
74
+ __decorate([
75
+ (0, metadata_1.Listable)(),
76
+ (0, metadata_1.Access)(metadata_1.AccessMode.ReadWrite),
77
+ __metadata("design:type", String)
78
+ ], _DeferredJoinedTestAPI.prototype, "authorId", void 0);
79
+ __decorate([
80
+ (0, metadata_1.Listable)(),
81
+ (0, metadata_1.Access)(metadata_1.AccessMode.ReadWrite),
82
+ __metadata("design:type", String)
83
+ ], _DeferredJoinedTestAPI.prototype, "title", void 0);
84
+ __decorate([
85
+ (0, metadata_1.Listable)(["authorId"]),
86
+ (0, metadata_1.Joined)({
87
+ table: authorTableName,
88
+ localKey: "authorId",
89
+ remoteField: "name",
90
+ }),
91
+ (0, metadata_1.Sortable)({ noIndex: true }),
92
+ (0, metadata_1.Filter)(),
93
+ __metadata("design:type", String)
94
+ ], _DeferredJoinedTestAPI.prototype, "name", void 0);
95
+ __decorate([
96
+ (0, metadata_1.Listable)(["authorId"]),
97
+ (0, metadata_1.Joined)({
98
+ table: authorTableName,
99
+ localKey: "authorId",
100
+ remoteField: "email",
101
+ }),
102
+ __metadata("design:type", String)
103
+ ], _DeferredJoinedTestAPI.prototype, "email", void 0);
104
+ _DeferredJoinedTestAPI = __decorate([
105
+ (0, interface_data_api_1.RegisterDataController)()
106
+ ], _DeferredJoinedTestAPI);
107
+ const authorsDataset = [
108
+ { name: "Alice Carter", email: "alice@example.com" },
109
+ { name: "Bob Stone", email: "bob@example.com" },
110
+ { name: "Carol Wilde", email: "carol@example.com" },
111
+ ];
112
+ const orphanAuthorId = "orphan-author-id";
113
+ const reqCtx = {};
114
+ describe("Query.List deferred joined self-resolution", () => {
115
+ it("resolves joined fields on a paged two-element destructure", async () => await resolvesJoinedFieldsOnPagedTwoElementDestructure());
116
+ it("keeps page lookups after the slice and off the count pipeline", async () => await keepsLookupsAfterSliceAndOffCountPipeline());
117
+ it("materializes joined fields for a caller-added filter", async () => await materializesJoinedFieldsForCallerFilter());
118
+ it("resolves joined fields on a directly awaited stream", async () => await resolvesJoinedFieldsOnDirectAwait());
119
+ it("resolves joined fields on an nth datum", async () => await resolvesJoinedFieldsOnNthDatum());
120
+ it("orders by a joined field when the caller sorts the stream", async () => await ordersByJoinedFieldOnCallerSort());
121
+ it("materializes the whole joined group when sorting through List", async () => await materializesJoinedGroupOnListSort());
122
+ it("exposes the deferred set and raw stream only on demand", async () => await exposesDeferredSetOnDemand());
123
+ it("keeps the change feed on the raw pipeline", async () => await keepsChangeFeedOnRawPipeline());
124
+ it("scopes field aggregates to the joined fields they read", async () => await scopesFieldAggregatesToJoinedFields());
125
+ it("still pages the lookups after a cast", async () => await stillPagesLookupsAfterCast());
126
+ it("materializes joined fields when embedded in another query", async () => await materializesJoinedFieldsWhenEmbedded());
127
+ it("resolves joined fields on async iteration", async () => await resolvesJoinedFieldsOnAsyncIteration());
128
+ it("returns the unresolved names when no database is provided", async () => await returnsUnresolvedNamesWithoutDatabase());
129
+ });
130
+ async function _seedTables() {
131
+ await (0, interface_database_decorators_1.RegisterSchema)(schemaName);
132
+ const schema = interface_database_1.Schema.get(schemaName);
133
+ if (schema) {
134
+ await schema.instance().table(bookTableName).delete();
135
+ await schema.instance().table(authorTableName).delete();
136
+ }
137
+ const schemaInstance = (0, utils_1.getSchemaInstance)(schemaName);
138
+ const authorModel = new AuthorModel(schemaInstance);
139
+ const bookModel = new BookModel(schemaInstance);
140
+ const authorIdsRecord = await authorModel.insert(authorsDataset);
141
+ const authorIds = Object.values(authorIdsRecord);
142
+ const booksDataset = [
143
+ { authorId: authorIds[0], title: "Alpha Rising" },
144
+ { authorId: authorIds[1], title: "Beta Stories" },
145
+ { authorId: authorIds[2], title: "Gamma Tales" },
146
+ { authorId: authorIds[0], title: "Alpha Returns" },
147
+ { authorId: orphanAuthorId, title: "Lost Chapter" },
148
+ ];
149
+ await bookModel.insert(booksDataset);
150
+ const thisObj = Object.create(_DeferredJoinedTestAPI.prototype);
151
+ return { thisObj, model: bookModel };
152
+ }
153
+ function listAsConsumer(harness) {
154
+ const meta = (0, interface_data_api_1.GetDataControllerMeta)(harness.thisObj);
155
+ return components_1.Query.List(harness.thisObj, meta, harness.model.table, reqCtx, undefined, undefined, harness.model.database);
156
+ }
157
+ function stageNames(staged) {
158
+ return staged.build().map((entry) => entry.stage);
159
+ }
160
+ async function resolvesJoinedFieldsOnPagedTwoElementDestructure() {
161
+ const harness = await _seedTables();
162
+ const [query, queryTotal] = listAsConsumer(harness);
163
+ const page = (await query
164
+ .slice(0, 10)
165
+ .pluck("_internal", "title", "name", "email"));
166
+ (0, chai_1.expect)(await queryTotal).to.equal(5);
167
+ (0, chai_1.expect)(page).to.have.lengthOf(5);
168
+ const alphaRising = page.find((book) => book.title === "Alpha Rising");
169
+ (0, chai_1.expect)(alphaRising?.name).to.equal("Alice Carter");
170
+ (0, chai_1.expect)(alphaRising?.email).to.equal("alice@example.com");
171
+ const lostChapter = page.find((book) => book.title === "Lost Chapter");
172
+ (0, chai_1.expect)(lostChapter?.name).to.equal(null);
173
+ (0, chai_1.expect)(lostChapter?.email).to.equal(null);
174
+ }
175
+ async function keepsLookupsAfterSliceAndOffCountPipeline() {
176
+ const harness = await _seedTables();
177
+ const [query, queryTotal] = listAsConsumer(harness);
178
+ const countStages = stageNames(queryTotal);
179
+ (0, chai_1.expect)(countStages).to.include("count");
180
+ (0, chai_1.expect)(countStages).to.not.include("lookup");
181
+ const directCountStages = stageNames(query.count());
182
+ (0, chai_1.expect)(directCountStages).to.include("count");
183
+ (0, chai_1.expect)(directCountStages).to.not.include("lookup");
184
+ const pageStages = stageNames(query.slice(0, 2));
185
+ (0, chai_1.expect)(pageStages.indexOf("lookup")).to.be.greaterThan(pageStages.indexOf("slice"));
186
+ }
187
+ async function materializesJoinedFieldsForCallerFilter() {
188
+ const harness = await _seedTables();
189
+ let [query] = listAsConsumer(harness);
190
+ query = query.filter((row) => row.key("name").eq("Alice Carter"));
191
+ const matches = (await query);
192
+ (0, chai_1.expect)(matches).to.have.lengthOf(2);
193
+ for (const match of matches) {
194
+ (0, chai_1.expect)(match.name).to.equal("Alice Carter");
195
+ (0, chai_1.expect)(match.email).to.equal("alice@example.com");
196
+ }
197
+ (0, chai_1.expect)(await query.count()).to.equal(2);
198
+ }
199
+ async function resolvesJoinedFieldsOnDirectAwait() {
200
+ const harness = await _seedTables();
201
+ const [query] = listAsConsumer(harness);
202
+ const rows = (await query);
203
+ (0, chai_1.expect)(rows).to.have.lengthOf(5);
204
+ const betaStories = rows.find((book) => book.title === "Beta Stories");
205
+ (0, chai_1.expect)(betaStories?.name).to.equal("Bob Stone");
206
+ (0, chai_1.expect)(betaStories?.email).to.equal("bob@example.com");
207
+ }
208
+ async function resolvesJoinedFieldsOnNthDatum() {
209
+ const harness = await _seedTables();
210
+ const [query] = listAsConsumer(harness);
211
+ const first = (await query.nth(0));
212
+ (0, chai_1.expect)(first.title).to.be.a("string");
213
+ (0, chai_1.expect)(first).to.have.property("name");
214
+ (0, chai_1.expect)(first).to.have.property("email");
215
+ const internalKeys = Object.keys(first).filter((key) => key.startsWith("__joined_orig_"));
216
+ (0, chai_1.expect)(internalKeys).to.deep.equal([]);
217
+ (0, chai_1.expect)(first.authorId).to.be.a("string");
218
+ const second = (await query.nth(1));
219
+ (0, chai_1.expect)(second.title).to.not.equal(first.title);
220
+ }
221
+ async function ordersByJoinedFieldOnCallerSort() {
222
+ const harness = await _seedTables();
223
+ const [query] = listAsConsumer(harness);
224
+ const rows = (await query.orderBy("name", "desc"));
225
+ (0, chai_1.expect)(rows).to.have.lengthOf(5);
226
+ const names = rows.map((book) => book.name).filter((name) => name !== null);
227
+ (0, chai_1.expect)(names).to.deep.equal([...names].sort().reverse());
228
+ (0, chai_1.expect)(names[0]).to.equal("Carol Wilde");
229
+ }
230
+ async function materializesJoinedGroupOnListSort() {
231
+ const harness = await _seedTables();
232
+ const meta = (0, interface_data_api_1.GetDataControllerMeta)(harness.thisObj);
233
+ const [query, , deferredJoined] = components_1.Query.List(harness.thisObj, meta, harness.model.table, reqCtx, ["name", "asc"], undefined, harness.model.database);
234
+ (0, chai_1.expect)(deferredJoined.size).to.equal(0);
235
+ const rows = (await query.slice(0, 5));
236
+ const gammaTales = rows.find((book) => book.title === "Gamma Tales");
237
+ (0, chai_1.expect)(gammaTales?.name).to.equal("Carol Wilde");
238
+ (0, chai_1.expect)(gammaTales?.email).to.equal("carol@example.com");
239
+ }
240
+ async function keepsChangeFeedOnRawPipeline() {
241
+ const harness = await _seedTables();
242
+ const [query] = listAsConsumer(harness);
243
+ const feedStages = stageNames(query.changes());
244
+ (0, chai_1.expect)(feedStages).to.include("changes");
245
+ (0, chai_1.expect)(feedStages).to.not.include("lookup");
246
+ }
247
+ async function scopesFieldAggregatesToJoinedFields() {
248
+ const harness = await _seedTables();
249
+ const [query] = listAsConsumer(harness);
250
+ const listedQuery = query;
251
+ const joinedFieldStages = stageNames(listedQuery.count("name"));
252
+ (0, chai_1.expect)(joinedFieldStages).to.include("count");
253
+ (0, chai_1.expect)(joinedFieldStages).to.include("lookup");
254
+ const plainFieldStages = stageNames(query.count("title"));
255
+ (0, chai_1.expect)(plainFieldStages).to.include("count");
256
+ (0, chai_1.expect)(plainFieldStages).to.not.include("lookup");
257
+ const distinctRowStages = stageNames(query.distinct());
258
+ (0, chai_1.expect)(distinctRowStages).to.include("distinct");
259
+ (0, chai_1.expect)(distinctRowStages).to.include("lookup");
260
+ }
261
+ async function stillPagesLookupsAfterCast() {
262
+ const harness = await _seedTables();
263
+ const [query] = listAsConsumer(harness);
264
+ const pageStages = stageNames(query.cast().slice(0, 2));
265
+ (0, chai_1.expect)(pageStages.indexOf("lookup")).to.be.greaterThan(pageStages.indexOf("slice"));
266
+ const page = (await query.cast().slice(0, 10));
267
+ const alphaRising = page.find((book) => book.title === "Alpha Rising");
268
+ (0, chai_1.expect)(alphaRising?.name).to.equal("Alice Carter");
269
+ }
270
+ async function materializesJoinedFieldsWhenEmbedded() {
271
+ const harness = await _seedTables();
272
+ const [query] = listAsConsumer(harness);
273
+ const unioned = (await harness.model.table.union(query));
274
+ (0, chai_1.expect)(unioned).to.have.lengthOf(10);
275
+ const withJoined = unioned.filter((row) => "name" in row);
276
+ (0, chai_1.expect)(withJoined).to.have.lengthOf(5);
277
+ (0, chai_1.expect)(withJoined.filter((row) => row.name === "Alice Carter")).to.have.lengthOf(2);
278
+ }
279
+ async function resolvesJoinedFieldsOnAsyncIteration() {
280
+ const harness = await _seedTables();
281
+ const [query] = listAsConsumer(harness);
282
+ const names = new Set();
283
+ for await (const row of query) {
284
+ names.add(row.name);
285
+ if (names.has("Alice Carter") && names.has("Bob Stone")) {
286
+ break;
287
+ }
288
+ }
289
+ (0, chai_1.expect)(names.has("Alice Carter")).to.equal(true);
290
+ (0, chai_1.expect)(names.has("Bob Stone")).to.equal(true);
291
+ }
292
+ async function returnsUnresolvedNamesWithoutDatabase() {
293
+ const harness = await _seedTables();
294
+ const meta = (0, interface_data_api_1.GetDataControllerMeta)(harness.thisObj);
295
+ const [query, , deferredJoined] = components_1.Query.List(harness.thisObj, meta, harness.model.table, reqCtx);
296
+ (0, chai_1.expect)(Array.from(deferredJoined).sort()).to.deep.equal(["email", "name"]);
297
+ const pageStages = stageNames(query.slice(0, 5));
298
+ (0, chai_1.expect)(pageStages).to.not.include("lookup");
299
+ }
300
+ async function exposesDeferredSetOnDemand() {
301
+ const harness = await _seedTables();
302
+ const meta = (0, interface_data_api_1.GetDataControllerMeta)(harness.thisObj);
303
+ const [, , resolvedByLayer] = listAsConsumer(harness);
304
+ (0, chai_1.expect)(resolvedByLayer.size).to.equal(0);
305
+ const [rawQuery, rawTotal, deferredJoined] = components_1.Query.List(harness.thisObj, meta, harness.model.table, reqCtx, undefined, undefined, harness.model.database, { exposeDeferredJoined: true });
306
+ (0, chai_1.expect)(Array.from(deferredJoined).sort()).to.deep.equal(["email", "name"]);
307
+ (0, chai_1.expect)(await rawTotal).to.equal(5);
308
+ const rawRows = (await rawQuery.slice(0, 5));
309
+ (0, chai_1.expect)(rawRows).to.have.lengthOf(5);
310
+ for (const row of rawRows) {
311
+ (0, chai_1.expect)(row).to.not.have.property("name");
312
+ (0, chai_1.expect)(row).to.not.have.property("email");
313
+ }
314
+ }
315
+ //# sourceMappingURL=deferred_joined.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deferred_joined.test.js","sourceRoot":"","sources":["../../../src/tests/components/deferred_joined.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,128 @@
1
+ # Data API Documentation
2
+
3
+ ## Overview
4
+
5
+ The Data API interface provides a declarative approach to building RESTful APIs with automatic CRUD operations. It connects your database models directly to API endpoints, handling validation, access control, foreign key resolution, and pagination with minimal boilerplate.
6
+
7
+ ## Key Features
8
+
9
+ - **Database-to-API Mapping** - Expose database tables as REST endpoints through data controllers.
10
+ - **Default CRUD Routes** - Built-in `get`, `list`, `new`, `edit`, and `delete` operations that work out of the box.
11
+ - **Access Control** - Define read-only, write-only, or read-write permissions per field, with per-action overrides.
12
+ - **Field Validation** - Attach custom validation functions to individual fields and enforce mandatory fields per operation.
13
+ - **Listable Fields and Pagination** - Control which fields appear in list responses, with support for multiple pluck modes and paginated results.
14
+ - **Sorting** - Mark fields as sortable to enable ordering in list queries.
15
+ - **Foreign Key Resolution** - Automatically resolve relationships between tables with optional field plucking.
16
+ - **Joined Fields** - Flatten a scalar field from another table onto each row so it can be sorted, filtered, and searched natively in the database.
17
+ - **Computed Fields** - Merge in-database computed expressions (row-local values or aggregates over other tables) onto each row, natively sortable, filterable, and pageable.
18
+ - **Filters** - Add default or custom filter logic to list endpoints using query parameters.
19
+ - **Modifier Integration** - Automatic data transformation (encryption, hashing, localization) through database-decorators.
20
+
21
+ ## Dependencies
22
+
23
+ This interface relies on the following AntelopeJS packages:
24
+
25
+ - [@antelopejs/interface-api](https://github.com/AntelopeJS/interface-api) - API controllers and route registration.
26
+ - [@antelopejs/interface-database-decorators](https://github.com/AntelopeJS/interface-database-decorators) - Database table definitions and models.
27
+
28
+ ## Quick Start
29
+
30
+ The following example creates a full CRUD API for a `users` table.
31
+
32
+ ```typescript
33
+ import { Controller } from "@antelopejs/interface-api";
34
+ import {
35
+ DataController,
36
+ DefaultRoutes,
37
+ RegisterDataController,
38
+ } from "@antelopejs/interface-data-api";
39
+ import {
40
+ Access,
41
+ AccessMode,
42
+ Listable,
43
+ Mandatory,
44
+ ModelReference,
45
+ } from "@antelopejs/interface-data-api/metadata";
46
+ import {
47
+ BasicDataModel,
48
+ Field,
49
+ Index,
50
+ Model,
51
+ RegisterTable,
52
+ Table,
53
+ } from "@antelopejs/interface-database-decorators";
54
+
55
+ // Define the database table
56
+ @RegisterTable("users")
57
+ class User extends Table {
58
+ @Index({ primary: true })
59
+ @Field("string")
60
+ declare _id: string;
61
+
62
+ @Index()
63
+ @Field("string")
64
+ declare email: string;
65
+
66
+ @Field("string")
67
+ declare name: string;
68
+
69
+ @Field("string")
70
+ declare password: string;
71
+ }
72
+
73
+ // Create the database model
74
+ class UserModel extends BasicDataModel(User, "users") {}
75
+
76
+ // Create the data controller
77
+ @RegisterDataController()
78
+ class UserAPI extends DataController(
79
+ User,
80
+ DefaultRoutes.All,
81
+ Controller("/users"),
82
+ ) {
83
+ @ModelReference()
84
+ @Model(UserModel, "my-database")
85
+ declare userModel: UserModel;
86
+
87
+ @Listable()
88
+ @Access(AccessMode.ReadOnly)
89
+ declare _id: string;
90
+
91
+ @Listable()
92
+ @Access(AccessMode.ReadWrite)
93
+ @Mandatory("new", "edit")
94
+ declare email: string;
95
+
96
+ @Listable()
97
+ @Access(AccessMode.ReadWrite)
98
+ @Mandatory("new")
99
+ declare name: string;
100
+
101
+ @Access(AccessMode.WriteOnly)
102
+ @Mandatory("new")
103
+ declare password: string;
104
+ }
105
+ ```
106
+
107
+ This generates the following REST endpoints:
108
+
109
+ | Method | Endpoint | Description |
110
+ | ------ | -------- | ----------- |
111
+ | GET | `/users/get?id=<id>` | Retrieve a single user |
112
+ | GET | `/users/list?limit=10` | List users with pagination |
113
+ | POST | `/users/new` | Create a new user |
114
+ | PUT | `/users/edit?id=<id>` | Update an existing user |
115
+ | DELETE | `/users/delete?id=<id>` | Delete a user |
116
+
117
+ ## Documentation Sections
118
+
119
+ - [Data Controllers](./2.data-controllers.md) - Create and configure data controllers
120
+ - [Routes](./3.routes.md) - Default routes, custom routes, and route options
121
+ - [Access Rights](./4.access-rights.md) - Control field read/write permissions
122
+ - [Validators](./5.validators.md) - Field-level validation and mandatory fields
123
+ - [Listable Fields](./6.listable.md) - Pagination, pluck modes, and sorting
124
+ - [Foreign Keys](./7.foreign-keys.md) - Establish relationships between tables
125
+ - [Filters](./8.filters.md) - Add filtering capabilities to list endpoints
126
+ - [Modifiers](./9.modifiers.md) - Automatic data transformation with database-decorators
127
+ - [Joined Fields](./10.joined.md) - Flatten a remote field onto rows for native sort/filter
128
+ - [Computed Fields](./11.computed.md) - In-database computed values, sortable and pageable natively
@@ -0,0 +1,157 @@
1
+ # Joined Fields
2
+
3
+ ## Overview
4
+
5
+ The `@Joined` decorator imports a single scalar field from another table and flattens it onto each row at query time. Unlike `@Foreign`, which embeds the full referenced record as a nested object in the response, `@Joined` copies one remote field to the top level of the row before sorting and filtering are applied. This makes the joined field natively sortable, filterable, and searchable in the database.
6
+
7
+ Joined fields are read-only: the value is set by a lookup at query time and is never persisted on the controller's own table. Declaring a field as joined automatically sets its access mode to read-only and forces non-indexed sorting.
8
+
9
+ ## Basic Usage
10
+
11
+ Apply `@Joined` to a field that should be populated from a remote table. The local row must carry a foreign key (`localKey`) that matches the remote table's `remoteIndex`.
12
+
13
+ ```typescript
14
+ import { Joined } from "@antelopejs/interface-data-api/metadata";
15
+ import {
16
+ Field,
17
+ Index,
18
+ RegisterTable,
19
+ Relation,
20
+ Table,
21
+ } from "@antelopejs/interface-database-decorators";
22
+
23
+ @RegisterTable("users")
24
+ class User extends Table {
25
+ @Index({ primary: true })
26
+ @Field("string")
27
+ declare _id: string;
28
+
29
+ @Field("string")
30
+ declare name: string;
31
+ }
32
+
33
+ @RegisterTable("orders")
34
+ class Order extends Table {
35
+ @Index({ primary: true })
36
+ @Field("string")
37
+ declare _id: string;
38
+
39
+ @Field("string")
40
+ @Relation({ to: () => User })
41
+ declare userId: string;
42
+
43
+ @Field("number")
44
+ declare total: number;
45
+ }
46
+
47
+ @RegisterDataController()
48
+ class OrderAPI extends DataController(
49
+ Order,
50
+ DefaultRoutes.All,
51
+ Controller("/orders"),
52
+ ) {
53
+ @ModelReference()
54
+ @Model(OrderModel, "my-database")
55
+ declare orderModel: OrderModel;
56
+
57
+ @Listable()
58
+ @Access(AccessMode.ReadOnly)
59
+ declare _id: string;
60
+
61
+ declare userId: string;
62
+
63
+ // Pull the remote "name" field from the User referenced by userId
64
+ @Listable()
65
+ @Joined({
66
+ table: User,
67
+ localKey: "userId",
68
+ remoteField: "name",
69
+ })
70
+ declare userName: string;
71
+
72
+ @Listable()
73
+ @Access(AccessMode.ReadOnly)
74
+ declare total: number;
75
+ }
76
+ ```
77
+
78
+ A GET request to `/orders/get?id=order-123` returns the joined value at the top level:
79
+
80
+ ```json
81
+ {
82
+ "_id": "order-123",
83
+ "userName": "Bob",
84
+ "total": 99.99
85
+ }
86
+ ```
87
+
88
+ ## Decorator Parameters
89
+
90
+ ```typescript
91
+ @Joined({ table, localKey, remoteField, remoteIndex?, schema? })
92
+ ```
93
+
94
+ | Option | Type | Required | Default | Description |
95
+ | ------ | ---- | -------- | ------- | ----------- |
96
+ | `table` | `Class<Table>` or `string` | Yes | - | The remote table class or registered table name string |
97
+ | `localKey` | `string` | Yes | - | The field on the local row holding the value matched against the remote table |
98
+ | `remoteField` | `string` | Yes | - | The remote field whose value is flattened onto the row |
99
+ | `remoteIndex` | `string` | No | `"_id"` | The index on the remote table to match `localKey` against |
100
+ | `schema` | `string` | No | Controller schema | Name of the schema the remote table is registered in, when it differs from the controller's schema |
101
+
102
+ ## Sorting and Filtering
103
+
104
+ Because the join is applied to the underlying query before sorting and filtering, joined fields behave like native columns. Combine `@Joined` with `@Sortable` or `@Filter` to order or filter by the imported value.
105
+
106
+ ```typescript
107
+ @Sortable()
108
+ @Filter()
109
+ @Joined({
110
+ table: User,
111
+ localKey: "userId",
112
+ remoteField: "name",
113
+ })
114
+ declare userName: string;
115
+ ```
116
+
117
+ Joined fields are always sorted without a database index (`@Sortable` is forced to non-indexed for them), since the value comes from a runtime lookup rather than a stored column.
118
+
119
+ ## Deferred Resolution on Lists
120
+
121
+ `Query.List` only materializes the joined groups that the request's sort or filters reference before the count. The remaining display-only groups are deferred: the returned stream resolves them itself as soon as an operation can observe them, so destructuring `[query, total]` alone always yields rows with every `@Joined` field resolved.
122
+
123
+ - Paging the stream (`slice`, `nth`) appends the lookups after the page boundary, so they only run on the returned rows.
124
+ - A bare `count()` — and any field aggregate (`sum`, `min`, `distinct(field)`, ...) on a non-joined field — skips them entirely (left-join lookups never change the row count nor other fields).
125
+ - `changes()` attaches to the raw stream, since change feeds cannot carry the join lookups.
126
+ - Any other operation (`filter`, `orderBy`, `map`, ...) materializes them first, since it may read the joined values.
127
+
128
+ The page-only benefit therefore holds as long as paging is the first thing the caller does. An operation applied to the whole stream before paging — a filter, a sort, or the `lookup` that `Query.Foreign` stages for a `@Foreign` field — materializes the joined groups over the whole matched set instead. The lookups still stay out of the count pipeline, but a caller that wants them strictly on the page must pass `{ exposeDeferredJoined: true }` to `Query.List` and resolve them itself after the slice: the raw stream is then returned unchanged and the third tuple element names the joined fields left to resolve with `Query.Joined`. This is what the default list route does, which also lets it limit the page lookups to the plucked fields.
129
+
130
+ ## Cross-Schema Joins
131
+
132
+ By default, the remote table is resolved in the same schema as the controller's table. Use the `schema` option to join against a table registered in a different schema; the join is resolved against that schema's default instance.
133
+
134
+ ```typescript
135
+ @Joined({
136
+ table: "users",
137
+ localKey: "userId",
138
+ remoteField: "name",
139
+ schema: "auth",
140
+ })
141
+ declare userName: string;
142
+ ```
143
+
144
+ When a table class is passed, its schema is inferred from its `@RegisterTable` metadata. If both the class metadata and the explicit `schema` option specify a schema and they disagree, an error is thrown.
145
+
146
+ ## Joined vs. Foreign
147
+
148
+ | Aspect | `@Joined` | `@Foreign` |
149
+ | ------ | --------- | ---------- |
150
+ | Result shape | A single scalar value flattened onto the row | The full referenced record (or plucked subset) nested under the field |
151
+ | Access | Read-only (enforced) | Configurable via `@Access` |
152
+ | Sort / filter | Native (join applied before sort/filter) | Not natively sortable/filterable on remote fields |
153
+ | Multiple references | One remote field per decorator | Single or array (`multi`) references |
154
+
155
+ ## Next Steps
156
+
157
+ See the [computed fields documentation](./11.computed.md) to generalize this merge mechanism to arbitrary in-database expressions, or the [modifiers documentation](./9.modifiers.md) to learn how automatic data transformation integrates with the Data API.