Skip to content

Java Spring Boot Backend API โ€‹

HaloLight Spring Boot backend API is built on Spring Boot 3.4.1, providing enterprise-grade backend service with complete JWT dual-token authentication.

API Documentation: https://halolight-api-java.h7ml.cn/api/swagger-ui

GitHub: https://github.com/halolight/halolight-api-java

Features โ€‹

  • ๐Ÿ” JWT Dual-Token - Access Token + Refresh Token, automatic renewal
  • ๐Ÿ›ก๏ธ RBAC Permissions - Role-based access control, wildcard matching
  • ๐Ÿ“ก RESTful API - Standardized interface design, OpenAPI documentation
  • ๐Ÿ—„๏ธ Spring Data JPA - Type-safe database operations
  • โœ… Data Validation - Bean Validation request parameter validation
  • ๐Ÿ“Š Logging System - Request logs, error tracking
  • ๐Ÿณ Docker Support - Multi-stage build, containerized deployment

Tech Stack โ€‹

TechnologyVersionDescription
Java23Runtime
Spring Boot3.4.1Web Framework
Spring Data JPA3.4.1Database ORM
PostgreSQL16Data Storage
Bean Validationjakarta.validationData Validation
JWTJJWTAuthentication
Springdoc OpenAPI2.7.0API Documentation

Quick Start โ€‹

Requirements โ€‹

  • Java >= 17
  • Maven >= 3.9
  • PostgreSQL 16 (optional, defaults to H2)

Installation โ€‹

bash
# Clone repository
git clone https://github.com/halolight/halolight-api-java.git
cd halolight-api-java

# Install dependencies
./mvnw clean install

Environment Variables โ€‹

bash
cp .env.example .env
env
# Database
DATABASE_URL=jdbc:postgresql://localhost:5432/halolight_db
DATABASE_USERNAME=postgres
DATABASE_PASSWORD=your-password

# JWT Secret
JWT_SECRET=your-super-secret-jwt-key-change-in-production-min-32-chars
JWT_EXPIRATION=86400000
JWT_REFRESH_EXPIRATION=604800000

# Service Configuration
PORT=8080
SPRING_PROFILES_ACTIVE=production

Database Initialization โ€‹

bash
# Auto-create tables (first run)
./mvnw spring-boot:run

# Run seed data (optional)
./mvnw exec:java -Dexec.mainClass="com.halolight.seed.DataSeeder"

Start Service โ€‹

bash
# Development mode
./mvnw spring-boot:run

# Production mode
./mvnw clean package -DskipTests
java -jar target/halolight-api-java-1.0.0.jar

Visit http://localhost:8080

Directory Structure โ€‹

halolight-api-java/
โ”œโ”€โ”€ src/main/java/com/halolight/
โ”‚   โ”œโ”€โ”€ controller/              # Controllers/Route handlers
โ”‚   โ”‚   โ”œโ”€โ”€ AuthController.java
โ”‚   โ”‚   โ”œโ”€โ”€ UserController.java
โ”‚   โ”‚   โ””โ”€โ”€ ...
โ”‚   โ”œโ”€โ”€ service/                 # Business logic layer
โ”‚   โ”‚   โ”œโ”€โ”€ AuthService.java
โ”‚   โ”‚   โ””โ”€โ”€ ...
โ”‚   โ”œโ”€โ”€ domain/                  # Data models
โ”‚   โ”‚   โ”œโ”€โ”€ entity/              # JPA Entities
โ”‚   โ”‚   โ””โ”€โ”€ repository/          # Repository interfaces
โ”‚   โ”œโ”€โ”€ config/                  # Middleware/Configuration
โ”‚   โ”‚   โ”œโ”€โ”€ SecurityConfig.java
โ”‚   โ”‚   โ””โ”€โ”€ ...
โ”‚   โ”œโ”€โ”€ web/dto/                 # Request validation DTOs
โ”‚   โ”œโ”€โ”€ security/                # Security components
โ”‚   โ””โ”€โ”€ HalolightApplication.java  # Application entry
โ”œโ”€โ”€ src/main/resources/          # Resource files
โ”‚   โ”œโ”€โ”€ application.yml
โ”‚   โ””โ”€โ”€ application-*.yml
โ”œโ”€โ”€ src/test/                    # Test files
โ”œโ”€โ”€ Dockerfile                   # Docker configuration
โ”œโ”€โ”€ docker-compose.yml
โ””โ”€โ”€ pom.xml                      # Maven configuration

