REST JSON API
@xond/api generates RESTful JSON APIs for all your Prisma models. This guide explains how to call these APIs, organized by operation type.
Base URL
All JSON API endpoints follow this pattern:
GET|POST|PUT|DELETE /json/:resource
GET|PUT|DELETE /json/:resource/:id
Where :resource is the camelCase name of your model (e.g., user, product, orderItem).
Authentication
All endpoints require JWT authentication. Include the JWT token in your request:
For GET Requests
Include the JWT token in the Authorization header:
GET /json/user
Authorization: Bearer <your-jwt-token>
For POST/PUT Requests
Include the JWT token in the request body as jwtCookie:
{
"jwtCookie": {
"id": "user-uuid",
"email": "user@example.com",
"role": "admin"
},
"name": "John Doe",
"email": "john@example.com"
}
The jwtCookie object contains user information extracted from the JWT token by your authentication middleware.
Response Format
All API responses follow this format:
{
"success": boolean,
"data": any | null,
"pagination": {
"totalRecords": number,
"totalPages": number,
"currentPage": number,
"pageSize": number
} | null,
"error": string | null
}
Success Response
{
"success": true,
"data": [...],
"pagination": {
"totalRecords": 100,
"totalPages": 10,
"currentPage": 1,
"pageSize": 10
},
"error": null
}
Error Response
{
"success": false,
"data": null,
"pagination": null,
"error": "An error occurred while fetching data."
}
Create
Create new records using POST requests.
Endpoint
POST /json/:resource
Request Body
Include jwtCookie and the data to create:
{
"jwtCookie": {
"id": "user-uuid",
"email": "user@example.com"
},
"name": "John Doe",
"email": "john@example.com",
"organizationId": "00000000-0000-0000-0000-000000000002"
}
Response
{
"success": true,
"data": {
"userId": "00000000-0000-0000-0000-000000000003",
"name": "John Doe",
"email": "john@example.com",
"createdAt": "2024-01-01T00:00:00.000Z",
"lastUpdated": "2024-01-01T00:00:00.000Z",
"updatedBy": "user-uuid",
"dataStatus": 1
},
"pagination": null,
"error": null
}
Auto-Generated Fields
The API automatically sets:
createdAt– Current timestamplastUpdated– Current timestampupdatedBy– User ID fromjwtCookie.iddataStatus– Set to1(active)- Primary key – Generated UUID if not provided
Creating Records with Relations
You can create records with nested relation data:
{
"jwtCookie": {
"id": "user-uuid"
},
"userId": "00000000-0000-0000-0000-000000000001",
"totalAmount": 99.99,
"orderItems": [
{
"productId": "00000000-0000-0000-0000-000000000002",
"quantity": 2,
"price": 49.99
}
]
}
Example
POST /json/user
Content-Type: application/json
Authorization: Bearer <token>
{
"jwtCookie": {
"id": "00000000-0000-0000-0000-000000000001"
},
"name": "John Doe",
"email": "john@example.com",
"organizationId": "00000000-0000-0000-0000-000000000002"
}
Retrieve
Retrieve records using GET requests. All filtering, sorting, paging, and search features are available for retrieve operations.
Endpoints
GET /json/:resource– Fetch multiple recordsGET /json/:resource/:id– Fetch a single record by ID
Fetch All Records
GET /json/:resource
Fetch Single Record
GET /json/:resource/:id
Query Parameters:
| Parameter | Type | Description | Example |
|---|---|---|---|
include | string | Comma-separated relations to include | ?include=organization,roles |
Example:
GET /json/user/00000000-0000-0000-0000-000000000001?include=organization
Response:
{
"success": true,
"data": {
"userId": "00000000-0000-0000-0000-000000000001",
"name": "John Doe",
"email": "john@example.com",
"organization": {
"organizationId": "00000000-0000-0000-0000-000000000002",
"name": "Acme Corp"
}
},
"pagination": null,
"error": null
}
Base Filters
Base filters are used to lock the table's context, ensuring users only access data they're authorized to see. Common use cases:
- Filter by
userIdto show only records belonging to the current user - Filter by
organizationIdto show only records for the user's organization - Filter by
yearto show only records for a specific year
Base filters are always applied and cannot be overridden by regular filters.
Format
Base filters are passed as an array in the query string:
?baseFilter=[{"field":"userId","value":"user-uuid","comparison":"equals"}]
Or URL-encoded:
?baseFilter=%5B%7B%22field%22%3A%22userId%22%2C%22value%22%3A%22user-uuid%22%2C%22comparison%22%3A%22equals%22%7D%5D
Multiple Base Filters
You can apply multiple base filters (they are combined with AND):
?baseFilter=[
{"field":"userId","value":"user-uuid","comparison":"equals"},
{"field":"year","value":"2024","comparison":"equals"}
]
Comparison Operators
| Operator | Description | Example |
|---|---|---|
equals | Exact match | {"field":"status","value":"active","comparison":"equals"} |
notEquals | Not equal | {"field":"status","value":"deleted","comparison":"notEquals"} |
greaterThan | Greater than | {"field":"age","value":"18","comparison":"greaterThan"} |
greaterThanOrEqual | Greater than or equal | {"field":"age","value":"18","comparison":"greaterThanOrEqual"} |
lessThan | Less than | {"field":"age","value":"65","comparison":"lessThan"} |
lessThanOrEqual | Less than or equal | {"field":"age","value":"65","comparison":"lessThanOrEqual"} |
contains | String contains | {"field":"name","value":"john","comparison":"contains"} |
startsWith | String starts with | {"field":"name","value":"john","comparison":"startsWith"} |
endsWith | String ends with | {"field":"email","value":"@example.com","comparison":"endsWith"} |
in | Value in array | {"field":"status","value":["active","pending"],"comparison":"in"} |
notIn | Value not in array | {"field":"status","value":["deleted","archived"],"comparison":"notIn"} |
isNull | Field is null | {"field":"deletedAt","value":null,"comparison":"isNull"} |
isNotNull | Field is not null | {"field":"deletedAt","value":null,"comparison":"isNotNull"} |
Example: Filter by User and Year
GET /json/order?baseFilter=[
{"field":"userId","value":"00000000-0000-0000-0000-000000000001","comparison":"equals"},
{"field":"year","value":"2024","comparison":"equals"}
]
This ensures users only see orders from 2024 that belong to them.
Filters
Filters are used for further filtering of data beyond base filters. They work the same way as base filters but are applied after base filters.
Format
?filter=[{"field":"status","value":"active","comparison":"equals"}]
Multiple Filters
Multiple filters can be combined with AND or OR using filterClause:
AND (default):
?filter=[
{"field":"status","value":"active","comparison":"equals"},
{"field":"category","value":"electronics","comparison":"equals"}
]&filterClause=and
OR:
?filter=[
{"field":"status","value":"active","comparison":"equals"},
{"field":"status","value":"pending","comparison":"equals"}
]&filterClause=or
Filtering Related Fields
You can filter by fields in related tables using dot notation:
?filter=[{"field":"organization.name","value":"Acme Corp","comparison":"equals"}]
Example: Filter Active Products in Electronics Category
GET /json/product?filter=[
{"field":"status","value":"active","comparison":"equals"},
{"field":"category","value":"electronics","comparison":"equals"}
]&filterClause=and
Sorting
Sort records by one or more fields.
Format
?sort=[{"field":"name","direction":"asc"}]
Multiple Sorts
Sort by multiple fields:
?sort=[
{"field":"status","direction":"asc"},
{"field":"createdAt","direction":"desc"}
]
Sorts are applied in order: first by status ascending, then by createdAt descending.
Sort Directions
asc– Ascending orderdesc– Descending order
Sorting Related Fields
Sort by fields in related tables:
?sort=[{"field":"organization.name","direction":"asc"}]
Example: Sort by Name Ascending
GET /json/user?sort=[{"field":"name","direction":"asc"}]
Example: Sort by Status Then Created Date
GET /json/order?sort=[
{"field":"status","direction":"asc"},
{"field":"createdAt","direction":"desc"}
]
Paging
Control pagination with page and pageSize parameters.
Parameters
page– Page number (default: 1)pageSize– Records per page (default: 10)
Example
GET /json/user?page=2&pageSize=20
This returns page 2 with 20 records per page.
Pagination Response
The response includes pagination metadata:
{
"success": true,
"data": [...],
"pagination": {
"totalRecords": 150,
"totalPages": 8,
"currentPage": 2,
"pageSize": 20
},
"error": null
}
Text Search
Search across multiple fields using text search.
Parameters
textSearchFields– Comma-separated list of fields to searchtextSearchValue– The search term
Format
?textSearchFields=name,email,phone&textSearchValue=john
Searching Related Fields
Search in related table fields using dot notation:
?textSearchFields=name,organization.name,organization.address&textSearchValue=acme
How It Works
Text search uses contains matching (case-insensitive in PostgreSQL). It searches across all specified fields and returns records where any field matches.
Example: Search Users by Name or Email
GET /json/user?textSearchFields=name,email&textSearchValue=john
This returns users where name or email contains "john".
Example: Search Orders by Customer Name
GET /json/order?textSearchFields=customer.name,customer.email&textSearchValue=smith
Including Relations
Include related data using the include parameter.
Format
?include=organization,roles,permissions
Nested Relations
Include nested relations using dot notation:
?include=organization.address,organization.contacts
Example: Include User's Organization
GET /json/user/00000000-0000-0000-0000-000000000001?include=organization
Response:
{
"success": true,
"data": {
"userId": "00000000-0000-0000-0000-000000000001",
"name": "John Doe",
"email": "john@example.com",
"organization": {
"organizationId": "00000000-0000-0000-0000-000000000002",
"name": "Acme Corp"
}
}
}
Selecting Specific Columns
Select only specific columns using the columns parameter.
Format
?columns=name,email,phone
Combining with Include
You can combine columns with include:
?columns=name,email&include=organization
This selects only name and email from the main table, but includes all fields from organization.
Example: Select Only Name and Email
GET /json/user?columns=name,email
Count Only
Get the count of records without fetching data:
?count=true
Example
GET /json/user?baseFilter=[{"field":"status","value":"active","comparison":"equals"}]&count=true
Response:
{
"success": true,
"data": {
"count": 42
},
"pagination": null,
"error": null
}
Complete Retrieve Examples
Example 1: Basic Fetch with Pagination
GET /json/user?page=1&pageSize=20&sort=[{"field":"name","direction":"asc"}]&include=organization
Example 2: Fetch with Base Filter
GET /json/order?baseFilter=[
{"field":"userId","value":"00000000-0000-0000-0000-000000000001","comparison":"equals"},
{"field":"year","value":"2024","comparison":"equals"}
]&page=1&pageSize=20
Example 3: Fetch with Filters
GET /json/product?baseFilter=[{"field":"organizationId","value":"org-uuid","comparison":"equals"}]&filter=[
{"field":"status","value":"active","comparison":"equals"},
{"field":"category","value":"electronics","comparison":"equals"}
]&filterClause=and&page=1&pageSize=10
Example 4: Fetch with Text Search
GET /json/user?baseFilter=[{"field":"organizationId","value":"org-uuid","comparison":"equals"}]&textSearchFields=name,email&textSearchValue=john&sort=[{"field":"name","direction":"asc"}]
Example 5: Complex Query with All Features
GET /json/order?baseFilter=[
{"field":"userId","value":"user-uuid","comparison":"equals"},
{"field":"year","value":"2024","comparison":"equals"}
]&filter=[
{"field":"status","value":"completed","comparison":"equals"},
{"field":"totalAmount","value":"100","comparison":"greaterThan"}
]&filterClause=and&textSearchFields=customer.name,customer.email&textSearchValue=smith&sort=[
{"field":"createdAt","direction":"desc"},
{"field":"totalAmount","direction":"desc"}
]&include=customer,orderItems.product&page=1&pageSize=20
Update
Update existing records using PUT requests.
Endpoint
PUT /json/:resource/:id
Request Body
Include jwtCookie and the fields to update:
{
"jwtCookie": {
"id": "user-uuid"
},
"name": "Jane Doe",
"email": "jane@example.com"
}
Response
{
"success": true,
"data": {
"userId": "00000000-0000-0000-0000-000000000001",
"name": "Jane Doe",
"email": "jane@example.com",
"lastUpdated": "2024-01-02T00:00:00.000Z",
"updatedBy": "user-uuid"
},
"pagination": null,
"error": null
}
Auto-Updated Fields
The API automatically updates:
lastUpdated– Current timestampupdatedBy– User ID fromjwtCookie.id
Partial Updates
You only need to include the fields you want to update. Other fields remain unchanged.
Example
PUT /json/user/00000000-0000-0000-0000-000000000001
Content-Type: application/json
Authorization: Bearer <token>
{
"jwtCookie": {
"id": "00000000-0000-0000-0000-000000000001"
},
"name": "Jane Doe",
"email": "jane@example.com"
}
Updating Relations
You can update relation fields by providing the foreign key value:
{
"jwtCookie": {
"id": "user-uuid"
},
"organizationId": "00000000-0000-0000-0000-000000000003"
}
Delete
Delete records using DELETE requests.
Endpoint
DELETE /json/:resource/:id
Request
No request body required. Include JWT token in Authorization header.
Example
DELETE /json/user/00000000-0000-0000-0000-000000000001
Authorization: Bearer <your-jwt-token>
Response
{
"success": true,
"data": null,
"pagination": null,
"error": null
}
Error Response
If the record doesn't exist or cannot be deleted:
{
"success": false,
"data": null,
"pagination": null,
"error": "Record not found or cannot be deleted"
}
Best Practices
- Always use base filters for context locking (user, organization, etc.) in retrieve operations
- Use filters for user-selected filtering options
- Combine base filters when you need multiple context locks
- Use text search for user-friendly search functionality
- Include relations only when needed to reduce payload size
- Select specific columns when you don't need all fields
- Use pagination for large datasets
- Handle errors by checking the
successfield - Include JWT token in all requests
- Use count=true when you only need the count
- Only include fields to update in PUT requests (partial updates)
- Verify record exists before deleting
Next Steps
- Learn about code generation
- Explore configuration options
- See commands reference