Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 14 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
# @antelopejs/interface-data-api
# `@antelopejs/interface-data-api`

<div align="center">
<a href="https://www.npmjs.com/package/@antelopejs/interface-data-api"><img alt="npm" src="https://img.shields.io/npm/v/@antelopejs/interface-data-api?style=for-the-badge&labelColor=000000&color=000000"></a>
<a href="./LICENSE"><img alt="License" src="https://img.shields.io/npm/l/@antelopejs/interface-data-api?style=for-the-badge&labelColor=000000&color=000000"></a>
<a href="https://discord.gg/g4pFt6eXqu"><img alt="Discord" src="https://img.shields.io/badge/Discord-18181B?logo=discord&style=for-the-badge&color=000000"></a>
<a href="https://interfaces.antelopejs.com/docs/data-api"><img alt="Docs" src="https://img.shields.io/badge/Docs-18181B?style=for-the-badge&color=000000"></a>
<a href="https://www.npmjs.com/package/@antelopejs/interface-data-api"><img data-illustration alt="npm" src="https://img.shields.io/npm/v/@antelopejs/interface-data-api?style=for-the-badge&labelColor=000000&color=000000"></a>
<a href="./LICENSE"><img data-illustration alt="License" src="https://img.shields.io/npm/l/@antelopejs/interface-data-api?style=for-the-badge&labelColor=000000&color=000000"></a>
<a href="https://discord.gg/g4pFt6eXqu"><img data-illustration alt="Discord" src="https://img.shields.io/badge/Discord-18181B?logo=discord&style=for-the-badge&color=000000"></a>
<a href="https://interfaces.antelopejs.com/docs/data-api"><img data-illustration alt="Docs" src="https://img.shields.io/badge/Docs-18181B?style=for-the-badge&color=000000"></a>
</div>

CRUD REST endpoints generated from a database table: declare a data controller and its fields, and get `get`, `list`, `new`, `edit` and `delete` routes with access rules, validation and pagination.
The AntelopeJS interface that generates CRUD REST endpoints from a database table. Declare a data controller and its fields, and it serves the `get`, `list`, `new`, `edit` and `delete` routes with access rules, validation and pagination.

## What it offers

- `DataController` and `@RegisterDataController()` turn a table class into a controller with the default routes you pick from `DefaultRoutes`.
- `@Access`, `@Mandatory` and `@Validator` decide which fields each route reads and writes, and check them.
- `@Access` decides which fields each route reads and writes. `@Mandatory` and `@Validator` check the written values.
- `@Listable`, `@Sortable` and `@Filter` shape the paginated `list` route.
- `@Foreign`, `@Joined` and `@Computed` resolve references, flatten remote fields and add values computed in the database.

## Implementations

The package runs in your module and has no implementing module of its own. The project needs modules that implement the interfaces it builds on:
The package runs in your module and has no implementing module of its own. The project needs modules that implement the interfaces it builds on.

| Interface | Module |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
Expand All @@ -27,13 +27,13 @@ The package runs in your module and has no implementing module of its own. The p

## Install

Add the package and the interfaces your controllers import to your module:
Add the package and its peer interfaces to your module.

```bash
pnpm add @antelopejs/interface-data-api @antelopejs/interface-api @antelopejs/interface-database-decorators
pnpm add @antelopejs/interface-data-api @antelopejs/interface-api @antelopejs/interface-api-util @antelopejs/interface-core @antelopejs/interface-database @antelopejs/interface-database-decorators
```

Then add the implementations to the project:
Then add the implementations to the project.

```bash
ajs project modules add @antelopejs/api @antelopejs/mongodb
Expand Down Expand Up @@ -101,7 +101,7 @@ export async function construct() {
}
```

The controller serves `GET /tasks/get?id=`, `GET /tasks/list`, `POST /tasks/new`, `PUT /tasks/edit?id=` and `DELETE /tasks/delete?id=`. Fields without `@Access` stay out of every response.
The controller serves `GET /tasks/get?id=`, `GET /tasks/list`, `POST /tasks/new`, `PUT /tasks/edit?id=` and `DELETE /tasks/delete?id=`. A field without `@Access` stays out of every response and every write, unless `@Joined` or `@Computed` makes it read-only.

## Documentation

Expand All @@ -119,7 +119,7 @@ The controller serves `GET /tasks/get?id=`, `GET /tasks/list`, `POST /tasks/new`

## Compatibility

The package declares these `@antelopejs/*` peer dependencies:
The package declares these `@antelopejs/*` peer dependencies.

<!-- antelopejs-docs-compat:start -->

Expand All @@ -135,7 +135,7 @@ The package declares these `@antelopejs/*` peer dependencies:

## Contributing

Read [CONTRIBUTING.md](./CONTRIBUTING.md) before you open a pull request. The local checks are:
Read [CONTRIBUTING.md](./CONTRIBUTING.md) before you open a pull request. Run the local checks with these commands.

