agentleFS
piominpiomin/claude-ai-spring-boot/skills/spring-boot/SKILL.md

spring-boot

Skill1.4k starsChanged 5 days agoApache-2.0View on GitHub

Spring Boot 4.x development - REST APIs, JPA, Security, Testing, and Cloud-native patterns. Use for building enterprise Java applications with Spring Boot.

10 other projects use a version of this, 1 of them edited.

Adopted by

Steady: 0 new projects took it in the last 4 weeks, none the 4 weeks before.thin data

Week of Sep 13: 0 new projectsWeek of Sep 20: 0 new projectsWeek of Sep 27: 0 new projectsWeek of Oct 4: 0 new projectsWeek of Oct 11: 0 new projectsWeek of Oct 18: 0 new projectsWeek of Oct 25: 0 new projectsWeek of Nov 1: 0 new projects
Sep 2026Oct 2026Nov 2026
New projects taking a version of this file each week, over the 8 weeks our latest run read: only 0 in all, so this is a hint, not a trend.

Most recently taken up by

Who's talking about piomin/claude-ai-spring-boot

The biggest 5 of 17 mentions linking to the repository that our crawl found. Each line is their own headline.

See all 17

Talked about on GitHub (17 times).

On GitHub · 17

12 more on GitHub

The file

We split this skill into the rules it gives an agent

7 rules. 0 are common, 3 are distinctive, and 4 are very specific to this project.

  • 0

    Common

    Most of the 9 edited versions we read keep these.

  • 3

    Distinctive

    This author's own choices. Worth a look.

  • 4

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Secure: - Add Spring Security, OAuth2, method security.

    line 22
  2. Very specific:

    Test: - Write unit, integration tests.

    line 23
  3. Very specific:

    Every error is RFC 9457 Problem Details with field errors in errors: [{field, code, message}].

    line 155
  4. Distinctive:

    Use deprecated Spring Boot 2.x/3.x patterns

    line 214
  5. Very specific:

    Use ddl-auto: update/create outside tests, or edit an already-applied migration

    line 216
  6. Very specific:

    Put @ApiResponse on controller methods (springdoc 3 then drops the success response.

    line 218
  7. Distinctive:

    Use case driven design

    line 249

“Common” is measured: kept by at least 6 in 10 of the 9 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Spring Boot Skill

Enterprise Spring Boot 4.x development with focus on clean architecture and production-ready code.

Core Workflow

  1. Analyze - Understand requirements, identify service boundaries, APIs, data models
  2. Design - Plan architecture, confirm design before coding
  3. Implement - Build with constructor injection and layered architecture; for a relational database, write Flyway migrations alongside entities (ddl-auto: validate)
  4. Secure - Add Spring Security, OAuth2, method security; verify tests pass
  5. Test - Write unit, integration tests; run ./mvnw clean verify and confirm all pass, with integration tests running (not skipped)
  6. Deploy - Configure health checks via Actuator; validate /actuator/health returns UP and /actuator/info shows build.version

Quick Start Templates

Entity

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

    @NotBlank
    private String name;

    @DecimalMin("0.0")
    private BigDecimal price;

    // Getters/Setters (no Lombok)
}

Repository

public interface ProductRepository extends JpaRepository<Product, Long> {
    List<Product> findByNameContainingIgnoreCase(String name);
}

Service

@Service
@Transactional(readOnly = true)
public class ProductService {
    private final ProductRepository repo;

    public ProductService(ProductRepository repo) {
        this.repo = repo;
    }

    public List<ProductResponse> search(String name) {
        return repo.findByNameContainingIgnoreCase(name).stream()
            .map(ProductResponse::from)
            .toList();
    }

    @Transactional
    public ProductResponse create(ProductRequest request) {
        var product = new Product();
        product.setName(request.name());
        product.setPrice(request.price());
        return ProductResponse.from(repo.save(product));
    }
}

REST Controller

@RestController
@RequestMapping("/api/v1/products")
@Validated
public class ProductController {
    private final ProductService service;