API Modules โ€‹

Authentication Endpoints โ€‹

MethodPathDescriptionPermission
POST/api/auth/loginUser loginPublic
POST/api/auth/registerUser registrationPublic
POST/api/auth/refreshRefresh tokenPublic
POST/api/auth/logoutLogoutAuthenticated
POST/api/auth/forgot-passwordForgot passwordPublic
POST/api/auth/reset-passwordReset passwordPublic
GET/api/auth/meGet current userAuthenticated

User Management Endpoints โ€‹

MethodPathDescriptionPermission
GET/api/usersGet user listusers:view
GET/api/users/{id}Get user detailsusers:view
POST/api/usersCreate userusers:create
PUT/api/users/{id}Update userusers:update
PUT/api/users/{id}/statusUpdate user statususers:update
DELETE/api/users/{id}Delete userusers:delete

Complete Endpoint List โ€‹

Role Management (Roles) - 6 Endpoints โ€‹

MethodPathDescription
GET/api/rolesGet role list
GET/api/roles/{id}Get role details
POST/api/rolesCreate role
PUT/api/roles/{id}Update role
POST/api/roles/{id}/permissionsAssign permissions
DELETE/api/roles/{id}Delete role

Permission Management (Permissions) - 4 Endpoints โ€‹

MethodPathDescription
GET/api/permissionsGet permission list
POST/api/permissionsCreate permission
PUT/api/permissions/{id}Update permission
DELETE/api/permissions/{id}Delete permission

Document Management (Documents) - 10 Endpoints โ€‹

MethodPathDescription
GET/api/documentsGet document list
GET/api/documents/{id}Get document details
POST/api/documentsCreate document
PUT/api/documents/{id}Update document
PUT/api/documents/{id}/renameRename document
POST/api/documents/{id}/moveMove document
POST/api/documents/{id}/tagsUpdate tags
POST/api/documents/{id}/shareShare document
POST/api/documents/{id}/unshareUnshare document
DELETE/api/documents/{id}Delete document

File Management (Files) - 10 Endpoints โ€‹

MethodPathDescription
POST/api/files/uploadUpload file
GET/api/filesGet file list
GET/api/files/storageGet storage quota
GET/api/files/{id}Get file details
GET/api/files/{id}/downloadDownload file
PUT/api/files/{id}/renameRename file
POST/api/files/{id}/moveMove file
PUT/api/files/{id}/favoriteToggle favorite
POST/api/files/{id}/shareShare file
DELETE/api/files/{id}Delete file

Team Management (Teams) - 6 Endpoints โ€‹

MethodPathDescription
GET/api/teamsGet team list
GET/api/teams/{id}Get team details
POST/api/teamsCreate team
PUT/api/teams/{id}Update team
POST/api/teams/{id}/membersAdd member
DELETE/api/teams/{id}/members/{userId}Remove member

Message Management (Messages) - 5 Endpoints โ€‹

MethodPathDescription
GET/api/messages/conversationsGet conversation list
GET/api/messages/conversations/{userId}Get conversation messages
POST/api/messagesSend message
PUT/api/messages/{id}/readMark as read
DELETE/api/messages/{id}Delete message

Notification Management (Notifications) - 5 Endpoints โ€‹

MethodPathDescription
GET/api/notificationsGet notification list
GET/api/notifications/unread-countGet unread count
PUT/api/notifications/{id}/readMark single as read
PUT/api/notifications/read-allMark all as read
DELETE/api/notifications/{id}Delete notification

Calendar Management (Calendar) - 8 Endpoints โ€‹

MethodPathDescription
GET/api/calendar/eventsGet event list
GET/api/calendar/events/{id}Get event details
POST/api/calendar/eventsCreate event
PUT/api/calendar/events/{id}Update event
PUT/api/calendar/events/{id}/rescheduleReschedule
POST/api/calendar/events/{id}/attendeesAdd attendee
DELETE/api/calendar/events/{id}/attendees/{attendeeId}Remove attendee
DELETE/api/calendar/events/{id}Delete event

Dashboard (Dashboard) - 5 Endpoints โ€‹

