API Documentation

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

  1. POST /api/auth/register or POST /api/auth/login to get a JWT token
  2. Include the token in subsequent requests: Authorization: Bearer <token>
  3. 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