Base URL: https://blog-cms-api-iekn.onrender.com
Getting Started
This API powers a blog CMS with posts, categories, comments, and JWT-based authentication. All protected endpoints require a Bearer token in the Authorization header.
Authentication Flow
POST /api/auth/register or POST /api/auth/login to get a JWT token
- Include the token in subsequent requests:
Authorization: Bearer <token>
- Use
GET /api/auth/me to verify the current session
Authentication
post /api/auth/register
Create a new user account and receive a JWT token.
Request body:
usernamestringrequired
emailstringrequired
passwordstringrequired
displayNamestringrequired
{
"username": "ahmad",
"email": "ahmad@test.com",
"password": "test123",
"displayName": "Ahmad Fahmi"
}
Response 200:
{
"token": "eyJhbGciOiJIUzI1NiIs...",
"expiresAt": "2026-07-18T14:05:41Z",
"user": {
"id": 1,
"username": "ahmad",
"email": "ahmad@test.com",
"displayName": "Ahmad Fahmi"
}
}
Errors: 409 if username already exists
post /api/auth/login
Log in with existing credentials and receive a JWT token.
usernamestringrequired
passwordstringrequired
{
"username": "ahmad",
"password": "test123"
}
Response 200:
{
"token": "eyJhbGciOiJIUzI1NiIs...",
"expiresAt": "2026-07-18T14:05:41Z",
"user": {
"id": 1,
"username": "ahmad",
"email": "ahmad@test.com",
"displayName": "Ahmad Fahmi"
}
}
Errors: 401 if credentials are invalid
get auth /api/auth/me
Get the currently authenticated user's info.
Response 200:
{
"userId": "1",
"username": "ahmad"
}
Posts
get /api/posts?published=true
List all posts. Use ?published=true (default) to show only published posts, or ?published=false to include drafts.
Response 200:
[
{
"id": 9,
"title": "Hello World",
"slug": "hello-world",
"excerpt": "A short excerpt",
"isPublished": true,
"createdAt": "2026-07-17T14:05:59Z",
"publishedAt": "2026-07-17T14:05:59Z",
"author": { "id": 1, "username": "ahmad", "displayName": "Ahmad Fahmi" },
"category": { "id": 2, "name": "Test Category", "slug": "test-category" },
"tags": [],
"commentCount": 0
}
]
get /api/posts/{slug}
Get a single post by its slug.
Response 200:
{
"id": 9,
"title": "Hello World",
"slug": "hello-world",
"content": "First blog post via API",
"excerpt": "A short excerpt",
"isPublished": true,
"author": { "username": "ahmad", "displayName": "Ahmad Fahmi" },
"category": { "name": "Test Category" },
"tags": [],
"comments": [],
"commentCount": 0
}
Errors: 404 if slug not found
post auth /api/posts
Create a new post. Requires auth.
titlestringrequired
contentstringrequired
excerptstringoptional
featuredImageUrlstringoptional
isPublishedbooleanrequired
categoryIdintrequired
tagIdsint[]required
{
"title": "Hello World",
"content": "This is my first post.",
"excerpt": "A short excerpt",
"isPublished": true,
"categoryId": 2,
"tagIds": []
}
Response 201:
// Returns the full post object (same as GET /{slug})
Errors: 409 if title already exists, 404 if category not found
put auth /api/posts/{id}
Update an existing post by ID. Requires auth.
Request body (same as POST):
{
"title": "Updated Title",
"content": "Updated content.",
"isPublished": true,
"categoryId": 2,
"tagIds": []
}
Errors: 404 if post or category not found
delete auth /api/posts/{id}
Delete a post by ID. Requires auth.
Response 204 on success. Errors: 404 if not found.
Categories
get /api/categories
List all categories.
Response 200:
[
{
"id": 2,
"name": "Test Category",
"slug": "test-category"
}
]
post auth /api/categories
Create a new category. Requires auth.
namestringrequired
{
"name": "Technology"
}
Response 200:
{
"id": 3,
"name": "Technology",
"slug": "technology"
}
Errors: 409 if category already exists
delete auth /api/categories/{id}
Delete a category by ID. Requires auth.
Response 204 on success. Errors: 404 if not found.
Comments
get /api/posts/{postId}/comments
Get all comments for a post.
Response 200:
[
{
"id": 1,
"content": "Great post!",
"createdAt": "2026-07-17T14:10:00Z",
"author": {
"id": 1,
"username": "ahmad",
"displayName": "Ahmad Fahmi"
}
}
]
post auth /api/posts/{postId}/comments
Add a comment to a post. Requires auth.
contentstringrequired
{
"content": "Great post!"
}
Errors: 404 if post not found
delete auth /api/posts/{postId}/comments/{id}
Delete a comment by ID. Requires auth.
Response 204 on success. Errors: 404 if not found.
Error Responses
All errors return a consistent JSON format:
{
"error": "Category with ID 999 not found."
}
400Bad Requestvalidation errors
401Unauthorizedinvalid credentials
404Not Foundresource doesn't exist
409Conflictduplicate title/name
500Internal Server Errorunexpected error