MethodPathDescription
GET/api/dashboard/statsStatistics data
GET/api/dashboard/visitsVisit trends
GET/api/dashboard/salesSales data
GET/api/dashboard/piePie chart data
GET/api/dashboard/tasksPending tasks

Authentication Mechanism โ€‹

JWT Dual-Token โ€‹

Access Token:  24-hour validity, used for API requests
Refresh Token: 7-day validity, used to refresh Access Token

Request Header โ€‹

http
Authorization: Bearer <access_token>

Refresh Flow โ€‹

java
// Frontend auto-refresh example
@Component
public class JwtTokenInterceptor {
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        Response response = chain.proceed(request);

        // 401 auto refresh
        if (response.code() == 401) {
            String newToken = refreshToken(refreshToken);
            Request newRequest = request.newBuilder()
                .header("Authorization", "Bearer " + newToken)
                .build();
            return chain.proceed(newRequest);
        }

        return response;
    }
}

Permission System โ€‹

Role Definitions โ€‹

RoleDescriptionPermissions
super_adminSuper Administrator* (all permissions)
adminAdministratorusers:*, documents:*, roles:*
userRegular Userdocuments:view, files:view
guestGuestdashboard:view

Permission Format โ€‹

{resource}:{action}

Examples:
- users:view      # View users
- users:create    # Create users
- users:*         # All user operations
- *               # All permissions

Permission Check โ€‹

java
@RestController
@RequestMapping("/api/users")
public class UserController {

    @PreAuthorize("hasPermission('users:view')")
    @GetMapping
    public Page<UserDTO> getUsers(Pageable pageable) {
        return userService.findAll(pageable);
    }

    @PreAuthorize("hasPermission('users:create')")
    @PostMapping
    public UserDTO createUser(@Valid @RequestBody CreateUserRequest request) {
        return userService.create(request);
    }
}

Error Handling โ€‹

Error Response Format โ€‹

json
{
  "timestamp": "2025-12-04T12:00:00.000Z",
  "status": 400,
  "error": "Bad Request",
  "message": "Validation failed",
  "path": "/api/users",
  "details": [
    { "field": "email", "message": "must be a valid email address" }
  ]
}

Error Codes โ€‹

Status CodeError CodeDescription
400Bad RequestParameter validation failed
401UnauthorizedUnauthorized
403ForbiddenNo permission
404Not FoundResource not found
409ConflictResource conflict
422Unprocessable EntityBusiness logic error
429Too Many RequestsRate limit exceeded
500Internal Server ErrorServer error

Database Models โ€‹

Spring Data JPA entities include 17 models:

java
// User Entity
@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(unique = true, nullable = false)
    private String email;

    @Column(nullable = false)
    private String name;

    private String password;
    private String avatar;

    @Enumerated(EnumType.STRING)
    private UserStatus status = UserStatus.ACTIVE;

    @ManyToMany
    @JoinTable(name = "user_roles",
        joinColumns = @JoinColumn(name = "user_id"),
        inverseJoinColumns = @JoinColumn(name = "role_id"))
    private Set<Role> roles;

    @CreatedDate
    private LocalDateTime createdAt;

    @LastModifiedDate
    private LocalDateTime updatedAt;
}

// Role Entity
@Entity
@Table(name = "roles")
public class Role {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(unique = true)
    private String name;

    private String description;

    @ManyToMany
    @JoinTable(name = "role_permissions")
    private Set<Permission> permissions;
}

// Permission Entity
@Entity
@Table(name = "permissions")
public class Permission {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(unique = true)
    private String name;  // Format: "users:create", "users:*", "*"

    private String description;
}

Complete Entity List:

  • User, Role, Permission (RBAC core)
  • Team, TeamMember (team management)
  • Document, File, Folder (document/file)
  • CalendarEvent, EventAttendee (calendar)
  • Notification, Message, Conversation (notification/message)
  • Dashboard, Visit, Sale (dashboard statistics)

Environment Variables โ€‹

