Comprehensive guide to all Imperial Pedia REST endpoints, request/response formats, and validation rules.
- 🌐 Overview
- 📊 Global Response Format
- 🔐 Authentication
- ⏱️ Rate Limiting
- 📥 Retrieval Endpoints
- ✍️ Creation Endpoint
- 🔄 Update Endpoints
- 🎛️ Status Management
- 🗑️ Deletion Endpoint
- 📦 Data Models
- ✔️ Validation Rules
⚠️ Error Handling- 💡 Examples
- 🗄️ Database Constraints
- 📈 HTTP Status Codes
- 🗂️ Changelog
Imperial Pedia API is a RESTful Spring Boot application for managing a comprehensive database of terms with support for categorization, relationships, and multiple status states.
| Property | Value |
|---|---|
| Base URL | http://localhost:8080/api |
| API Version | 1.0.0-SNAPSHOT |
| Content Type | application/json |
| Authentication | None (Public API) |
| Rate Limiting | Enabled (Bucket4j, configurable) |
All endpoints return responses wrapped in the standard ApiResponse<T> envelope:
{
"success": true,
"statusCode": 200,
"message": "Operation successful",
"data": {
/* response payload - type depends on endpoint */
},
"timestamp": "2026-03-22T11:13:15"
}{
"success": false,
"statusCode": 400,
"message": "Error description",
"path": "/api/terms/letter/a",
"timestamp": "2026-03-22T11:13:15"
}Status: ✅ No Authentication Required
All endpoints are publicly accessible. Authentication will be added in future versions using Spring Security.
Rate limiting is active and configurable via application.properties under rate-limit.*.
- Rules are evaluated from top to bottom (
rate-limit.rules[index]) - First matching rule is applied
- Endpoint groups have independent counters
- Buckets are isolated per client and group (
clientKey:group) - Exceeded limits return
429 Too Many Requests
- Uses first IP from
X-Forwarded-Forwhen present - Falls back to
request.getRemoteAddr()
All endpoint paths below are relative to the API base path
/api.
Get all published terms that start with a specific letter.
GET /terms/letter/{letter}| Name | Type | Required | Description |
|---|---|---|---|
letter |
String | ✅ Yes | Single letter or word (e.g., "A", "Star", "Jedi") |
| Case-insensitive, normalized to UPPERCASE |
{
"success": true,
"statusCode": 200,
"message": "Terms retrieved successfully",
"data": [
{
"title": "anakin skywalker",
"slug": "anakin-skywalker",
"content": "Jedi Knight who fell to the dark side...",
"seoTitle": "Anakin Skywalker - Imperialpedia",
"seoDescription": "Biography of Anakin Skywalker",
"featuredImageUrl": "https://example.com/anakin.jpg",
"categoryNames": ["characters", "jedi"],
"relatedTerms": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"slug": "luke-skywalker",
"title": "luke skywalker",
"featuredImageUrl": "https://example.com/luke.jpg"
}
]
}
],
"timestamp": "2026-03-22T11:13:15"
}| Code | Scenario |
|---|---|
400 |
Letter is null or blank |
404 |
No published terms found for that letter |
Retrieve all archived terms, optionally filtered by letter.
GET /terms/archived[?letter=X]| Name | Type | Required | Description |
|---|---|---|---|
letter |
String | ❌ No | Optional letter filter (e.g., "?letter=A") |
| If blank/missing, returns all archived terms |
Same structure as endpoint 1.1 but with archived terms.
| Code | Scenario |
|---|---|
404 |
No archived terms found |
Retrieve all draft terms, optionally filtered by letter.
GET /terms/draft[?letter=X]| Name | Type | Required | Description |
|---|---|---|---|
letter |
String | ❌ No | Optional letter filter (e.g., "?letter=D") |
Same structure as endpoint 1.1 but with draft terms.
| Code | Scenario |
|---|---|
404 |
No draft terms found |
Retrieve a published term by its unique slug.
GET /terms/slug/{slug}| Name | Type | Required | Description |
|---|---|---|---|
slug |
String | ✅ Yes | Unique slug (lowercase, hyphen-separated) |
| Case-insensitive; normalized to lowercase |
Full TermDetailResponse with categories and related terms.
| Code | Scenario |
|---|---|
400 |
Slug is null or blank |
404 |
Term not found with this slug |
Retrieve a published term by its unique UUID.
GET /terms/{id}| Name | Type | Required | Description |
|---|---|---|---|
id |
UUID | ✅ Yes | Term UUID (e.g., "550e8400-e29b-41d4-a716-446655440000") |
Full TermDetailResponse with categories and related terms.
| Code | Scenario |
|---|---|
400 |
Invalid UUID format |
404 |
Term not found with this ID |
Create a new term with complete metadata, categories, and related terms.
POST /terms/create
Content-Type: application/json{
"title": "Darth Vader",
"slug": "darth-vader",
"content": "Anakin Skywalker turned to the dark side...",
"seoTitle": "Darth Vader - Dark Lord of the Sith",
"seoDescription": "Learn about Darth Vader, the Sith Lord",
"featuredImageUrl": "https://example.com/darth-vader.jpg",
"status": "DRAFT",
"categoryNames": ["Characters", "Sith", "Villains"],
"relatedTerms": ["anakin-skywalker", "luke-skywalker"],
"relatedTermIds": ["uuid1", "uuid2"]
}| Field | Type | Required | Constraints |
|---|---|---|---|
title |
String | ✅ | Max 255 chars, cannot be blank |
slug |
String | ✅ | Max 255 chars, unique, lowercase hyphen-separated |
content |
String | ✅ | Max 255 chars, cannot be blank |
seoTitle |
String | ❌ | Max 255 chars, optional |
seoDescription |
String | ❌ | Max 500 chars, optional |
featuredImageUrl |
String | ❌ | Max 500 chars, URL format |
status |
Enum | ❌ | DRAFT, PUBLISHED, ARCHIVED (default: DRAFT) |
categoryNames |
String[] | ❌ | Max 20 items, auto-created if missing |
relatedTerms |
String[] | ❌ | Max 20 slugs, must exist in DB |
relatedTermIds |
UUID[] | ❌ | Max 20 UUIDs, must exist in DB |
{
"success": true,
"statusCode": 201,
"message": "Term created successfully",
"data": {
"title": "darth vader",
"slug": "darth-vader",
"content": "anakin skywalker turned to the dark side...",
"seoTitle": "darth vader - dark lord of the sith",
"seoDescription": "learn about darth vader, the sith lord",
"featuredImageUrl": "https://example.com/darth-vader.jpg",
"categoryNames": ["characters", "sith", "villains"],
"relatedTerms": [
{
"id": "uuid",
"slug": "anakin-skywalker",
"title": "anakin skywalker",
"featuredImageUrl": "https://..."
}
]
},
"timestamp": "2026-03-22T11:13:15"
}| Code | Scenario |
|---|---|
400 |
Validation failed (blank fields, invalid format) |
409 |
Slug already exists |
404 |
Related term slug/ID not found |
Completely replace a term with new data (all required fields must be provided).
PUT /terms/update/{id}
Content-Type: application/json| Name | Type | Required | Description |
|---|---|---|---|
id |
UUID | ✅ Yes | UUID of the term to update |
Same as Create endpoint (all required fields mandatory).
Full TermDetailResponse after update.
| Code | Scenario |
|---|---|
404 |
Term not found |
409 |
Slug already exists (for different term) |
400 |
Validation failed |
Update only the fields provided (optional fields can be omitted).
PATCH /terms/update/{id}
Content-Type: application/json| Name | Type | Required | Description |
|---|---|---|---|
id |
UUID | ✅ Yes | UUID of the term to patch |
{
"title": "Updated Title",
"slug": "updated-slug",
"content": "Updated content",
"seoTitle": "Updated SEO Title",
"seoDescription": "Updated SEO Description",
"featuredImageUrl": "https://new-image-url.jpg",
"status": "PUBLISHED",
"categoryNames": ["NewCategory"],
"relatedTerms": ["slug1"],
"relatedTermIds": ["uuid1"]
}| Field | Type | Effect |
|---|---|---|
title |
String | Updates term name |
slug |
String | Updates unique identifier |
content |
String | Updates description |
seoTitle |
String | Updates SEO title |
seoDescription |
String | Updates SEO description |
featuredImageUrl |
String | Updates image URL |
status |
Enum | Updates status |
categoryNames |
String[] | Replaces all categories |
relatedTerms |
String[] | Replaces all related terms |
relatedTermIds |
UUID[] | Replaces all related terms |
Full TermDetailResponse after patch.
| Code | Scenario |
|---|---|
404 |
Term not found |
400 |
Invalid field names or values |
409 |
Slug already exists |
Change term status from DRAFT to PUBLISHED (makes it visible in public queries).
PUT /terms/publish/{id}| Name | Type | Required | Description |
|---|---|---|---|
id |
UUID | ✅ Yes | UUID of the term to publish |
Full TermDetailResponse with status = "PUBLISHED".
| Code | Scenario |
|---|---|
404 |
Term not found |
Change term status to DRAFT (removes from published view).
PUT /terms/draft/{id}| Name | Type | Required | Description |
|---|---|---|---|
id |
UUID | ✅ Yes | UUID of the term |
Full TermDetailResponse with status = "DRAFT".
| Code | Scenario |
|---|---|
404 |
Term not found |
Change term status to ARCHIVED (preserves data but hides from public view).
PUT /terms/archive/{id}| Name | Type | Required | Description |
|---|---|---|---|
id |
UUID | ✅ Yes | UUID of the term |
Full TermDetailResponse with status = "ARCHIVED".
| Code | Scenario |
|---|---|
404 |
Term not found |
Permanently delete a term and all its associations from the database.
DELETE /terms/delete/{id}| Name | Type | Required | Description |
|---|---|---|---|
id |
UUID | ✅ Yes | UUID of the term to delete |
{
"success": true,
"statusCode": 204,
"message": "Term deleted successfully",
"timestamp": "2026-03-22T11:13:15"
}| Code | Scenario |
|---|---|
404 |
Term not found |
400 |
Invalid UUID |
Complete term information with relationships and categories.
{
"title": "string",
"slug": "string",
"content": "string",
"seoTitle": "string | null",
"seoDescription": "string | null",
"featuredImageUrl": "string | null",
"categoryNames": ["string"],
"relatedTerms": [
{
"id": "uuid",
"slug": "string",
"title": "string",
"featuredImageUrl": "string | null"
}
]
}| Field | Type | Description |
|---|---|---|
title |
String | Term name (lowercase) |
slug |
String | Unique identifier (lowercase, hyphen-separated) |
content |
String | Term description (lowercase) |
seoTitle |
String|null | SEO title tag |
seoDescription |
String|null | SEO meta description |
featuredImageUrl |
String|null | Featured image URL |
categoryNames |
String[] | Associated category names |
relatedTerms |
RelatedTermResponse[] | Related term references |
Summary information of a related term.
{
"id": "uuid",
"slug": "string",
"title": "string",
"featuredImageUrl": "string | null"
}| Field | Type | Description |
|---|---|---|
id |
UUID | Term identifier |
slug |
String | Term slug (lowercase, hyphen-separated) |
title |
String | Term name (lowercase) |
featuredImageUrl |
String|null | Featured image URL |
- Required: ✅ Yes
- Type: String
- Max Length: 255 characters
- Constraints: Cannot be blank
- Normalization: Trimmed, lowercase
- Required: ✅ Yes
- Type: String
- Max Length: 255 characters
- Constraints: Must be unique, lowercase, hyphen-separated alphanumerics only
- Pattern:
^[a-z0-9]+(?:-[a-z0-9]+)*$ - Normalization: Trimmed, lowercase
- Required: ✅ Yes
- Type: String
- Max Length: 255 characters
- Constraints: Cannot be blank
- Normalization: Trimmed, lowercase
- Required: ❌ No
- Type: String
- Constraints: seoTitle max 255 chars, seoDescription max 500 chars
- Default: null
- Normalization: Trimmed, lowercase
- Required: ❌ No
- Type: String (URL)
- Max Length: 500 characters
- Default: null
- Normalization: Trimmed, lowercase
- Required: ❌ No (default: DRAFT)
- Type: Enum
- Values:
DRAFT,PUBLISHED,ARCHIVED - Input: Case-insensitive (e.g., "draft", "DRAFT", "Draft" all accepted)
- Required: ❌ No
- Type: String[]
- Max Items: 20 per term
- Constraints: Auto-created if don't exist, case-insensitive deduplication
- Normalization: Trimmed, lowercase, space-collapsed
- Required: ❌ No
- Type: String[] (slugs) or UUID[]
- Max Items: 20 per term
- Constraints: Must exist in database, no self-references
- Normalization: Deduplicated, normalized
{
"success": false,
"statusCode": 400,
"message": "Descriptive error message",
"path": "/api/terms/create",
"timestamp": "2026-03-22T11:13:15"
}| Code | HTTP Status | Type | Scenario |
|---|---|---|---|
| 400 | Bad Request | Validation | Invalid input, blank required field, invalid format |
| 404 | Not Found | Resource | Term/slug/category doesn't exist |
| 409 | Conflict | Integrity | Slug already exists, duplicate entry |
| 429 | Too Many Requests | Throttling | Request quota exceeded for matched endpoint group |
| 500 | Server Error | System | Unexpected server error |
curl -X POST http://localhost:8080/api/terms/create \
-H "Content-Type: application/json" \
-d '{
"title": "Luke Skywalker",
"slug": "luke-skywalker",
"content": "Jedi Knight, son of Anakin Skywalker",
"seoTitle": "Luke Skywalker | Imperialpedia",
"categoryNames": ["Characters", "Jedi"],
"status": "PUBLISHED"
}'{
"success": true,
"statusCode": 201,
"message": "Term created successfully",
"data": {
"title": "luke skywalker",
"slug": "luke-skywalker",
"content": "jedi knight, son of anakin skywalker",
"seoTitle": "luke skywalker | imperialpedia",
"seoDescription": null,
"featuredImageUrl": null,
"categoryNames": ["characters", "jedi"],
"relatedTerms": []
},
"timestamp": "2026-03-22T11:13:15"
}curl -X PATCH http://localhost:8080/api/terms/update/550e8400-e29b-41d4-a716-446655440000 \
-H "Content-Type: application/json" \
-d '{
"title": "Luke Skywalker - Jedi Master"
}'{
"success": true,
"statusCode": 200,
"message": "Term patched successfully",
"data": {
"title": "luke skywalker - jedi master",
"slug": "luke-skywalker",
"content": "jedi knight, son of anakin skywalker",
"categoryNames": ["characters", "jedi"],
"relatedTerms": []
},
"timestamp": "2026-03-22T11:13:15"
}curl http://localhost:8080/api/terms/letter/l{
"success": true,
"statusCode": 200,
"message": "Terms retrieved successfully",
"data": [
{
"title": "luke skywalker",
"slug": "luke-skywalker",
"content": "jedi knight, son of anakin skywalker",
"categoryNames": ["characters", "jedi"],
"relatedTerms": []
},
{
"title": "leia organa",
"slug": "leia-organa",
"content": "princess, rebel leader, luke's sister",
"categoryNames": ["characters", "rebellion"],
"relatedTerms": []
}
],
"timestamp": "2026-03-22T11:13:15"
}- Slug: Only one term per slug (case-insensitive comparison)
- Category Name: Only one category per name (case-insensitive comparison)
- Related Terms: Cannot have self-references (term cannot reference itself)
- EntityGraph: Related terms and categories are eagerly loaded for detail endpoints
- Performance: Optimized for single-record retrieval with full relationship data
- Lazy Loading: List endpoints use basic projection for efficiency
| HTTP Code | ApiResponse statusCode | Meaning | When Used |
|---|---|---|---|
| 200 | 200 | OK - Request successful | GET, PUT, PATCH success |
| 201 | 201 | Created - Resource created | POST new term success |
| 204 | 204 | No Content | DELETE success |
| 400 | 400 | Bad Request | Input validation failed |
| 404 | 404 | Not Found | Resource doesn't exist |
| 409 | 409 | Conflict | Duplicate slug/integrity violation |
| 429 | 429 | Too Many Requests | Endpoint-specific rate limit exceeded |
| 500 | 500 | Server Error | Unexpected server error |
Date: March 22, 2026
Features:
- ✅ Initial API implementation
- ✅ Full CRUD operations for terms
- ✅ Status management (DRAFT, PUBLISHED, ARCHIVED)
- ✅ Category association and auto-creation
- ✅ Related terms linking (by slug or UUID)
- ✅ Comprehensive input validation
- ✅ Automatic data normalization
- ✅ Consistent response format with ApiResponse wrapper
- ✅ Utility classes for input parsing
- ✅ Complete error handling
Known Limitations:
- ❌ No authentication (will be added in v2.0)
- ❌ No pagination (returns all results)
- ❌ No caching layer
📖 Back to README | 📋 Project Summary
Last Updated: March 23, 2026
API Version: 1.0.0-SNAPSHOT
Status: ✅ Production Ready