    public ProductController(ProductService service) {
        this.service = service;
    }

    @GetMapping
    public List<ProductResponse> search(@RequestParam(defaultValue = "") String name) {
        return service.search(name);
    }

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public ProductResponse create(@Valid @RequestBody ProductRequest request) {
        return service.create(request);
    }
}

DTOs (Records)

public record ProductRequest(
    @NotBlank String name,
    @DecimalMin("0.0") BigDecimal price
) {}

public record ProductResponse(Long id, String name, BigDecimal price) {
    public static ProductResponse from(Product product) {
        return new ProductResponse(product.getId(), product.getName(), product.getPrice());
    }
}

Global Exception Handler (Problem Details)

@RestControllerAdvice
public class ApiExceptionHandler extends ResponseEntityExceptionHandler {

    public record FieldMessage(String field, String code, String message) {}

    @Override
    protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers,
                                                                  HttpStatusCode status, WebRequest request) {
        ex.getBody().setProperty("errors", ex.getFieldErrors().stream()
                .map(e -> new FieldMessage(e.getField(), code(e.getCode()), e.getDefaultMessage()))
                .toList());
        return super.handleMethodArgumentNotValid(ex, headers, status, request);
    }

    private static String code(String constraint) {
        return switch (constraint) {
            case "NotBlank", "NotNull" -> "required";
            case null, default -> "invalid";
        };
    }

    @ExceptionHandler
    ProblemDetail handleOptimisticLock(ObjectOptimisticLockingFailureException ex) {
        return ProblemDetail.forStatusAndDetail(HttpStatus.CONFLICT, "Record was changed by someone else; reload and retry");
    }

    @ExceptionHandler
    ProblemDetail handleUnexpected(Exception ex) {
        logger.error("Unexpected error", ex);
        return ProblemDetail.forStatusAndDetail(HttpStatus.INTERNAL_SERVER_ERROR, "Unexpected server error");
    }
}

Every error is RFC 9457 Problem Details with field errors in errors: [{field, code, message}]. Full handler (unique constraints, bad sort, documented codes): references/web.md.

Test Slice

@WebMvcTest(ProductController.class)
class ProductControllerTest {
    @Autowired MockMvc mockMvc;
    @MockitoBean ProductService service;

    @Test
    void createProduct_validRequest_returns201() throws Exception {
        var response = new ProductResponse(1L, "Widget", new BigDecimal("10.00"));
        when(service.create(any())).thenReturn(response);

        mockMvc.perform(post("/api/v1/products")
                .contentType(MediaType.APPLICATION_JSON)
                .content("""
                    {"name":"Widget","price":10.0}
                    """))
            .andExpect(status().isCreated())
            .andExpect(jsonPath("$.name").value("Widget"));
    }
}

Reference Guide

Load detailed patterns based on context:

TopicReferenceWhen to Load
Web/RESTreferences/web.mdControllers, paged lists, validation, PUT and version, Problem Details (RFC 9457) with errors[]
API built for a UIreferences/ui-contract.mdOnly when a UI or generated client consumes the OpenAPI contract (see conventions): /api, springdoc 3 contract export, error codes, filters, relations, Keycloak, "Connect a UI"
Data Accessreferences/data.mdJPA, repositories, transactions, queries, Flyway migrations
Securityreferences/security.mdSpring Security 6, OAuth2, JWT, auth
Cloud/Configreferences/cloud.mdConfig server, discovery, resilience, virtual threads, HTTP interfaces, GraalVM native, OTLP tracing
AIreferences/ai.mdOnly when the app calls an LLM: Spring AI 2.x ChatClient, structured output, tool calling, RAG, chat memory, MCP server, testing, 1.x to 2.0 renames
Testingreferences/testing.mdUnit, integration, slice tests, Testcontainers @ServiceConnection

Constraints

