Skip to content

Latest commit

 

History

History
915 lines (708 loc) · 22.7 KB

File metadata and controls

915 lines (708 loc) · 22.7 KB
path docs/api/Collection_API.mdx
title Collection API Reference
description Complete API documentation for SveltyCMS Collections - endpoints, parameters, response formats, and advanced features
order 41
icon mdi:api
author admin
created 2025-08-06
updated 2025-10-02
tags
developer
api
collections
reference

Collection API Reference

The Collection API provides comprehensive endpoints for managing collection entries in SveltyCMS. This API supports advanced features including pagination, filtering, sorting, batch operations, and multi-tenant isolation.

Table of Contents

Base Endpoints

All collection API endpoints follow the pattern:

/api/collections/[collectionId]/[operation]

Primary Routes

Method Endpoint Description Notes
GET /api/collections List all collections ✅ API endpoint (metadata only)
GET /api/collections/{collectionId} List collection entries REMOVED - Use +page.server.ts load()
POST /api/collections/{collectionId} Create new entry ✅ Client-side mutation
GET /api/collections/{collectionId}/{entryId} Get specific entry REMOVED - Use +page.server.ts load()
PATCH /api/collections/{collectionId}/{entryId} Update entry ✅ Client-side mutation
DELETE /api/collections/{collectionId}/{entryId} Delete entry ✅ Client-side mutation (returns 204)
POST /api/collections/{collectionId}/batch Batch operations ✅ Client-side mutation
PATCH /api/collections/{collectionId}/{entryId}/status Update entry status ✅ Client-side mutation
GET /api/collections/{collectionId}/{entryId}/revisions Get revision history ✅ API endpoint
POST /api/collections/{collectionId}/import Import data (JSON/CSV) ✅ Client-side mutation
GET /api/collections/{collectionId}/export Export data (JSON/CSV) ✅ API endpoint

Authentication & Authorization

Required Headers

Authorization: Bearer <jwt_token>
Content-Type: application/json

Permission Levels

  • Admin: Full access to all operations
  • User: Limited to published content (non-admin users)
  • Role-based: Permissions defined by user role configuration

Multi-Tenant Isolation

In multi-tenant mode, all operations are automatically scoped to the tenant context from the JWT token.

Common Parameters

URL Parameters

  • collectionId (string, required): The unique identifier of the collection
  • entryId (string, optional): The unique identifier of a specific entry

Query Parameters

Pagination

{
  page?: number;          // Page number (default: 1)
  pageSize?: number;      // Items per page (default: 25)
}

Filtering

{
  filter?: string;        // JSON string of filter criteria
}

Filter Examples:

// Simple equality
{ "status": "published" }

// Negation operator
{ "status": "!=deleted" }

// Multiple conditions
{
  "status": "published",
  "category": "news"
}

Sorting

{
  sortField?: string;     // Field to sort by (default: "createdAt")
  sortDirection?: "asc" | "desc";  // Sort direction (default: "desc")
}

Alternative Sorting (Legacy):

{
  sort?: string;          // JSON string: {"fieldName": 1|-1}
}

Language & Content

{
  contentLanguage?: string;  // Language code for translated content
  _langChange?: number;      // Timestamp for cache invalidation
  _cacheBust?: number;       // Timestamp for cache busting
}

Collection Management

List Collections

Retrieves metadata for all collections accessible to the authenticated user.

Request:

GET /api/collections?includeFields=true&includeStats=false

Query Parameters:

  • includeFields (boolean, optional) - Include field schemas in response (default: false)
  • includeStats (boolean, optional) - Include entry statistics (default: false)

Response:

{
	"success": true,
	"data": {
		"collections": [
			{
				"id": "23d772dd3783492a9115ee9ea6bc6185",
				"name": "posts",
				"label": "Posts",
				"description": "Blog posts collection",
				"icon": "mdi:post",
				"path": "/posts",
				"permissions": {
					"read": true,
					"write": true
				},
				"fields": [],
				"stats": {
					"totalEntries": 0,
					"publishedEntries": 0,
					"draftEntries": 0
				}
			}
		],
		"total": 1
	},
	"performance": {
		"duration": 12.5
	}
}

