Skip to content

Latest commit

 

History

History
257 lines (192 loc) · 6.5 KB

File metadata and controls

257 lines (192 loc) · 6.5 KB

Listable Fields

Overview

The @Listable decorator controls which fields are included in list endpoint responses. By default, list operations only return fields explicitly marked as listable, keeping payloads lightweight. The decorator supports multiple pluck modes for different levels of detail, and list responses include built-in pagination metadata.

The @Sortable decorator marks fields that can be used as a sorting key in list queries.

Basic Usage

Fields decorated with @Listable() are included in list responses. Fields without this decorator are excluded from list results but still appear in single-record get responses (provided they have read access).

import {
  Access,
  AccessMode,
  Listable,
  ModelReference,
} from "@antelopejs/interface-data-api/metadata";

@RegisterDataController()
class UserAPI extends DataController(
  User,
  DefaultRoutes.All,
  Controller("/users"),
) {
  @ModelReference()
  @Model(UserModel, "my-database")
  declare userModel: UserModel;

  @Listable()
  @Access(AccessMode.ReadOnly)
  declare _id: string;

  @Listable()
  @Access(AccessMode.ReadOnly)
  declare email: string;

  @Access(AccessMode.ReadOnly)
  declare biography: string; // Excluded from list, included in get
}

A request to GET /users/list returns _id and email for each record. A request to GET /users/get?id=user-123 returns all three fields.

Pluck Modes

Pluck modes allow a single controller to serve multiple list views with different field selections. Each @Listable call can target a named mode.

Default Mode

Calling @Listable() without a mode name registers the field under the default "list" mode.

@Listable()
declare name: string;

Custom Modes

Pass true and a mode name to register a field under a specific pluck mode.

@Listable()                    // Included in default "list" mode
@Listable(true, "detailed")   // Also included in "detailed" mode
declare name: string;

@Listable(true, "detailed")   // Only in "detailed" mode
declare internalNotes: string;

@Listable()                    // Only in default "list" mode
declare email: string;

Exclude from a Mode

Pass false to explicitly exclude a field from a mode.

@Listable()
@Listable(false, "minimal")   // Excluded from "minimal" mode
declare description: string;

Access Pluck Modes via Routes

Define separate routes for each pluck mode using DefaultRoutes.WithOptions with the pluckMode option.

const routes = {
  list: DefaultRoutes.List,
  detailed: DefaultRoutes.WithOptions(DefaultRoutes.List, {
    pluckMode: "detailed",
  }),
};

@RegisterDataController()
class UserAPI extends DataController(User, routes, Controller("/users")) {
  @ModelReference()
  @Model(UserModel, "my-database")
  declare userModel: UserModel;

  @Listable()
  @Access(AccessMode.ReadOnly)
  declare name: string;

  @Listable(true, "detailed")
  @Access(AccessMode.ReadOnly)
  declare internalNotes: string;
}
  • GET /users/list returns only name.
  • GET /users/detailed returns both name and internalNotes.

Listable with Getters

When a field uses a getter (computed property), the @Listable decorator must specify which database fields the getter depends on. This ensures those fields are fetched from the database.

@Listable(["firstName", "lastName"])
@Access(AccessMode.ReadOnly)
get fullName() {
  return `${this.firstName} ${this.lastName}`;
}

Pagination

List responses include pagination metadata. The response structure is:

{
  results: T[];     // Records for the current page
  total: number;    // Total number of matching records
  offset: number;   // Number of records skipped
  limit: number;    // Maximum records per page
}

Query Parameters

Parameter Default Description
limit 10 Maximum number of records to return
offset 0 Number of records to skip

Example

GET /users/list?limit=2&offset=4
{
  "results": [
    { "_id": "user-5", "name": "Eve" },
    { "_id": "user-6", "name": "Frank" }
  ],
  "total": 100,
  "offset": 4,
  "limit": 2
}

Limit Maximum Page Size

Use the maxPage route option to cap the limit parameter. If a request specifies a limit larger than maxPage, the value is clamped to maxPage.

const routes = {
  list: DefaultRoutes.WithOptions(DefaultRoutes.List, { maxPage: 25 }),
};

@RegisterDataController()
class UserAPI extends DataController(User, routes, Controller("/users")) {
  // A request with ?limit=100 returns at most 25 records
}

Sorting with @Sortable

The @Sortable decorator marks a field as eligible for use as a sorting key in list operations.

import { Sortable } from "@antelopejs/interface-data-api/metadata";

@RegisterDataController()
class UserAPI extends DataController(
  User,
  DefaultRoutes.All,
  Controller("/users"),
) {
  @ModelReference()
  @Model(UserModel, "my-database")
  declare userModel: UserModel;

  @Listable()
  @Access(AccessMode.ReadOnly)
  @Sortable()
  declare _id: string;

  @Listable()
  @Access(AccessMode.ReadOnly)
  @Sortable({ noIndex: true })
  declare name: string;

  @Listable()
  @Access(AccessMode.ReadOnly)
  @Sortable({ noIndex: true })
  declare age: number;
}

Sort Query Parameters

Parameter Description
sortKey The field name to sort by. Must be decorated with @Sortable.
sortDirection "asc" (ascending) or "desc" (descending). Defaults to "asc".

The noIndex Option

By default, @Sortable() assumes the field has a database index and uses indexed ordering. Set noIndex: true when the field is not indexed, so the framework performs an in-memory sort instead.

@Sortable()                    // Uses database index for sorting
declare _id: string;

@Sortable({ noIndex: true })   // In-memory sort, no index required
declare name: string;

Example

GET /users/list?sortKey=age&sortDirection=desc&limit=3
{
  "results": [
    { "_id": "user-1", "name": "Senior", "age": 65 },
    { "_id": "user-2", "name": "Adult", "age": 30 },
    { "_id": "user-3", "name": "Young", "age": 18 }
  ],
  "total": 3,
  "offset": 0,
  "limit": 3
}

If sortKey refers to a field that is not marked with @Sortable, the API returns a 400 Bad Request error.

Next Steps

See the foreign keys documentation to learn how to establish relationships between tables.