Programmed by NYAGANYA
Blog REST API is a Spring Boot backend project built to manage authentication, users, and blog content through secure REST endpoints. The application uses JWT-based authentication, role-based authorization, PostgreSQL persistence, and Swagger/OpenAPI documentation to provide a complete backend foundation for a blog platform.
The project is structured around three main responsibilities:
- authentication and token issuance
- user access and role management
- blog creation, reading, updating, and deletion
The main aim of this project is to provide a secure, maintainable, and production-oriented backend for a blogging system where:
- users can register and log in
- authenticated users can create and manage blog posts
- administrators can manage user roles
- access to protected resources is enforced through JWT and role rules
- API behavior is clearly documented and easy to test
Many simple blog systems begin with open endpoints, weak authentication, and no clear authorization model. That causes several real problems:
- any client can attempt unauthorized access to protected resources
- users cannot be cleanly separated into normal users and administrators
- blog ownership rules are often missing or inconsistent
- manual endpoint testing becomes difficult without API documentation
- backend growth becomes hard to maintain without a clear service and controller structure
This project addresses those backend design problems directly by organizing responsibilities into controllers, services, repositories, DTOs, entities, and security components.
This application solves the above challenges through a layered backend architecture:
AuthControllerandAuthServicehandle registration and loginJwtServicecreates and validates JWT tokensJwtAuthenticationFilterchecks bearer tokens for incoming protected requestsSecurityConfigdefines public and protected routesUserControllerandUserServiceexpose current-user and role-management endpointsBlogControllerandBlogServicemanage blog operations- ownership and admin checks prevent unauthorized updates and deletions
- exception handlers return structured error responses
- Swagger/OpenAPI makes the API easier to inspect and test
- User registration with duplicate email protection
- User login with JWT token generation
- Stateless authentication with bearer tokens
- Protected blog endpoints
- Role-based access for
ROLE_USERandROLE_ADMIN - Admin-only user listing and role updates
- Blog ownership checks for update and delete operations
- Centralized exception handling
- OpenAPI and Swagger UI integration
- Java 25
- Spring Boot 4.0.5
- Spring Web MVC
- Spring Security
- Spring Data JPA
- Hibernate ORM
- JWT using
jjwt - BCrypt password hashing
- Method-level authorization with
@PreAuthorize
- PostgreSQL
- SpringDoc OpenAPI
- Swagger UI
- Maven Wrapper
- JUnit
src/main/java/com/tervux/blog_restapi
├── config
│ ├── OpenApiConfig.java
│ └── SecurityConfig.java
├── controller
│ ├── AuthController.java
│ ├── BlogController.java
│ └── UserController.java
├── dto
│ ├── AuthResponse.java
│ ├── BlogRequestDto.java
│ ├── BlogResponseDto.java
│ ├── LoginRequest.java
│ ├── RegisterRequest.java
│ ├── UpdateRoleRequest.java
│ └── UserResponseDto.java
├── entity
│ ├── Blog.java
│ ├── Role.java
│ └── User.java
├── exception
│ ├── AccessDeniedException.java
│ ├── EmailAlreadyExistsException.java
│ ├── ErrorResponse.java
│ ├── GlobalExceptionHandler.java
│ └── ResourceNotFoundException.java
├── repository
│ ├── BlogRepository.java
│ └── UserRepository.java
├── security
│ ├── CustomAccessDeniedHandler.java
│ ├── CustomAuthenticationEntryPoint.java
│ ├── CustomUserDetailsService.java
│ ├── JwtAuthenticationFilter.java
│ └── JwtService.java
├── service
│ ├── AuthService.java
│ ├── AuthServiceImpl.java
│ ├── BlogService.java
│ ├── BlogServiceImpl.java
│ ├── UserService.java
│ └── UserServiceImpl.java
└── BlogRestapiApplication.java
The application follows a clear backend request path:
- A client sends an HTTP request.
- The controller receives the request and validates input DTOs.
- The service layer applies business rules.
- The repository layer interacts with PostgreSQL.
- Security filters validate JWT before protected endpoints are reached.
- A DTO or structured error response is returned to the client.
flowchart LR
A[Client] --> B[HTTP Request]
B --> C[Spring Security Filter Chain]
C --> D[JwtAuthenticationFilter]
D --> E[Controller Layer]
E --> F[Service Layer]
F --> G[Repository Layer]
G --> H[(PostgreSQL)]
H --> G
G --> F
F --> E
E --> I[DTO Response or Error Response]
I --> A
sequenceDiagram
participant Client
participant AuthController
participant AuthService
participant UserRepository
participant PasswordEncoder
participant AuthenticationManager
participant JwtService
Client->>AuthController: POST /api/auth/register
AuthController->>AuthService: register(request)
AuthService->>UserRepository: existsByEmail(email)
AuthService->>PasswordEncoder: encode(password)
AuthService->>UserRepository: save(user)
AuthService->>JwtService: generateToken(savedUser)
JwtService-->>AuthService: JWT
AuthService-->>AuthController: AuthResponse
AuthController-->>Client: 201 Created
Client->>AuthController: POST /api/auth/login
AuthController->>AuthService: login(request)
AuthService->>AuthenticationManager: authenticate(email, password)
AuthService->>UserRepository: findByEmail(email)
AuthService->>JwtService: generateToken(user)
JwtService-->>AuthService: JWT
AuthService-->>AuthController: AuthResponse
AuthController-->>Client: 200 OK
flowchart TD
A[Client sends request with Bearer token] --> B[JwtAuthenticationFilter]
B --> C{Token present and valid?}
C -- No --> D[Security context cleared]
D --> E[AuthenticationEntryPoint returns 401]
C -- Yes --> F[Load user details]
F --> G[Set authenticated user in SecurityContext]
G --> H[BlogController endpoint]
H --> I[BlogServiceImpl]
I --> J{Admin or owner?}
J -- No --> K[AccessDeniedException]
K --> L[Return 403]
J -- Yes --> M[Save or delete blog]
M --> N[Return success response]
The security configuration is stateless. That means the server does not store login sessions. Instead, every protected request carries a JWT token in the Authorization header.
POST /api/auth/registerPOST /api/auth/login/swagger-ui/**/swagger-ui.html/v3/api-docs/**
/api/blogs/**/api/users/**
ROLE_USERcan create, read, update, and delete blogs they ownROLE_ADMINcan access all blog endpoints and manage users- only admins can list users and update roles
Handles:
- user registration
- email uniqueness checking
- password hashing
- login authentication
- JWT token generation
Handles:
- creating blog posts
- reading all blog posts
- reading a specific blog post
- updating blogs
- deleting blogs
- checking whether the current user is the owner or an admin
Handles:
- fetching the current authenticated user
- listing all users for admin use
- updating a user's role
| Method | Endpoint | Access | Description |
|---|---|---|---|
POST |
/api/auth/register |
Public | Register a new user |
POST |
/api/auth/login |
Public | Authenticate user and return JWT |
GET |
/api/blogs |
Authenticated | Get all blogs |
POST |
/api/blogs |
Authenticated | Create a new blog |
GET |
/api/blogs/{id} |
Authenticated | Get blog by id |
PUT |
/api/blogs/{id} |
Authenticated | Update a blog if owner or admin |
DELETE |
/api/blogs/{id} |
Authenticated | Delete a blog if owner or admin |
GET |
/api/users/me |
Authenticated | Get current user profile |
GET |
/api/users |
Admin | Get all users |
PUT |
/api/users/{userId}/role |
Admin | Update user role |
{
"name": "John Doe",
"email": "john@example.com",
"password": "strongPassword123"
}{
"email": "john@example.com",
"password": "strongPassword123"
}{
"token": "your-jwt-token",
"type": "Bearer"
}{
"title": "My First Blog",
"content": "This is the content of the blog post."
}The project uses validation annotations and centralized exception handling to keep responses consistent.
Examples of handled cases:
- invalid request body
- duplicate email during registration
- invalid credentials during login
- missing resource such as unknown blog or user
- forbidden actions such as editing another user's blog
Typical error response shape:
{
"timestamp": "2026-03-28T04:00:00",
"status": 403,
"error": "FORBIDDEN",
"message": "You are not allowed to update this blog",
"path": "/api/blogs/1"
}idnameemailpasswordrolecreatedAt- relationship to blogs
idtitlecontentcreatedAtupdatedAtauthor
ROLE_USERROLE_ADMIN
- Java 25
- PostgreSQL
- Maven or Maven Wrapper
Create a PostgreSQL database named:
CREATE DATABASE blog_restapi;Default application configuration currently expects:
- database:
blog_restapi - username:
postgres - password:
password123 - port:
5432
You can change these values in application.properties.
git clone https://github.com/JonniTech/SpringBoot-Blogs-RestAPI.git
cd SpringBoot-Blogs-RestAPI
./mvnw spring-boot:runThe application will run at:
http://localhost:8000
Swagger UI will be available at:
http://localhost:8000/swagger-ui.html
OpenAPI JSON will be available at:
http://localhost:8000/v3/api-docs
The current project configuration includes:
server.port=8000server.max-http-request-header-size=32KBjwt.expiration=86400000- PostgreSQL datasource settings
Important note for real deployment:
- replace the development database password
- replace the JWT secret with a strong environment-based secret
- disable Swagger UI in production if it is not required
- move secrets to environment variables
- add refresh token support
- add pagination for blog listing
- add search and filtering
- add unit and integration tests for controllers and services
- add Docker and Docker Compose support
- add CI pipeline for build and test automation
- add rate limiting and audit logging
- add profile-specific configuration for development and production
This project is not just a CRUD example. It demonstrates how to combine security, persistence, documentation, role management, and ownership checks into one coherent backend. It is useful as:
- a learning project for Spring Boot security
- a base backend for a blog platform
- a starter template for role-based REST APIs
- a reference for JWT authentication with clean project structure
Programmed by NYAGANYA