Entry Management

List Entries

⚠️ REMOVED - Use +page.server.ts load() function for SSR data loading instead.

For initial page loads, use SvelteKit's load function:

// +page.server.ts
export const load: PageServerLoad = async ({ locals, params }) => {
	const entries = await dbAdapter
		.queryBuilder(`collection_${collectionId}`)
		.where({ tenantId: locals.tenantId })
		.paginate({ page: 1, pageSize: 25 })
		.execute();

	return { entries: entries.data };
};

List Entries (Old API - Removed)

Request:

GET /api/collections/posts?page=1&pageSize=10&filter={"status":"published"}

Response:

{
	"success": true,
	"data": {
		"items": [
			{
				"_id": "entry123",
				"title": "Sample Post",
				"content": "Post content...",
				"status": "published",
				"createdAt": "2025-08-06T10:00:00Z",
				"updatedAt": "2025-08-06T10:00:00Z"
			}
		],
		"total": 150,
		"page": 1,
		"pageSize": 10,
		"totalPages": 15
	},
	"performance": {
		"duration": 45.2
	}
}

Create Entry

Request:

POST /api/collections/posts
Content-Type: application/json

{
  "title": "New Post",
  "content": "Post content...",
  "status": "draft",
  "tags": ["technology", "web"]
}

Response:

{
	"success": true,
	"data": {
		"_id": "entry456",
		"title": "New Post",
		"content": "Post content...",
		"status": "draft",
		"tags": ["technology", "web"],
		"createdAt": "2025-08-06T10:30:00Z",
		"updatedAt": "2025-08-06T10:30:00Z",
		"createdBy": "user123",
		"updatedBy": "user123"
	}
}

Update Entry

Request:

PATCH /api/collections/posts/entry456
Content-Type: application/json

{
  "title": "Updated Post Title",
  "status": "published"
}

Response:

{
	"success": true,
	"data": {
		"_id": "entry456",
		"title": "Updated Post Title",
		"status": "published",
		"updatedAt": "2025-08-06T11:00:00Z",
		"updatedBy": "user123"
	}
}

Delete Entry

Request:

DELETE /api/collections/posts/entry456

Response:

HTTP/1.1 204 No Content

No response body is returned. A successful deletion returns HTTP status 204 (No Content).

Batch Operations

Batch Update/Delete/Clone

Request:

POST /api/collections/posts/batch
Content-Type: application/json

{
  "action": "status",
  "entryIds": ["entry1", "entry2", "entry3"],
  "status": "publish"
}

Note: Valid status values are: publish, unpublish, draft, archived.


**Available Actions:**

- `status`: Update status of multiple entries (requires `status` field: `publish`, `unpublish`, `draft`, `archived`)
- `delete`: Delete multiple entries
- `clone`: Clone multiple entries (requires `cloneCount` field)

**Response:**

