
Spring Boot Application
- 415 installs
- 305 repo stars
- Updated March 4, 2026
- aj-geddes/useful-ai-prompts
spring-boot-application is an agent skill that scaffolds Spring Boot REST services with JPA persistence, Spring Security, and deployment profiles so developers building JVM APIs can follow production-ready Spring convent
About
spring-boot-application is an aj-geddes/useful-ai-prompts agent skill for production-ready Spring Boot applications using annotation-based configuration, dependency injection, REST controllers, JPA data persistence, service layers, and Spring Security. Its quick-start pom.xml targets spring-boot-starter-parent 3.1.0 with spring-boot-starter-web, spring-boot-starter-data-jpa, and spring-boot-starter-security dependencies. Seven reference guides cover project setup, JPA entity models, Spring Data repositories, service-layer business logic, REST request/response handling, security configuration, and application profiles. Best practices emphasize DTO mapping, @Transactional boundaries, validated controllers, and keeping business logic out of controllers. Developers reach for spring-boot-application when bootstrapping a Java microservice, adding JPA entities, or wiring Spring Security—not for non-JVM stacks or frontend-only work.
- Project scaffolding
- REST and validation
- Spring Data JPA
- Security and profiles
- Actuator and packaging
Spring Boot Application by the numbers
- 415 all-time installs (skills.sh)
- Ranked #15 of 89 Java & JVM skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/aj-geddes/useful-ai-prompts --skill spring-boot-applicationAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 415 |
|---|---|
| repo stars | ★ 305 |
| Last updated | March 4, 2026 |
| Repository | aj-geddes/useful-ai-prompts ↗ |
How do you scaffold a Spring Boot REST API?
Scaffold and extend Spring Boot services with REST controllers, JPA persistence, security config, profiles, and container-ready deployment for JVM APIs.
Who is it for?
Java backend developers creating Spring Boot microservices who need JPA, REST, and Spring Security patterns aligned to starter-parent conventions.
Skip if: Teams building Node, Go, or Python APIs, or developers who only need frontend React components without a JVM backend.
When should I use this skill?
User builds Spring Boot REST APIs, configures JPA entities, wires Spring Security, or scaffolds Java microservices with profiles and containers.
What you get
Spring Boot project structure with pom.xml, JPA entities, repositories, REST controllers, security config, and application profile templates.
- pom.xml scaffold
- JPA entity and repository templates
- REST controller and security configuration
By the numbers
- Includes 7 reference guides from setup through Spring Security
- Quick-start targets spring-boot-starter-parent 3.1.0
- Documents spring-boot-starter-web, data-jpa, and security dependencies
Files
Spring Boot Application
Table of Contents
Overview
Develop production-ready Spring Boot applications with proper annotation-based configuration, dependency injection, REST controllers, JPA data persistence, service layers, and security implementation following Spring conventions.
When to Use
- Building Spring Boot REST APIs
- Implementing service-oriented architectures
- Configuring data persistence with JPA
- Managing dependency injection
- Implementing Spring Security
- Building microservices with Spring Boot
Quick Start
Minimal working example:
<!-- pom.xml -->
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>api-service</artifactId>
<version>1.0.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.0</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
// ... (see reference guides for full implementation)Reference Guides
Detailed implementations in the references/ directory:
| Guide | Contents |
|---|---|
| Spring Boot Project Setup | Spring Boot Project Setup |
| Entity Models with JPA Annotations | Entity Models with JPA Annotations |
| Repository Layer with Spring Data JPA | Repository Layer with Spring Data JPA |
| Service Layer with Business Logic | Service Layer with Business Logic |
| REST Controllers with Request/Response Handling | REST Controllers with Request/Response Handling |
| Spring Security Configuration | Spring Security Configuration |
| Application Configuration | Application Configuration |
Best Practices
✅ DO
- Use dependency injection for loose coupling
- Implement service layer for business logic
- Use repositories for data access
- Leverage Spring Security for authentication
- Use @Transactional for transaction management
- Validate input in controllers
- Return appropriate HTTP status codes
- Use DTOs for request/response mapping
- Implement proper exception handling
- Use Spring's @Async for async operations
❌ DON'T
- Put business logic in controllers
- Access database directly in controllers
- Store secrets in configuration files
- Use eager loading for large relationships
- Ignore transaction boundaries
- Return database entities in API responses
- Implement authentication in controllers
- Use raw SQL without parameterized queries
- Forget to validate user input
Application Configuration
Application Configuration
# application.yml
spring:
application:
name: api-service
datasource:
url: jdbc:postgresql://localhost:5432/mydb
username: ${DB_USER}
password: ${DB_PASSWORD}
driver-class-name: org.postgresql.Driver
jpa:
hibernate:
ddl-auto: validate
properties:
hibernate:
dialect: org.hibernate.dialect.PostgreSQLDialect
security:
jwt:
secret: ${JWT_SECRET}
expiration: 86400000
server:
port: 8080
servlet:
context-path: /Entity Models with JPA Annotations
Entity Models with JPA Annotations
// User.java
package com.example.model;
import jakarta.persistence.*;
import java.time.LocalDateTime;
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
@Entity
@Table(name = "users", indexes = {
@Index(name = "idx_email", columnList = "email", unique = true),
@Index(name = "idx_role", columnList = "role")
})
public class User implements UserDetails {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
private String id;
@Column(unique = true, nullable = false)
private String email;
@Column(nullable = false)
private String passwordHash;
@Column(name = "first_name")
private String firstName;
@Column(name = "last_name")
private String lastName;
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private Role role = Role.USER;
@Column(name = "is_active")
private Boolean isActive = true;
@Column(name = "created_at", nullable = false, updatable = false)
private LocalDateTime createdAt = LocalDateTime.now();
@Column(name = "updated_at")
private LocalDateTime updatedAt = LocalDateTime.now();
@OneToMany(mappedBy = "author", cascade = CascadeType.ALL, orphanRemoval = true)
private Collection<Post> posts;
// Getters and setters
@Override
public String getUsername() {
return email;
}
@Override
public String getPassword() {
return passwordHash;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return java.util.List.of(new SimpleGrantedAuthority(role.name()));
}
@Override
public boolean isEnabled() {
return isActive;
}
public enum Role {
USER, ADMIN
}
}
// Post.java
@Entity
@Table(name = "posts")
public class Post {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
private String id;
@Column(nullable = false)
private String title;
@Column(nullable = false, columnDefinition = "TEXT")
private String content;
@Column(nullable = false)
private Boolean published = false;
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "user_id")
private User author;
@Column(name = "created_at", nullable = false, updatable = false)
private LocalDateTime createdAt = LocalDateTime.now();
@Column(name = "updated_at")
private LocalDateTime updatedAt = LocalDateTime.now();
// Getters and setters
}Repository Layer with Spring Data JPA
Repository Layer with Spring Data JPA
// UserRepository.java
package com.example.repository;
import com.example.model.User;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import java.util.Optional;
@Repository
public interface UserRepository extends JpaRepository<User, String> {
Optional<User> findByEmail(String email);
@Query("SELECT u FROM User u WHERE u.email LIKE %:search% OR u.firstName LIKE %:search%")
Page<User> searchUsers(String search, Pageable pageable);
Page<User> findByRole(User.Role role, Pageable pageable);
}
// PostRepository.java
@Repository
public interface PostRepository extends JpaRepository<Post, String> {
Page<Post> findByAuthorAndPublishedTrue(User author, Pageable pageable);
@Query(value = "SELECT p FROM Post p WHERE p.published = true ORDER BY p.createdAt DESC",
countQuery = "SELECT COUNT(p) FROM Post p WHERE p.published = true")
Page<Post> findPublishedPosts(Pageable pageable);
Long countByAuthorId(String authorId);
}REST Controllers with Request/Response Handling
REST Controllers with Request/Response Handling
// UserController.java
package com.example.controller;
import com.example.model.User;
import com.example.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
@GetMapping
public ResponseEntity<Page<User>> getUsers(
@RequestParam(required = false) String search,
Pageable pageable) {
Page<User> users = userService.getUsers(search, pageable);
return ResponseEntity.ok(users);
}
@GetMapping("/{id}")
public ResponseEntity<User> getUser(@PathVariable String id) {
return userService.getUserById(id)
.map(ResponseEntity::ok)
.orElseGet(() -> ResponseEntity.notFound().build());
}
@PostMapping
public ResponseEntity<User> createUser(@RequestBody CreateUserRequest request) {
User user = userService.createUser(
request.getEmail(),
request.getPassword(),
request.getFirstName(),
request.getLastName()
);
return ResponseEntity.status(HttpStatus.CREATED).body(user);
}
@PatchMapping("/{id}")
@PreAuthorize("@securityService.isCurrentUser(#id)")
public ResponseEntity<User> updateUser(
@PathVariable String id,
@RequestBody UpdateUserRequest request) {
User user = userService.updateUser(id, request.getFirstName(), request.getLastName());
return ResponseEntity.ok(user);
}
@DeleteMapping("/{id}")
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<Void> deleteUser(@PathVariable String id) {
userService.deleteUser(id);
return ResponseEntity.noContent().build();
}
}
// DTO classes
@Data
class CreateUserRequest {
private String email;
private String password;
private String firstName;
private String lastName;
}
@Data
class UpdateUserRequest {
private String firstName;
private String lastName;
}Service Layer with Business Logic
Service Layer with Business Logic
// UserService.java
package com.example.service;
import com.example.model.User;
import com.example.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Optional;
@Service
@RequiredArgsConstructor
@Transactional
public class UserService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
public User createUser(String email, String password, String firstName, String lastName) {
if (userRepository.findByEmail(email).isPresent()) {
throw new IllegalArgumentException("Email already exists");
}
User user = new User();
user.setEmail(email);
user.setPasswordHash(passwordEncoder.encode(password));
user.setFirstName(firstName);
user.setLastName(lastName);
return userRepository.save(user);
}
@Transactional(readOnly = true)
public Page<User> getUsers(String search, Pageable pageable) {
if (search != null && !search.isBlank()) {
return userRepository.searchUsers(search, pageable);
}
return userRepository.findAll(pageable);
}
@Transactional(readOnly = true)
public Optional<User> getUserById(String id) {
return userRepository.findById(id);
}
public User updateUser(String id, String firstName, String lastName) {
User user = userRepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("User not found"));
if (firstName != null) user.setFirstName(firstName);
if (lastName != null) user.setLastName(lastName);
return userRepository.save(user);
}
public void deleteUser(String id) {
userRepository.deleteById(id);
}
}
// PostService.java
@Service
@RequiredArgsConstructor
@Transactional
public class PostService {
private final PostRepository postRepository;
private final UserRepository userRepository;
public Post createPost(String userId, String title, String content) {
User author = userRepository.findById(userId)
.orElseThrow(() -> new IllegalArgumentException("User not found"));
Post post = new Post();
post.setTitle(title);
post.setContent(content);
post.setAuthor(author);
return postRepository.save(post);
}
@Transactional(readOnly = true)
public Page<Post> getPublishedPosts(Pageable pageable) {
return postRepository.findPublishedPosts(pageable);
}
public Post publishPost(String postId) {
Post post = postRepository.findById(postId)
.orElseThrow(() -> new IllegalArgumentException("Post not found"));
post.setPublished(true);
return postRepository.save(post);
}
}Spring Boot Project Setup
Spring Boot Project Setup
<!-- pom.xml -->
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>api-service</artifactId>
<version>1.0.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.0</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.12.3</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>Spring Security Configuration
Spring Security Configuration
// SecurityConfig.java
package com.example.config;
import com.example.security.JwtAuthenticationFilter;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
private final JwtAuthenticationFilter jwtAuthenticationFilter;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeHttpRequests()
.requestMatchers("/api/auth/**").permitAll()
.requestMatchers("/api/users/**").authenticated()
.anyRequest().authenticated()
.and()
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
// JwtAuthenticationFilter.java
@Component
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtTokenProvider tokenProvider;
private final UserDetailsService userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
try {
String token = getJwtFromRequest(request);
if (token != null && tokenProvider.validateToken(token)) {
String userId = tokenProvider.getUserIdFromToken(token);
UserDetails userDetails = userDetailsService.loadUserByUsername(userId);
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (Exception ex) {
logger.error("Could not set user authentication", ex);
}
filterChain.doFilter(request, response);
}
private String getJwtFromRequest(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7);
}
return null;
}
}#!/bin/bash
# security-checklist.sh - Generate a security review checklist
# Usage: ./security-checklist.sh [--output checklist.md]
set -euo pipefail
OUTPUT="${{1:-/dev/stdout}}"
cat > "$OUTPUT" << 'CHECKLIST'
# Security Review Checklist
## Authentication & Authorization
- [ ] All endpoints require authentication
- [ ] Role-based access control implemented
- [ ] Session management is secure
## Input Validation
- [ ] All user inputs are validated
- [ ] SQL injection prevention
- [ ] XSS prevention
## Data Protection
- [ ] Sensitive data encrypted at rest
- [ ] Sensitive data encrypted in transit
- [ ] PII handling compliant
## TODO: Add domain-specific security checks
CHECKLIST
echo "Checklist generated: $OUTPUT" >&2
Related skills
How it compares
Choose spring-boot-application for annotation-driven Spring Boot JVM APIs; use framework-specific Node or Python skills when the backend is not Java.
FAQ
Which Spring Boot version does spring-boot-application reference?
The spring-boot-application skill quick-start pom.xml uses spring-boot-starter-parent version 3.1.0 with spring-boot-starter-web, spring-boot-starter-data-jpa, and spring-boot-starter-security dependencies as the baseline scaffold.
What layers does spring-boot-application document?
The spring-boot-application skill separates REST controllers, service-layer business logic, Spring Data JPA repositories, entity models, Spring Security configuration, and application profiles. Best practices keep business logic out of controllers and return DTOs instead of raw e
How many reference guides ship with spring-boot-application?
The spring-boot-application skill includes seven reference guides covering project setup, JPA entity models, repositories, service layers, REST controllers, Spring Security, and application configuration under its references directory.