MUST DO

  • Constructor injection (no field injection)
  • @Valid on all request bodies
  • @Transactional for multi-step writes
  • @Transactional(readOnly = true) for reads
  • Type-safe config with @ConfigurationProperties
  • Global exception handling with @RestControllerAdvice
  • Externalize secrets (use env vars, not properties files)
  • Flyway migrations + ddl-auto: validate when persisting to a relational database (skip Flyway otherwise)
  • Errors as Problem Details; list endpoints return PagedModel
  • Permit /actuator/health/** so the liveness and readiness probes work behind security
  • Only when the API is built for a UI (see conventions, "REST API Built for a UI"): follow references/ui-contract.md

MUST NOT DO

  • Field injection (@Autowired on fields)
  • Skip input validation on endpoints
  • Mix blocking and reactive code
  • Store secrets in application.properties
  • Use deprecated Spring Boot 2.x/3.x patterns
  • Hardcode URLs, credentials, environment values
  • Use ddl-auto: update/create outside tests, or edit an already-applied migration
  • Return Page/PageImpl from a controller, or put @EnableSpringDataWebSupport on the application class (@WebMvcTest registers it twice)
  • Put @ApiResponse on controller methods (springdoc 3 then drops the success response; use an OpenApiCustomizer)
  • Return an exception's message from the catch-all handler

Spring Boot 4 / Spring Data 4 Notes

  • PropertyReferenceException and TypeInformation moved to org.springframework.data.core
  • @MockBean/@SpyBean are removed: use @MockitoBean/@MockitoSpyBean
  • @WebMvcTest creates no repositories: every repository a controller injects needs a @MockitoBean in its test
  • Build with ./mvnw clean verify: classes compiled by the IDE in target can hide javac errors

Architecture Patterns

Project Structure:

src/main/java/pl/piomin/services/
├── controller/     # REST endpoints
├── service/        # Business logic
├── repository/     # Data access
├── model/          # Entities
├── dto/            # Request/Response DTOs
├── config/         # Configuration
└── exception/      # Custom exceptions + handler

Layering:

  • Controller → Service → Repository
  • Controller handles HTTP, validation
  • Service handles business logic, transactions
  • Repository handles data persistence

Clean Architecture Principles:

  • Domain models independent of frameworks
  • Use case driven design
  • Dependency inversion (interfaces)
  • Clear boundaries between layers

Common Annotations

AnnotationPurpose
@RestControllerREST controller (combines @Controller + @ResponseBody)
@ServiceBusiness logic component
@RepositoryData access component
@TransactionalTransaction management
@ValidTrigger validation
@ConfigurationPropertiesBind properties to class
@EnableMethodSecurityEnable method security

Reactive WebFlux Endpoint

@RestController
@RequestMapping("/api/v1/orders")
public class OrderController {
    private final OrderService orderService;

    public OrderController(OrderService orderService) {
        this.orderService = orderService;
    }

    @GetMapping("/{id}")
    public Mono<ResponseEntity<OrderDto>> getOrder(@PathVariable UUID id) {
        return orderService.findById(id)
                .map(ResponseEntity::ok)
                .defaultIfEmpty(ResponseEntity.notFound().build());
    }

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public Mono<OrderDto> createOrder(@Valid @RequestBody CreateOrderRequest request) {
        return orderService.create(request);
    }
}

Spring Security JWT

@Configuration
@EnableMethodSecurity
public class SecurityConfig {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        return http
                .csrf(AbstractHttpConfigurer::disable)
                .sessionManagement(s -> s.sessionCreationPolicy(STATELESS))
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers("/actuator/health/**").permitAll()  // + "/actuator/info" when built for a UI
                        .anyRequest().authenticated())
                .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()))
                .build();
    }
}

Knowledge Base

Spring Boot 4.x, Java 21+, Spring WebFlux, Project Reactor, Spring Data JPA, Spring Security, OAuth2/JWT, Hibernate, R2DBC, Spring Cloud, Resilience4j, Micrometer, JUnit 5, Testcontainers, Mockito, Maven, Paketo Buildpacks, Skaffold

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.