```json
{
	"success": true,
	"data": {
		"processed": 3,
		"successful": 3,
		"failed": 0,
		"results": [
			{
				"entryId": "entry1",
				"success": true,
				"operation": "status_update"
			}
		]
	}
}

Batch Delete Example

Request:

POST /api/collections/posts/batch
Content-Type: application/json

{
  "action": "delete",
  "entryIds": ["entry1", "entry2", "entry3"]
}

Response:

{
	"success": true,
	"message": "3 entries deleted successfully",
	"data": {
		"action": "delete",
		"results": [
			{
				"entryId": "entry1",
				"success": true
			}
		],
		"summary": {
			"total": 3,
			"successful": 3,
			"failed": 0
		}
	}
}

Error Handling

Standard Error Response

{
	"success": false,
	"error": "Error message",
	"code": "ERROR_CODE",
	"details": {
		"field": "validation error details"
	}
}

Common Error Codes

Code HTTP Status Description
UNAUTHORIZED 401 Invalid or missing authentication
FORBIDDEN 403 Insufficient permissions
NOT_FOUND 404 Collection or entry not found
VALIDATION_ERROR 400 Invalid request data
SERVER_ERROR 500 Internal server error

Validation Errors

{
	"success": false,
	"error": "Validation failed",
	"details": {
		"title": "Title is required",
		"email": "Invalid email format"
	}
}

Performance Optimization

Caching Strategy

The API implements a two-tier caching system for optimal performance:

Server-Side Page Cache

  • Pattern: collection:{collectionId}:page:{page}:size:{pageSize}:filter:{...}:sort:{...}:mode:{mode}:tenant:{tenantId}
  • TTL: 300 seconds (5 minutes)
  • Usage: Caches server-rendered collection pages
  • Invalidation: Automatic on any data mutation (create, update, delete, status change)

API Response Cache

  • Pattern: api:userId:/api/collections/{collectionId}*
  • TTL: 300 seconds (5 minutes)
  • Usage: Caches API responses for read operations
  • Invalidation: Automatic on data mutations

Cache Invalidation

All mutation endpoints automatically invalidate cached data:

// Pattern used to clear all cached pages for a collection
const cachePattern = `collection:${collectionId}:*`;
await cacheService.clearByPattern(cachePattern);

Endpoints that trigger cache invalidation:

  • POST /collections/{collectionId} (create entry)
  • PATCH /collections/{collectionId}/{entryId} (update entry)
  • DELETE /collections/{collectionId}/{entryId} (delete entry)
  • PATCH /collections/{collectionId}/{entryId}/status (update status)
  • POST /collections/{collectionId}/batch (batch operations)

This ensures that after any data change, subsequent requests will fetch fresh data from the database.

Query Optimization

  • Indexes: Automatic indexing on _id, status, createdAt, tenantId
  • Projection: Only requested fields returned
  • Pagination: Efficient limit/skip with total count

Performance Monitoring

All API responses include performance metrics:

{
	"performance": {
		"duration": 45.2, // milliseconds
		"cacheHit": false,
		"queryTime": 38.5
	}
}

Multi-Tenant Support

Automatic Tenant Scoping

When MULTI_TENANT=true, all operations are automatically scoped by tenantId:

// Automatically applied to all queries
const filter = {
	...userFilter,
	tenantId: extractedFromJWT
};

Tenant Isolation

  • Data isolation: Complete separation of tenant data
  • Permission isolation: Role-based access within tenant
  • Collection isolation: Tenant-specific collection schemas

Advanced Features

Type System

All collection entries use centralized TypeScript types from @src/content/types.ts:

import type { CollectionEntry, Schema, StatusType } from '@src/content/types';

// CollectionEntry interface extends Record<string, unknown> with standard fields:
interface CollectionEntry extends Record<string, unknown> {
	_id: string;
	status: StatusType;
	createdAt: string;
	updatedAt: string;
	createdBy: string;
	updatedBy: string;
	tenantId?: string;
}

// Status types
type StatusType = 'draft' | 'published' | 'archived' | 'scheduled' | 'unpublished';

This ensures type consistency across the entire codebase and simplifies maintenance.

Widget Processing

All entries are processed through the widget system for:

  • Field validation: Based on widget configuration
  • Data transformation: Custom widget processing
  • Permission checks: Field-level access control

Revision History

GET /api/collections/posts/entry123/revisions

Returns complete revision history for audit trails.

Status Management

Built-in status system supports:

  • draft: Unpublished content
  • published: Public content
  • archived: Soft-deleted content
  • scheduled: Time-based publishing
  • Custom statuses via collection configuration

Language Support

  • Translated fields: Automatic language detection
  • Content language: Request-specific language content
  • Fallback languages: Configurable fallback chain

Integration Examples

Frontend Integration (Svelte)

import { getData } from '@utils/apiClient';

// List entries with filters
const entries = await getData({
	collectionId: 'posts',
	page: 1,
	pageSize: 10,
	filter: JSON.stringify({ status: 'published' }),
	contentLanguage: 'en'
});

Node.js Integration

const response = await fetch('/api/collections/posts', {
	method: 'GET',
	headers: {
		Authorization: `Bearer ${token}`,
		'Content-Type': 'application/json'
	}
});

const data = await response.json();

cURL Examples

# List entries
curl -X GET "https://api.example.com/api/collections/posts?page=1&pageSize=5" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json"

# Create entry
curl -X POST "https://api.example.com/api/collections/posts" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"title":"New Post","content":"Content here","status":"published"}'

# Update entry
curl -X PATCH "https://api.example.com/api/collections/posts/entry123" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"title":"Updated Title","status":"published"}'

# Batch delete
curl -X POST "https://api.example.com/api/collections/posts/batch" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"action":"delete","entryIds":["id1","id2"]}'

# Batch status update
curl -X POST "https://api.example.com/api/collections/posts/batch" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"action":"status","entryIds":["id1","id2"],"status":"published"}'

Testing

Test Suite Overview

The Collection API has comprehensive integration tests located in tests/bun/api/collections.test.ts.

Test Coverage:

Test Area Tests Description
Authentication 6 Verify auth requirements for all endpoints
Collection Listing 2 Test GET /api/collections with various parameters
Content Structure 3 Test collection metadata and recompilation
Search Functionality 3 Test POST /api/search with different queries
CRUD Operations 4 Test create, read, update, delete operations
Data Export 1 Test GET /api/exportData endpoint
Total 19 Comprehensive API coverage

Running Tests

# Run all collection tests
bun test tests/bun/api/collections.test.ts

# Run with verbose output
bun test tests/bun/api/collections.test.ts --verbose

# Run specific test suite
bun test tests/bun/api/collections.test.ts -t "RESTful Collection Operations"

Key Test Scenarios

1. Authentication Tests

// Tests verify that all endpoints require authentication
it('should fail without authentication', async () => {
	const response = await fetch(`${API_BASE_URL}/api/collections`);
	expect(response.status).toBe(401);
});

2. Collection Listing

it('should list all collections with admin authentication', async () => {
	const response = await fetch(`${API_BASE_URL}/api/collections`, {
		headers: { Authorization: `Bearer ${authToken}` }
	});
	expect(response.status).toBe(200);
	const result = await response.json();
	expect(result.success).toBe(true);
	expect(result.data.collections).toBeDefined();
});

3. CRUD Operations

// Create entry
it('should create a new entry', async () => {
	const response = await fetch(`${API_BASE_URL}/api/collections/${collectionId}`, {
		method: 'POST',
		headers: {
			'Content-Type': 'application/json',
			Authorization: `Bearer ${authToken}`
		},
		body: JSON.stringify({ title: 'Test Post', content: 'Test content' })
	});
	expect(response.status).toBe(200);
	expect(result.success).toBe(true);
});

// Get entries with pagination
it('should get collection entries with pagination', async () => {
	const response = await fetch(`${API_BASE_URL}/api/collections/${collectionId}?page=1&pageSize=10`, {
		headers: { Authorization: `Bearer ${authToken}` }
	});
	expect(response.status).toBe(200);
});

4. Search Functionality

it('should search content across collections', async () => {
	const response = await fetch(`${API_BASE_URL}/api/search`, {
		method: 'POST',
		headers: {
			'Content-Type': 'application/json',
			Authorization: `Bearer ${authToken}`
		},
		body: JSON.stringify({ query: 'test', collections: [] })
	});
	expect(response.status).toBe(200);
	expect(result.success).toBe(true);
});

5. Error Handling

it('should return 404 for invalid collection ID', async () => {
	const response = await fetch(`${API_BASE_URL}/api/collections/invalid-collection-id`, {
		method: 'POST',
		headers: {
			'Content-Type': 'application/json',
			Authorization: `Bearer ${authToken}`
		},
		body: JSON.stringify({ title: 'Test Post' })
	});
	expect(response.status).toBe(404);
});

Manual Testing with cURL

List Collections

curl -X GET "http://localhost:5173/api/collections" \
  -H "Authorization: Bearer YOUR_TOKEN"

Create Entry

curl -X POST "http://localhost:5173/api/collections/23d772dd3783492a9115ee9ea6bc6185" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Test Post",
    "content": "This is test content",
    "status": "published"
  }'

Get Collection Entries

curl -X GET "http://localhost:5173/api/collections/23d772dd3783492a9115ee9ea6bc6185?page=1&pageSize=10" \
  -H "Authorization: Bearer YOUR_TOKEN"

Search Content

curl -X POST "http://localhost:5173/api/search" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "test",
    "collections": []
  }'

Recompile Collections

curl -X POST "http://localhost:5173/api/content-structure" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"action": "recompile"}'

Test Data Setup

The test suite uses the following setup pattern:

beforeEach(async () => {
	// Clean database to ensure test isolation
	await cleanupTestDatabase();

	// Create admin user and get authentication token
	authToken = await loginAsAdminAndGetToken();
});

Integration Testing

For testing collection operations in your own code:

import { getData, postData, patchData, deleteData } from '@utils/apiClient';

// Test creating an entry
const newEntry = await postData({
	endpoint: `/api/collections/${collectionId}`,
	data: { title: 'Test', content: 'Content' }
});

// Test retrieving entries
const entries = await getData({
	endpoint: `/api/collections/${collectionId}`,
	params: { page: 1, pageSize: 10 }
});

// Test updating an entry
const updated = await patchData({
	endpoint: `/api/collections/${collectionId}/${entryId}`,
	data: { title: 'Updated Title' }
});

// Test deleting an entry
await deleteData({
	endpoint: `/api/collections/${collectionId}/${entryId}`
});

Test Coverage Summary

Endpoint Method Coverage
/api/collections GET ✅ Tested
/api/collections/{id} GET ✅ Tested
/api/collections/{id} POST ✅ Tested
/api/collections/{id}/{entryId} GET ⚠️ Partial
/api/collections/{id}/{entryId} PATCH ⚠️ Partial
/api/collections/{id}/{entryId} DELETE ⚠️ Partial
/api/collections/{id}/batch POST ❌ Needs tests
/api/collections/{id}/export GET ⚠️ Partial
/api/collections/{id}/import POST ❌ Needs tests
/api/search POST ✅ Tested
/api/content-structure GET ✅ Tested
/api/content-structure POST ✅ Tested
/api/exportData GET ✅ Tested

Legend:

  • ✅ Tested: Comprehensive test coverage
  • ⚠️ Partial: Basic tests exist, needs expansion
  • ❌ Needs tests: No test coverage yet

Best Practices

Performance

  1. Use pagination: Always specify reasonable pageSize limits
  2. Optimize filters: Use indexed fields in filter conditions
  3. Cache responses: Implement client-side caching for repeated queries
  4. Batch operations: Use batch endpoints for multiple operations

Security

  1. Validate input: Always validate and sanitize user input
  2. Permission checks: Verify user permissions before operations
  3. Rate limiting: Implement rate limiting for public APIs
  4. HTTPS only: Use secure connections in production

Data Integrity

  1. Atomic operations: Use transactions for related operations
  2. Validation: Implement comprehensive data validation
  3. Backup strategy: Regular backups of critical data
  4. Audit trails: Enable revision tracking for important collections

This API provides a robust foundation for content management with features including performance optimization, security, and scalability.