[data-reveal]{opacity:1!important;transform:none!important}
Expose database records through an API
Data API

Expose database records through an API

Overview

Use @antelopejs/interface-data-api to expose a table through named CRUD routes and an explicit field policy. A data controller connects the table model to HTTP handlers; its metadata decides which fields clients may read, write, list, and filter.

Build one resource first

  1. Prepare the database. Define a table and register its schema with database-decorators. Your AntelopeJS module needs a database provider and an API provider.
  2. Choose the routes. Start with the route set your clients need, rather than exposing every write operation by default.
  3. Set the field policy. Add access rights, validators, and list fields. Field permissions are not user authentication or row-level authorization.
  4. Check one read and one rejected write. Confirm the response excludes private fields and that invalid input cannot reach storage. Then add relationships and filters.

Quick Start

The following example declares all five CRUD routes for a users table. It assumes a configured provider and an existing my-database instance. Import the table declaration before calling await RegisterSchema("myapp") in the declaring module's initialization; await that setup before serving requests.

This is a field-policy example, not a production account system: write-only prevents a password from appearing in responses, but does not hash it or authenticate requests. Add an appropriate database modifier and your authentication policy before accepting real credentials.

import { Controller } from "@antelopejs/interface-api";
import {
  DataController,
  DefaultRoutes,
  RegisterDataController,
} from "@antelopejs/interface-data-api";
import {
  Access,
  AccessMode,
  Listable,
  Mandatory,
  ModelReference,
} from "@antelopejs/interface-data-api/metadata";
import {
  BasicDataModel,
  Field,
  Index,
  Model,
  RegisterTable,
  Table,
} from "@antelopejs/interface-database-decorators";

// Define the database table
@RegisterTable("users", "myapp")
class User extends Table {
  @Field("string")
  declare _id: string;

  @Index()
  @Field("string")
  declare email: string;

  @Field("string")
  declare name: string;

  @Field("string")
  declare password: string;
}

// Create the database model
class UserModel extends BasicDataModel(User, "users") {}

// Create the data controller
@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.ReadWrite)
  @Mandatory("new", "edit")
  declare email: string;

  @Listable()
  @Access(AccessMode.ReadWrite)
  @Mandatory("new")
  declare name: string;

  @Access(AccessMode.WriteOnly)
  @Mandatory("new")
  declare password: string;
}

This generates the following REST endpoints:

MethodEndpointDescription
GET/users/get?id=<id>Retrieve a single user
GET/users/list?limit=10List users with pagination
POST/users/newCreate a new user
PUT/users/edit?id=<id>Update an existing user
DELETE/users/delete?id=<id>Delete a user

Check the field policy

Use disposable data and your configured API base URL. Create a user with name, email, and a dummy password; the response is an array of inserted IDs. Use the first ID for the following checks:

  1. GET /users/get?id=<id> returns the readable fields, never password.
  2. GET /users/list?limit=10 includes _id, name, and email in results.
  3. PUT /users/edit?id=<id> with { "name": "Updated" } preserves the omitted email. With { "email": "" }, it returns 400 because email is mandatory on edit.

@Mandatory does not check email syntax or password strength. Add validators before accepting untrusted input.

Core guides

Relationships and advanced queries

  • Foreign Keys - Establish relationships between tables
  • Filters - Add filtering capabilities to list endpoints
  • Modifiers - Automatic data transformation with database-decorators
  • Joined Fields - Flatten a remote field onto rows for native sort/filter
  • Computed Fields - In-database computed values, sortable and pageable natively