Variable NameDescriptionDefault Value
SPRING_PROFILES_ACTIVERuntime environmentdevelopment
PORTService port8080
DATABASE_URLDatabase connectionjdbc:postgresql://localhost:5432/halolight_db
DATABASE_USERNAMEDatabase usernamepostgres
DATABASE_PASSWORDDatabase password-
JWT_SECRETJWT secret (min 32 chars)-
JWT_EXPIRATIONAccessToken expiration (ms)86400000 (24h)
JWT_REFRESH_EXPIRATIONRefreshToken expiration (ms)604800000 (7d)
CORS_ALLOWED_ORIGINSCORS allowed originshttp://localhost:3000

Usage โ€‹

yaml
# application.yml
spring:
  datasource:
    url: ${DATABASE_URL}
    username: ${DATABASE_USERNAME}
    password: ${DATABASE_PASSWORD}

jwt:
  secret: ${JWT_SECRET}
  expiration: ${JWT_EXPIRATION:86400000}
  refreshExpiration: ${JWT_REFRESH_EXPIRATION:604800000}

Common Commands โ€‹

bash
# Development
./mvnw spring-boot:run                # Start development server
./mvnw spring-boot:run -Dspring-boot.run.profiles=dev  # Specify environment

# Build
./mvnw clean package                  # Build JAR
./mvnw clean package -DskipTests      # Build without tests
./mvnw clean install                  # Install to local repository

# Testing
./mvnw test                           # Run all tests
./mvnw test -Dtest=UserServiceTest    # Run specific test
./mvnw verify                         # Run integration tests
./mvnw test jacoco:report             # Generate coverage report

# Database
./mvnw flyway:migrate                 # Run migrations (if using Flyway)
./mvnw liquibase:update               # Update schema (if using Liquibase)

# Code Quality
./mvnw checkstyle:check               # Code style check
./mvnw spotbugs:check                 # Static analysis

Deployment โ€‹

Docker โ€‹

bash
docker build -t halolight-api-java .
docker run -p 8080:8080 --env-file .env halolight-api-java

Docker Compose โ€‹

bash
docker-compose up -d
yaml
# docker-compose.yml
version: '3.8'

services:
  app:
    build: .
    ports:
      - "8080:8080"
    environment:
      - SPRING_PROFILES_ACTIVE=production
      - DATABASE_URL=jdbc:postgresql://db:5432/halolight
      - DATABASE_USERNAME=postgres
      - DATABASE_PASSWORD=${DB_PASSWORD}
      - JWT_SECRET=${JWT_SECRET}
    depends_on:
      - db
    restart: unless-stopped

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: halolight
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - postgres_data:/var/lib/postgresql/data
    ports:
      - "5432:5432"

volumes:
  postgres_data:

Production Environment Configuration โ€‹

env
SPRING_PROFILES_ACTIVE=production
DATABASE_URL=jdbc:postgresql://prod-db.example.com:5432/halolight
DATABASE_USERNAME=halolight_user
DATABASE_PASSWORD=your-production-password
JWT_SECRET=your-production-secret-min-32-chars
CORS_ALLOWED_ORIGINS=https://halolight.h7ml.cn

Testing โ€‹

Run Tests โ€‹

bash
./mvnw test                           # Run unit tests
./mvnw test jacoco:report             # Generate coverage report
./mvnw verify                         # Run integration tests

Test Example โ€‹

