Data Management
Data
Phestus provides a data service for interacting with application records.
The data service is exposed through:
context.service.data
It provides a consistent API for querying and mutating data without requiring modules to communicate directly with a specific database, CMS, ORM, or storage provider.
The primary operations are:
find
findById
count
create
update
delete
Finding Data
Records can be queried using find.
const users = await context.service.data.find(
'users',
)
A query can optionally be provided:
const users = await context.service.data.find(
'users',
{
limit: 10,
offset: 0,
},
)
The result is a FindResult:
interface FindResult<T> {
docs: T[];
totalDocs: number;
limit: number;
offset: number;
hasNextPage: boolean;
hasPrevPage: boolean;
}
This provides both the returned records and pagination information.
Finding a Record by ID
When a specific record is needed, findById can be used.
const user = await context.service.data.findById(
'users',
userId,
)
The result is either the requested record or null when no matching record exists.
Counting Records
The count method returns the number of records matching an optional query.
const total = await context.service.data.count(
'users',
)
A query can also be provided:
const total = await context.service.data.count(
'users',
{
where: {
fields: {
status: {
equals: 'active',
},
},
},
},
)
Queries
Queries are represented by PhestusQuery.
interface PhestusQuery {
where?: PhestusWhere;
sort?: string | string[];
limit?: number;
offset?: number;
select?: string[];
}
Queries can therefore control:
- Filtering
- Sorting
- Pagination
- Selected fields
Filtering
Filtering is performed through where.
Field conditions are defined through fields.
const users = await context.service.data.find(
'users',
{
where: {
fields: {
email: {
equals: 'john@example.com',
},
},
},
},
)
Multiple fields can be queried at the same time:
const users = await context.service.data.find(
'users',
{
where: {
fields: {
status: {
equals: 'active',
},
age: {
greaterThanOrEqual: 18,
},
},
},
},
)
Query Operators
Phestus defines the following query operators:
equals
notEquals
contains
startsWith
endsWith
greaterThan
greaterThanOrEqual
lessThan
lessThanOrEqual
in
notIn
exists
For example:
const users = await context.service.data.find(
'users',
{
where: {
fields: {
name: {
startsWith: 'John',
},
},
},
},
)
Values can be strings, numbers, booleans, dates, null, or arrays of supported query values.
Combining Conditions
Queries can combine conditions using and, or, and not.
For example:
const users = await context.service.data.find(
'users',
{
where: {
and: [
{
fields: {
status: {
equals: 'active',
},
},
},
{
fields: {
age: {
greaterThanOrEqual: 18,
},
},
},
],
},
},
)
An or condition can be used when any of several conditions should match:
const users = await context.service.data.find(
'users',
{
where: {
or: [
{
fields: {
role: {
equals: 'admin',
},
},
},
{
fields: {
role: {
equals: 'moderator',
},
},
},
],
},
},
)
A condition can also be negated using not.
Sorting
Records can be sorted using sort.
const users = await context.service.data.find(
'users',
{
sort: 'createdAt',
},
)
Multiple sort values can be provided:
const users = await context.service.data.find(
'users',
{
sort: ['createdAt', 'name'],
},
)
The interpretation of sort values is handled by the service implementation.
Pagination
Pagination is controlled through limit and offset.
const users = await context.service.data.find(
'users',
{
limit: 20,
offset: 40,
},
)
The returned FindResult contains the pagination state:
{
docs,
totalDocs,
limit,
offset,
hasNextPage,
hasPrevPage,
}
Selecting Fields
Specific fields can be requested using select.
const users = await context.service.data.find(
'users',
{
select: [
'name',
'email',
],
},
)
The service implementation determines how field selection is translated to the underlying provider.
Creating Data
Records are created with create.
const user = await context.service.data.create(
'users',
{
name: 'John Doe',
email: 'john@example.com',
},
)
The returned value is the created record.
Updating Data
Existing records can be updated with update.
const user = await context.service.data.update(
'users',
userId,
{
name: 'Jane Doe',
},
)
The collection and record ID identify the record, while the final argument contains the data to update.
Deleting Data
Records can be deleted with delete.
const user = await context.service.data.delete(
'users',
userId,
)
The deleted record is returned by the service.
Data and Schemas
Data management and schema management represent two different responsibilities within the service layer.
Schema management defines what the data looks like:
Schema
↓
Fields
↓
Types
↓
Validation
↓
Relationships
Data management operates on records conforming to that schema:
Data
↓
Find
↓
Create
↓
Update
↓
Delete
Together, they provide the foundation for provider-independent data management in Phestus.
Service Abstraction
The data service does not require modules to know how records are stored.
The architecture is:
Module
↓
PhestusService
↓
DataService
↓
Service Adapter
↓
Data Provider
A module can therefore use:
await context.service.data.find(
'users',
)
without knowing whether the underlying implementation uses Payload, PostgreSQL, another database, or another data source.
The service adapter is responsible for translating Phestus queries and data operations into the implementation-specific API.
This keeps modules focused on application capabilities while the service layer manages the details of data storage and retrieval.