Skip to content

Commit 9bed65c

Browse files
committed
feat: Add inspirecms-api package for headless CMS REST API
This package transforms InspireCMS into a headless CMS by providing: - DocumentType-driven API endpoints (auto-generated from content types) - Field exposure configuration (choose which fields appear in API) - Flexible authentication (API tokens with abilities and expiration) - Public/private access control per content type - Query parameters: filtering, sorting, pagination, includes - Schema discovery endpoints for API documentation - Rate limiting for API protection - Filament integration for managing API settings and tokens Key components: - ContentTypeController: Dynamic CRUD for all API-enabled types - SchemaController: API discovery and documentation - TokenController: API token authentication - ApiSettingsForm: Filament form components for configuration - ApiTokenResource: Filament resource for token management Endpoints available when configured: - GET /api/v1/{type} - List content - GET /api/v1/{type}/{id} - Get by ID - GET /api/v1/{type}/slug/{s} - Get by slug - POST /api/v1/{type} - Create content - PUT /api/v1/{type}/{id} - Update content - DELETE /api/v1/{type}/{id} - Delete content - GET /api/v1/schema - API schema discovery - POST /api/v1/auth/token - Create API token
1 parent 5981f77 commit 9bed65c

29 files changed

+3265
-0
lines changed

packages/inspirecms-api/README.md

Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
1+
# InspireCMS Headless API
2+
3+
A REST API package for InspireCMS that transforms it into a headless CMS. Automatically generate API endpoints based on your DocumentTypes.
4+
5+
## Features
6+
7+
- **DocumentType-Driven API**: Automatically generate REST endpoints based on your content types
8+
- **Field Exposure Control**: Configure which fields are visible in API responses
9+
- **Flexible Authentication**: API tokens with abilities and expiration
10+
- **Public/Private Access**: Configure read/write access per content type
11+
- **Query Parameters**: Filtering, sorting, pagination, and field selection
12+
- **Schema Discovery**: Auto-generated API documentation
13+
- **Rate Limiting**: Built-in protection against API abuse
14+
15+
## Installation
16+
17+
1. Add the package to your composer.json:
18+
19+
```json
20+
{
21+
"repositories": [
22+
{
23+
"type": "path",
24+
"url": "packages/inspirecms-api"
25+
}
26+
],
27+
"require": {
28+
"solution-forest/inspirecms-api": "*"
29+
}
30+
}
31+
```
32+
33+
2. Run composer update:
34+
35+
```bash
36+
composer update
37+
```
38+
39+
3. Publish the configuration:
40+
41+
```bash
42+
php artisan vendor:publish --tag=inspirecms-api-config
43+
```
44+
45+
4. Run migrations:
46+
47+
```bash
48+
php artisan migrate
49+
```
50+
51+
## Configuration
52+
53+
### Enable API for a DocumentType
54+
55+
1. Go to **Settings > Document Types** in the CMS admin
56+
2. Edit a document type
57+
3. Navigate to the **API** tab
58+
4. Enable the API and configure settings
59+
60+
Or programmatically:
61+
62+
```php
63+
$documentType->update([
64+
'api_settings' => [
65+
'enabled' => true,
66+
'slug' => 'blog-posts',
67+
'public_read' => true,
68+
'public_write' => false,
69+
'allowed_operations' => ['index', 'show'],
70+
]
71+
]);
72+
```
73+
74+
### Configure Field Exposure
75+
76+
Each field can be configured to:
77+
- Be exposed or hidden in API responses
78+
- Be readable (included in GET responses)
79+
- Be writable (accepted in POST/PUT requests)
80+
- Have a custom alias name in the API
81+
82+
## API Endpoints
83+
84+
Once a DocumentType has API enabled, the following endpoints become available:
85+
86+
### Content Endpoints
87+
88+
| Method | Endpoint | Description |
89+
|--------|----------|-------------|
90+
| GET | `/api/v1/{type}` | List all items |
91+
| GET | `/api/v1/{type}/{id}` | Get item by ID |
92+
| GET | `/api/v1/{type}/slug/{slug}` | Get item by slug |
93+
| POST | `/api/v1/{type}` | Create new item |
94+
| PUT | `/api/v1/{type}/{id}` | Update item |
95+
| DELETE | `/api/v1/{type}/{id}` | Delete item |
96+
97+
### Authentication Endpoints
98+
99+
| Method | Endpoint | Description |
100+
|--------|----------|-------------|
101+
| POST | `/api/v1/auth/token` | Create API token |
102+
| DELETE | `/api/v1/auth/token` | Revoke current token |
103+
| GET | `/api/v1/auth/tokens` | List user's tokens |
104+
105+
### Schema Endpoints
106+
107+
| Method | Endpoint | Description |
108+
|--------|----------|-------------|
109+
| GET | `/api/v1/schema` | Get full API schema |
110+
| GET | `/api/v1/schema/{type}` | Get schema for type |
111+
112+
## Query Parameters
113+
114+
### Filtering
115+
116+
```
117+
GET /api/v1/blog-posts?filter[status]=published
118+
GET /api/v1/blog-posts?filter[created_at][gte]=2024-01-01
119+
GET /api/v1/blog-posts?filter[author_id][in]=1,2,3
120+
```
121+
122+
Supported operators:
123+
- `eq` - Equal (default)
124+
- `neq` - Not equal
125+
- `gt` / `gte` - Greater than (or equal)
126+
- `lt` / `lte` - Less than (or equal)
127+
- `like` - LIKE search
128+
- `in` / `not_in` - IN / NOT IN
129+
- `null` / `not_null` - NULL checks
130+
131+
### Sorting
132+
133+
```
134+
GET /api/v1/blog-posts?sort=title # Ascending
135+
GET /api/v1/blog-posts?sort=-created_at # Descending
136+
GET /api/v1/blog-posts?sort=-created_at,title # Multiple
137+
```
138+
139+
### Pagination
140+
141+
```
142+
GET /api/v1/blog-posts?page=2&per_page=20
143+
```
144+
145+
### Including Relations
146+
147+
```
148+
GET /api/v1/blog-posts?include=author,children
149+
```
150+
151+
### Locale
152+
153+
```
154+
GET /api/v1/blog-posts?locale=en
155+
```
156+
157+
### Search
158+
159+
```
160+
GET /api/v1/blog-posts?search=keyword
161+
```
162+
163+
## Authentication
164+
165+
### Creating a Token
166+
167+
```bash
168+
curl -X POST https://yoursite.com/api/v1/auth/token \
169+
-H "Content-Type: application/json" \
170+
-d '{
171+
"email": "user@example.com",
172+
"password": "your-password",
173+
"name": "My App Token"
174+
}'
175+
```
176+
177+
Response:
178+
```json
179+
{
180+
"message": "Token created successfully.",
181+
"data": {
182+
"token": "abc123...",
183+
"type": "Bearer",
184+
"expires_at": "2024-02-15T00:00:00Z"
185+
}
186+
}
187+
```
188+
189+
### Using the Token
190+
191+
```bash
192+
# Bearer token
193+
curl -H "Authorization: Bearer abc123..." https://yoursite.com/api/v1/blog-posts
194+
195+
# API Key header
196+
curl -H "X-API-Key: abc123..." https://yoursite.com/api/v1/blog-posts
197+
```
198+
199+
## Response Format
200+
201+
### List Response
202+
203+
```json
204+
{
205+
"data": [
206+
{
207+
"id": "uuid-1",
208+
"type": "blog-posts",
209+
"attributes": {
210+
"title": "My Blog Post",
211+
"slug": "my-blog-post",
212+
"status": "published",
213+
"locale": "en",
214+
"content": "<p>...</p>",
215+
"featured_image": {
216+
"id": 42,
217+
"url": "/storage/media/image.jpg"
218+
}
219+
},
220+
"meta": {
221+
"created_at": "2024-01-15T10:00:00Z",
222+
"updated_at": "2024-01-15T12:00:00Z",
223+
"published_at": "2024-01-15T10:00:00Z"
224+
},
225+
"links": {
226+
"self": "/api/v1/blog-posts/uuid-1"
227+
}
228+
}
229+
],
230+
"meta": {
231+
"current_page": 1,
232+
"last_page": 5,
233+
"per_page": 15,
234+
"total": 72
235+
},
236+
"links": {
237+
"first": "/api/v1/blog-posts?page=1",
238+
"last": "/api/v1/blog-posts?page=5",
239+
"next": "/api/v1/blog-posts?page=2"
240+
}
241+
}
242+
```
243+
244+
## Environment Variables
245+
246+
```env
247+
INSPIRECMS_API_ENABLED=true
248+
INSPIRECMS_API_PREFIX=api
249+
INSPIRECMS_API_VERSION=v1
250+
INSPIRECMS_API_RATE_LIMIT_ENABLED=true
251+
INSPIRECMS_API_RATE_LIMIT_PUBLIC=60
252+
INSPIRECMS_API_RATE_LIMIT_AUTH=300
253+
INSPIRECMS_API_CACHE_ENABLED=true
254+
INSPIRECMS_API_CACHE_TTL=300
255+
```
256+
257+
## Extending the API
258+
259+
### Custom Field Transformers
260+
261+
You can register custom transformers for field types:
262+
263+
```php
264+
use SolutionForest\InspireCmsApi\Services\FieldTransformerService;
265+
266+
$transformer = app(FieldTransformerService::class);
267+
// Add custom transformation logic
268+
```
269+
270+
### Adding Middleware
271+
272+
```php
273+
// In your service provider
274+
Route::middleware(['your-middleware'])->group(function () {
275+
// Custom API routes
276+
});
277+
```
278+
279+
## License
280+
281+
MIT License - see LICENSE file for details.
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
{
2+
"name": "solution-forest/inspirecms-api",
3+
"description": "Headless CMS REST API package for InspireCMS - Expose your content via RESTful endpoints",
4+
"keywords": [
5+
"solution-forest",
6+
"laravel",
7+
"inspirecms",
8+
"headless-cms",
9+
"rest-api",
10+
"api"
11+
],
12+
"homepage": "https://github.com/solutionforest/inspirecms-api",
13+
"license": "MIT",
14+
"authors": [
15+
{
16+
"name": "Solution Forest",
17+
"email": "info@solutionforest.net",
18+
"role": "Developer"
19+
}
20+
],
21+
"require": {
22+
"php": ">=8.2",
23+
"solution-forest/inspirecms-core": "^4.0",
24+
"spatie/laravel-package-tools": "^1.15.0"
25+
},
26+
"require-dev": {
27+
"laravel/pint": "^1.0",
28+
"orchestra/testbench": "^9.0|^10.0",
29+
"pestphp/pest": "^3.0"
30+
},
31+
"autoload": {
32+
"psr-4": {
33+
"SolutionForest\\InspireCmsApi\\": "src/"
34+
}
35+
},
36+
"autoload-dev": {
37+
"psr-4": {
38+
"SolutionForest\\InspireCmsApi\\Tests\\": "tests/"
39+
}
40+
},
41+
"scripts": {
42+
"lint": "pint",
43+
"test": "pest"
44+
},
45+
"config": {
46+
"sort-packages": true,
47+
"allow-plugins": {
48+
"pestphp/pest-plugin": true
49+
}
50+
},
51+
"extra": {
52+
"laravel": {
53+
"providers": [
54+
"SolutionForest\\InspireCmsApi\\InspireCmsApiServiceProvider"
55+
],
56+
"aliases": {
57+
"InspireCmsApi": "SolutionForest\\InspireCmsApi\\Facades\\InspireCmsApi"
58+
}
59+
}
60+
},
61+
"minimum-stability": "stable",
62+
"prefer-stable": true
63+
}

0 commit comments

Comments
 (0)