java
@SpringBootTest
@AutoConfigureMockMvc
public class UserControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private ObjectMapper objectMapper;

    @Test
    public void testLogin() throws Exception {
        LoginRequest request = new LoginRequest();
        request.setEmail("admin@halolight.h7ml.cn");
        request.setPassword("123456");

        mockMvc.perform(post("/api/auth/login")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(request)))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.accessToken").exists())
                .andExpect(jsonPath("$.refreshToken").exists());
    }

    @Test
    @WithMockUser(authorities = {"users:view"})
    public void testGetUsers() throws Exception {
        mockMvc.perform(get("/api/users")
                .param("page", "0")
                .param("size", "10"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.content").isArray());
    }
}

Performance Metrics โ€‹

Benchmark โ€‹

MetricValueDescription
Request Throughput~3000 QPSSimple queries, 4 cores 8GB
Average Response Time15-30msP50, database queries
P95 Response Time50-100msIncluding complex queries
Memory Usage256-512 MBStable running state
CPU Usage10-30%Medium load

Performance Testing โ€‹

bash
# Using Apache Bench
ab -n 10000 -c 100 -H "Authorization: Bearer TOKEN" \
  http://localhost:8080/api/users

# Using wrk
wrk -t4 -c100 -d30s -H "Authorization: Bearer TOKEN" \
  http://localhost:8080/api/users

Observability โ€‹

Logging System โ€‹

java
// Logback configuration
@Slf4j
@RestController
public class UserController {

    @GetMapping("/api/users/{id}")
    public UserDTO getUser(@PathVariable Long id) {
        log.info("Fetching user with id: {}", id);
        try {
            return userService.findById(id);
        } catch (Exception e) {
            log.error("Error fetching user {}: {}", id, e.getMessage(), e);
            throw e;
        }
    }
}

Health Check โ€‹

java
@Component
public class CustomHealthIndicator implements HealthIndicator {

    @Override
    public Health health() {
        // Check database connection
        boolean dbUp = checkDatabase();

        if (dbUp) {
            return Health.up()
                .withDetail("database", "Available")
                .build();
        }

        return Health.down()
            .withDetail("database", "Unavailable")
            .build();
    }
}

Endpoint: GET /actuator/health

json
{
  "status": "UP",
  "components": {
    "db": { "status": "UP" },
    "diskSpace": { "status": "UP" }
  }
}

Monitoring Metrics โ€‹

yaml
# application.yml
management:
  endpoints:
    web:
      exposure:
        include: health,metrics,prometheus
  metrics:
    export:
      prometheus:
        enabled: true

Prometheus Endpoint: GET /actuator/prometheus

FAQ โ€‹

Q: How to modify JWT expiration time? โ€‹

A: Configure in .env or application.yml:

env
JWT_EXPIRATION=3600000          # 1 hour (milliseconds)
JWT_REFRESH_EXPIRATION=86400000  # 1 day (milliseconds)

Q: How to enable HTTPS? โ€‹

A: Generate certificate and configure Spring Boot:

yaml
# application.yml
server:
  port: 8443
  ssl:
    enabled: true
    key-store: classpath:keystore.p12
    key-store-password: your-password
    key-store-type: PKCS12
bash
# Generate self-signed certificate (development)
keytool -genkeypair -alias halolight -keyalg RSA -keysize 2048 \
  -storetype PKCS12 -keystore keystore.p12 -validity 365

Q: How to configure database connection pool? โ€‹

A: Use HikariCP (Spring Boot default):

yaml
spring:
  datasource:
    hikari:
      maximum-pool-size: 10
      minimum-idle: 5
      connection-timeout: 30000
      idle-timeout: 600000
      max-lifetime: 1800000

Q: How to implement pagination and sorting? โ€‹

A: Use Spring Data JPA Pageable:

java
@GetMapping("/api/users")
public Page<UserDTO> getUsers(
    @RequestParam(defaultValue = "0") int page,
    @RequestParam(defaultValue = "10") int size,
    @RequestParam(defaultValue = "id,desc") String sort
) {
    String[] sortParams = sort.split(",");
    Sort.Direction direction = sortParams.length > 1 &&
        sortParams[1].equals("desc") ? Sort.Direction.DESC : Sort.Direction.ASC;

    Pageable pageable = PageRequest.of(page, size, Sort.by(direction, sortParams[0]));
    return userService.findAll(pageable);
}

Development Tools โ€‹

  • IntelliJ IDEA - Official recommended IDE with Spring Boot support
  • Spring Boot DevTools - Hot reload, automatic restart
  • Lombok - Reduce boilerplate code
  • MapStruct - DTO mapping generation
  • JaCoCo - Code coverage tool
  • Postman/Insomnia - API testing tools

Comparison with Other Backends โ€‹

FeatureSpring BootNestJSFastAPIGo Fiber
LanguageJavaTypeScriptPythonGo
ORMJPA/HibernatePrismaSQLAlchemyGORM
Performanceโญโญโญโญโญโญโญโญโญโญโญโญโญโญโญโญ
Learning Curveโญโญโญโญโญโญโญโญโญโญโญโญ
Enterprise-Gradeโญโญโญโญโญโญโญโญโญโญโญโญโญโญโญ
Ecosystemโญโญโญโญโญโญโญโญโญโญโญโญโญโญโญโญ
Community Supportโญโญโญโญโญโญโญโญโญโญโญโญโญโญโญโญ