CRUD Endpoints
Auto-generated RESTful CRUD endpoints for each resource. Learn how to use list, get, create, update, and delete operations with filtering and pagination.
Quickback automatically generates RESTful CRUD endpoints for each resource you define. This page covers how to use these endpoints.
Endpoint Overview
For a resource named rooms, Quickback generates:
| Method | Endpoint | Description |
|---|---|---|
GET | /rooms | List all records |
GET | /rooms/:id | Get a single record |
POST | /rooms | Create a new record |
POST | /rooms/batch | Batch create multiple records |
PATCH | /rooms/:id | Update a record |
PATCH | /rooms/batch | Batch update multiple records |
DELETE | /rooms/:id | Delete a record |
DELETE | /rooms/batch | Batch delete multiple records |
PUT | /rooms/:id | Upsert a record (requires config) |
PUT | /rooms/batch | Batch upsert multiple records (requires config) |
List Records
GET /roomsReturns a paginated list of records the user has access to.
Query Parameters
| Parameter | Description | Example |
|---|---|---|
limit | Number of records to return (default: 50, max: 100) | ?limit=25 |
offset | Number of records to skip | ?offset=50 |
sort | Field to sort by | ?sort=createdAt |
order | Sort direction: asc or desc | ?order=desc |
Filtering
Filter records using query parameters:
GET /rooms?status=active # Exact match
GET /rooms?capacity.gt=10 # Greater than
GET /rooms?capacity.gte=10 # Greater than or equal
GET /rooms?capacity.lt=50 # Less than
GET /rooms?capacity.lte=50 # Less than or equal
GET /rooms?status.ne=deleted # Not equal
GET /rooms?name.like=Conference # Pattern match (LIKE %value%)
GET /rooms?status.in=active,pending,review # IN clauseResponse
{
"data": [
{
"id": "room_123",
"name": "Conference Room A",
"capacity": 10,
"roomTypeId": "rt_456",
"roomType_label": "Conference",
"status": "active",
"createdAt": "2024-01-15T10:30:00Z"
}
],
"pagination": {
"limit": 50,
"offset": 0,
"count": 1
}
}FK Label Resolution
Foreign key columns are automatically enriched with _label fields containing the referenced table's display value. For example, roomTypeId gets a corresponding roomType_label field. See Display Column for details.
Get Single Record
GET /rooms/:idReturns a single record by ID.
Response
{
"id": "room_123",
"name": "Conference Room A",
"capacity": 10,
"roomTypeId": "rt_456",
"roomType_label": "Conference",
"status": "active",
"createdAt": "2024-01-15T10:30:00Z"
}Errors
| Status | Description |
|---|---|
404 | Record not found or not accessible |
403 | User lacks permission to view this record |
Create Record
POST /rooms
Content-Type: application/json
{
"name": "Conference Room B",
"capacity": 8,
"roomType": "meeting"
}Creates a new record. Only fields listed in guards.createable are accepted.
Response
{
"data": {
"id": "room_456",
"name": "Conference Room B",
"capacity": 8,
"roomType": "meeting",
"createdAt": "2024-01-15T11:00:00Z",
"createdBy": "user_789"
}
}Errors
| Status | Description |
|---|---|
400 | Invalid field or missing required field |
403 | User lacks permission to create records |
Update Record
PATCH /rooms/:id
Content-Type: application/json
{
"name": "Updated Room Name",
"capacity": 12
}Updates an existing record. Only fields listed in guards.updatable are accepted.
Response
{
"data": {
"id": "room_123",
"name": "Updated Room Name",
"capacity": 12,
"modifiedAt": "2024-01-15T12:00:00Z",
"modifiedBy": "user_789"
}
}Errors
| Status | Description |
|---|---|
400 | Invalid field or field not updatable |
403 | User lacks permission to update this record |
404 | Record not found |
Delete Record
DELETE /rooms/:idDeletes a record. Behavior depends on the delete.mode configuration.
Soft Delete (default)
Sets deletedAt and deletedBy fields. Record remains in database but is filtered from queries.
Hard Delete
Permanently removes the record from the database.
Response
{
"data": {
"id": "room_123",
"deleted": true
}
}Errors
| Status | Description |
|---|---|
403 | User lacks permission to delete this record |
404 | Record not found |
Upsert Record (PUT)
PUT /rooms/:id
Content-Type: application/json
{
"name": "External Room",
"capacity": 20,
"externalId": "ext-123"
}Creates or updates a record by ID. Requires special configuration:
generateId: falsein database configguards: falsein resource definition
Behavior
- If record exists: Updates all provided fields
- If record doesn't exist: Creates with the provided ID
Use Cases
- Syncing data from external systems
- Webhook handlers with external IDs
- Idempotent operations (safe to retry)
See Guards documentation for setup details.
Batch Operations
Quickback provides batch endpoints for efficient bulk operations. Batch operations automatically inherit from their corresponding CRUD operations and maintain full security layer consistency.
Batch Create Records
POST /rooms/batch
Content-Type: application/json
{
"records": [
{ "name": "Room A", "capacity": 10 },
{ "name": "Room B", "capacity": 20 },
{ "name": "Room C", "capacity": 15 }
],
"options": {
"atomic": false
}
}Creates multiple records in a single request. Each record follows the same validation rules as single create operations.
Request Body
| Field | Type | Description |
|---|---|---|
records | Array | Array of record objects to create |
options.atomic | Boolean | If true, all records succeed or all fail (default: false) |
Response (Partial Success - Default)
{
"success": [
{ "id": "room_1", "name": "Room A", "capacity": 10 },
{ "id": "room_2", "name": "Room B", "capacity": 20 }
],
"errors": [
{
"index": 2,
"record": { "name": "Room C", "capacity": 15 },
"error": {
"error": "Field cannot be set during creation",
"layer": "guards",
"code": "GUARD_FIELD_NOT_CREATEABLE",
"details": { "fields": ["status"] }
}
}
],
"meta": {
"total": 3,
"succeeded": 2,
"failed": 1,
"atomic": false
}
}HTTP Status Codes
201- All records created successfully207- Partial success (some records failed)400- Atomic mode enabled and one or more records failed
Batch Size Limit
Default: 100 records per request (configurable via maxBatchSize)
Batch Update Records
PATCH /rooms/batch
Content-Type: application/json
{
"records": [
{ "id": "room_1", "capacity": 12 },
{ "id": "room_2", "name": "Updated Room B" },
{ "id": "room_3", "capacity": 25 }
],
"options": {
"atomic": false
}
}Updates multiple records in a single request. All records must include an id field.
Request Body
| Field | Type | Description |
|---|---|---|
records | Array | Array of record objects with id and fields to update |
options.atomic | Boolean | If true, all records succeed or all fail (default: false) |
Response
{
"success": [
{ "id": "room_1", "capacity": 12, "modifiedAt": "2024-01-15T14:00:00Z" },
{ "id": "room_2", "name": "Updated Room B", "modifiedAt": "2024-01-15T14:00:00Z" }
],
"errors": [
{
"index": 2,
"record": { "id": "room_3", "capacity": 25 },
"error": {
"error": "Not found",
"layer": "firewall",
"code": "NOT_FOUND",
"details": { "id": "room_3" }
}
}
],
"meta": {
"total": 3,
"succeeded": 2,
"failed": 1,
"atomic": false
}
}Features
- Batch fetching: Single database query for all IDs (with firewall)
- Per-record access: Access checks run with record context
- Field validation: Guards apply to each record individually
Batch Delete Records
DELETE /rooms/batch
Content-Type: application/json
{
"ids": ["room_1", "room_2", "room_3"],
"options": {
"atomic": false
}
}Deletes multiple records in a single request. Supports both soft and hard delete modes.
Request Body
| Field | Type | Description |
|---|---|---|
ids | Array | Array of record IDs to delete |
options.atomic | Boolean | If true, all records succeed or all fail (default: false) |
Response (Soft Delete)
{
"success": [
{ "id": "room_1", "deletedAt": "2024-01-15T15:00:00Z", "deletedBy": "user_789" },
{ "id": "room_2", "deletedAt": "2024-01-15T15:00:00Z", "deletedBy": "user_789" }
],
"errors": [
{
"index": 2,
"id": "room_3",
"error": {
"error": "Not found",
"layer": "firewall",
"code": "NOT_FOUND",
"details": { "id": "room_3" }
}
}
],
"meta": {
"total": 3,
"succeeded": 2,
"failed": 1,
"atomic": false
}
}Delete Modes
- Soft delete (default): Sets
deletedAt,deletedBy,modifiedAt,modifiedByfields - Hard delete: Permanently removes records from database
Batch Upsert Records
PUT /rooms/batch
Content-Type: application/json
{
"records": [
{ "id": "room_1", "name": "Updated Room A", "capacity": 10 },
{ "id": "new_room", "name": "New Room", "capacity": 30 }
],
"options": {
"atomic": false
}
}Creates or updates multiple records in a single request. Creates if ID doesn't exist, updates if it does.
Strict Requirements (same as single PUT):
generateId: falsein database config (user provides IDs)guards: falsein resource definition (no field restrictions)- All records must include an
idfield
Note: System-managed fields (createdAt, createdBy, modifiedAt, modifiedBy, deletedAt, deletedBy) are always protected and will be rejected if included in the request, regardless of guards configuration.
Request Body
| Field | Type | Description |
|---|---|---|
records | Array | Array of record objects with id and all fields |
options.atomic | Boolean | If true, all records succeed or all fail (default: false) |
Response
{
"success": [
{ "id": "room_1", "name": "Updated Room A", "capacity": 10, "modifiedAt": "2024-01-15T16:00:00Z" },
{ "id": "new_room", "name": "New Room", "capacity": 30, "createdAt": "2024-01-15T16:00:00Z" }
],
"errors": [],
"meta": {
"total": 2,
"succeeded": 2,
"failed": 0,
"atomic": false
}
}How It Works
- Batch existence check with firewall
- Split records into CREATE and UPDATE batches
- Validate new records with
validateCreate() - Validate existing records with
validateUpdate() - Check CREATE access for new records
- Check UPDATE access for existing records (per-record)
- Execute bulk insert and individual updates
- Return combined results
Batch Operation Features
Partial Success Mode (Default)
By default, batch operations use partial success mode:
- All records are processed independently
- Failed records go into
errorsarray with detailed error information - Successful records go into
successarray - HTTP status
207 Multi-Statusif any errors,201/200if all success
{
"success": [ /* succeeded records */ ],
"errors": [
{
"index": 2,
"record": { /* original input */ },
"error": {
"error": "Human-readable message",
"layer": "guards",
"code": "GUARD_FIELD_NOT_CREATEABLE",
"details": { "fields": ["status"] },
"hint": "These fields are set automatically or must be omitted"
}
}
],
"meta": {
"total": 10,
"succeeded": 8,
"failed": 2,
"atomic": false
}
}Atomic Mode (Opt-in)
Enable atomic mode for all-or-nothing behavior:
{
"records": [ /* ... */ ],
"options": {
"atomic": true
}
}Atomic mode behavior:
- First error immediately stops processing
- All changes are rolled back (database transaction)
- HTTP status
400 Bad Request - Returns single error with failure details
{
"error": "Batch operation failed in atomic mode",
"layer": "validation",
"code": "BATCH_ATOMIC_FAILED",
"details": {
"failedAt": 2,
"reason": { /* the actual error */ }
},
"hint": "Transaction rolled back. Fix the error and retry the entire batch."
}Human-Readable Errors
All batch operation errors include:
- Layer identification: Which security layer rejected the request
- Error code: Machine-readable code for programmatic handling
- Clear message: Human-readable explanation
- Details: Contextual information (fields, IDs, reasons)
- Helpful hints: Actionable guidance for resolution
Performance Optimizations
- Batch size limits: Default 100 records (prevents memory exhaustion)
- Single firewall query:
WHERE id IN (...)instead of N queries - Bulk operations: Single INSERT for multiple records (CREATE, UPSERT)
- O(1) lookups: Map-based record lookup instead of Array.find()
Configuration
Batch operations are auto-enabled when corresponding CRUD operations exist:
// Auto-enabled - no configuration needed
crud: {
create: { access: { roles: ['member'] } },
update: { access: { roles: ['member'] } }
// batchCreate and batchUpdate automatically available
}
// Customize batch operations
crud: {
create: { access: { roles: ['member'] } },
batchCreate: {
access: { roles: ['admin'] }, // Different access rules
maxBatchSize: 50, // Lower limit
allowAtomic: false // Disable atomic mode
}
}
// Disable batch operations
crud: {
create: { access: { roles: ['member'] } },
batchCreate: false // Explicitly disable
}Security Layer Application
Batch operations maintain full security layer consistency:
- Firewall: Auto-apply ownership fields, batch fetch with isolation
- Access: Operation-level for CREATE, per-record for UPDATE/DELETE
- Guards: Per-record field validation (same rules as single operations)
- Masking: Applied to success array (respects user permissions)
- Audit: Single timestamp for entire batch for consistency
Authentication
All endpoints require authentication. Include your auth token in the request header:
Authorization: Bearer <your-token>The user's context (userId, roles, organizationId) is extracted from the token and used to:
- Apply firewall filters (data isolation)
- Check access permissions
- Set audit fields (createdBy, modifiedBy)
Error Responses
All errors use a flat structure with contextual fields:
{
"error": "Insufficient permissions",
"layer": "access",
"code": "ACCESS_ROLE_REQUIRED",
"details": {
"required": ["admin"],
"current": ["member"]
},
"hint": "Contact an administrator to grant necessary permissions"
}See Errors for the complete reference of error codes by security layer.