```bash
pnpm install
Expand Down
73 changes: 38 additions & 35 deletions docs/1.introduction.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,27 @@
# Expose database records through an API
---
title: Expose database records through an API
description: Build a first data controller that serves CRUD routes for a users table, then check which fields it reads and writes.
mode: tutorial
---

## 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.
In this tutorial, we expose a `users` table through five CRUD routes with `@antelopejs/interface-data-api`. A data controller connects the table model to HTTP handlers. Its metadata decides which fields clients can 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](./3.routes.md) your clients need, rather than exposing every write operation by default.
3. **Set the field policy.** Add [access rights](./4.access-rights.md), [validators](./5.validators.md), and [list fields](./6.listable.md). 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](./7.foreign-keys.md) and [filters](./8.filters.md).
We build one resource in four steps.

## Quick Start
1. **Prepare the database.** Define a table and register its schema with database-decorators. The AntelopeJS project needs a database module and an API module.
2. **Choose the routes.** Start with the [route set](./3.routes.md) your clients need, instead of exposing every write route by default.
3. **Set the field policy.** Add [access rights](./4.access-rights.md), [validators](./5.validators.md) and [list fields](./6.listable.md). Field permissions do not authenticate users and do not authorize access to rows.
4. **Check one read and one rejected write.** Confirm that the response leaves out private fields and that invalid input never reaches storage. Then add [relationships](./7.foreign-keys.md) and [filters](./8.filters.md).

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.
## Quick start

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](./9.modifiers.md) and your authentication policy before accepting real credentials.
The example declares all five CRUD routes for a `users` table. It assumes a configured database module and an existing `my-database` instance. Import the table declaration, then `await RegisterSchema("myapp")` when the declaring module initializes. Wait for that setup to finish before the module serves requests.

This example sets a field policy. It is not a production account system. `AccessMode.WriteOnly` keeps the password out of responses, but it does not hash the password or authenticate requests. Add a [database modifier](./9.modifiers.md) and your authentication policy before you accept real credentials.

```ts docs-check
import { Controller } from "@antelopejs/interface-api";
Expand All @@ -40,7 +46,6 @@ import {
Table,
} from "@antelopejs/interface-database-decorators";

// Define the database table
@RegisterTable("users", "myapp")
class User extends Table {
@Field("string")
Expand All @@ -57,10 +62,8 @@ class User extends Table {
declare password: string;
}

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

// Create the data controller
@RegisterDataController()
class UserAPI extends DataController(
User,
Expand Down Expand Up @@ -91,38 +94,38 @@ class UserAPI extends DataController(
}
```

This generates the following REST endpoints:
The controller serves these REST endpoints.

| Method | Endpoint | Description |
| ------ | ----------------------- | -------------------------- |
| GET | `/users/get?id=<id>` | Retrieve a single user |
| GET | `/users/list?limit=10` | List users with pagination |
| POST | `/users/new` | Create a new user |
| PUT | `/users/edit?id=<id>` | Update an existing user |
| DELETE | `/users/delete?id=<id>` | Delete a user |
| Method | Endpoint | Description |
| ------ | ----------------------- | -------------------- |
| GET | `/users/get?id=<id>` | Read one user |
| GET | `/users/list?limit=10` | List users, one page |
| POST | `/users/new` | Create a user |
| PUT | `/users/edit?id=<id>` | Update a 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:
Use disposable data and the base URL of your API module. First, create a user with `POST /users/new` and a body that holds `name`, `email` and a dummy `password`. The response is the created user, with its `_id` and without `password`. Use that `_id` in the next 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.
1. `GET /users/get?id=<id>` returns the readable fields. You see `_id`, `email` and `name`, and never `password`.
2. `GET /users/list?limit=10` returns `_id`, `name` and `email` for each entry of `results`.
3. `PUT /users/edit?id=<id>` with `{ "name": "Updated" }` keeps the email you did not send. With `{ "email": "" }`, the route answers `400`, because `email` is mandatory on `edit`.

`@Mandatory` does not check email syntax or password strength. Add [validators](./5.validators.md) before accepting untrusted input.
`@Mandatory` does not check the email syntax or the password strength. Add [validators](./5.validators.md) before you accept untrusted input.

## Core guides

- [Data Controllers](./2.data-controllers.md) - Create and configure data controllers
- [Routes](./3.routes.md) - Default routes, custom routes, and route options
- [Access Rights](./4.access-rights.md) - Control field read/write permissions
- [Validators](./5.validators.md) - Field-level validation and mandatory fields
- [Listable Fields](./6.listable.md) - Pagination, pluck modes, and sorting
- [Data controllers](./2.data-controllers.md). Create and configure data controllers.
- [Routes](./3.routes.md). Default routes, custom routes and route options.
- [Access rights](./4.access-rights.md). Which fields each action reads and writes.
- [Validators](./5.validators.md). Field validation and mandatory fields.
- [Listable fields](./6.listable.md). Pagination, pluck modes and sorting.

## Relationships and advanced queries

- [Foreign Keys](./7.foreign-keys.md) - Establish relationships between tables
- [Filters](./8.filters.md) - Add filtering capabilities to list endpoints
- [Modifiers](./9.modifiers.md) - Automatic data transformation with database-decorators
- [Joined Fields](./10.joined.md) - Flatten a remote field onto rows for native sort/filter
- [Computed Fields](./11.computed.md) - In-database computed values, sortable and pageable natively
- [Foreign keys](./7.foreign-keys.md). Relations between tables.
- [Filters](./8.filters.md). Filters on list endpoints.
- [Modifiers](./9.modifiers.md). Data transformation with database-decorators modifiers.
- [Joined fields](./10.joined.md). A remote field flattened onto each row, sortable and filterable in the database.
- [Computed fields](./11.computed.md). Values computed in the database, sortable and pageable there.
Loading
Loading