spring-boot4-skill 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/README.md +6 -3
- package/package.json +1 -1
- package/skills/java-spring-framework/SKILL.md +11 -2
- package/skills/java-spring-framework/references/spring-boot-4.md +86 -0
- package/skills/java-spring-framework/references/spring-data-mongodb.md +168 -0
- package/skills/java-spring-framework/references/spring-framework-7.md +37 -0
- package/skills/java-spring-framework/references/spring-graphql.md +150 -0
- package/skills/java-spring-framework/references/spring-redis.md +183 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.4.0] - 2026-02-21
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Skill:** New references: `spring-redis.md` (Redis, RedisTemplate, cache backend, session store, pub/sub), `spring-data-mongodb.md` (MongoDB, MongoTemplate, MongoRepository, documents, transactions, indexes), `spring-graphql.md` (Spring for GraphQL, schema, @QueryMapping/@MutationMapping, Records).
|
|
13
|
+
- **Skill:** spring-boot-4.md: new section 14 (API documentation — OpenAPI/springdoc) and section 15 (Scheduling — @Scheduled, cron, virtual threads).
|
|
14
|
+
- **Skill:** spring-framework-7.md: new section 14 (Bean Validation 3.1 — @Valid, @NotNull, @Size, @Email, Records).
|
|
15
|
+
- **Skill:** SKILL.md Reference Files table: Redis, MongoDB, GraphQL rows; extended Load when for spring-boot-4 (OpenAPI/springdoc, scheduling) and spring-framework-7 (Bean Validation, @Valid); Quick decision mermaid nodes for Redis, MongoDB, GraphQL.
|
|
16
|
+
- **README:** Skill reference table: spring-redis.md, spring-data-mongodb.md, spring-graphql.md; extended descriptions for spring-boot-4 and spring-framework-7.
|
|
17
|
+
|
|
18
|
+
[1.4.0]: https://github.com/AyrtonAldayr/agent-skill-java-spring-framework/compare/v1.3.0...v1.4.0
|
|
19
|
+
|
|
8
20
|
## [1.3.0] - 2026-02-21
|
|
9
21
|
|
|
10
22
|
### Added
|
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/spring-boot4-skill)
|
|
4
4
|
|
|
5
5
|
> **2026-standard** project scaffolder + Claude Code AI skill for Java / Spring development.
|
|
6
|
-
> By [AyrtonAldayr](https://github.com/AyrtonAldayr) · **v1.
|
|
6
|
+
> By [AyrtonAldayr](https://github.com/AyrtonAldayr) · **v1.4.0**
|
|
7
7
|
|
|
8
8
|
This repository provides two tools in one:
|
|
9
9
|
|
|
@@ -133,10 +133,13 @@ Once installed, Claude Code acts as a **Senior Spring Boot 4 architect**:
|
|
|
133
133
|
|
|
134
134
|
| File | Contents |
|
|
135
135
|
|---|---|
|
|
136
|
-
| `skills/java-spring-framework/references/spring-framework-7.md` | All Spring 7 APIs
|
|
137
|
-
| `skills/java-spring-framework/references/spring-boot-4.md` | Boot 4: native, virtual threads, testing
|
|
136
|
+
| `skills/java-spring-framework/references/spring-framework-7.md` | All Spring 7 APIs, Bean Validation, @Valid |
|
|
137
|
+
| `skills/java-spring-framework/references/spring-boot-4.md` | Boot 4: native, virtual threads, testing, reactive stack, rate limiting, connection pools, caching, performance, OpenAPI, scheduling |
|
|
138
138
|
| `skills/java-spring-framework/references/spring-security-7.md` | OAuth2 Resource Server, JWT, method security, CORS |
|
|
139
|
+
| `skills/java-spring-framework/references/spring-redis.md` | Redis, cache distribuido, session store |
|
|
140
|
+
| `skills/java-spring-framework/references/spring-data-mongodb.md` | MongoDB, document DB, Spring Data MongoDB |
|
|
139
141
|
| `skills/java-spring-framework/references/spring-messaging.md` | Kafka, event-driven, @KafkaListener, producer/consumer |
|
|
142
|
+
| `skills/java-spring-framework/references/spring-graphql.md` | GraphQL API, Spring for GraphQL |
|
|
140
143
|
| `skills/java-spring-framework/references/spring-modulith.md` | Module structure, events, integration testing, common pitfalls |
|
|
141
144
|
| `skills/java-spring-framework/references/build-templates.md` | Complete Gradle KTS + Maven POM templates |
|
|
142
145
|
| `skills/java-spring-framework/references/troubleshooting-migration.md` | Common errors (javax/jakarta, RestTemplate), Boot 3→4 checklist |
|
package/package.json
CHANGED
|
@@ -44,6 +44,12 @@ flowchart TD
|
|
|
44
44
|
M -->|Yes| N[spring-messaging.md]
|
|
45
45
|
A --> O{Rate limit / resources / performance?}
|
|
46
46
|
O -->|Yes| P[spring-boot-4.md]
|
|
47
|
+
A --> Q{Redis / cache distribuido?}
|
|
48
|
+
Q -->|Yes| R[spring-redis.md]
|
|
49
|
+
A --> S{MongoDB / document DB?}
|
|
50
|
+
S -->|Yes| T[spring-data-mongodb.md]
|
|
51
|
+
A --> U{GraphQL API?}
|
|
52
|
+
U -->|Yes| V[spring-graphql.md]
|
|
47
53
|
```
|
|
48
54
|
|
|
49
55
|
## Mandatory Workflow
|
|
@@ -80,10 +86,13 @@ Load these as needed — do not load all at once:
|
|
|
80
86
|
|
|
81
87
|
| Topic | File | Load when |
|
|
82
88
|
|---|---|---|
|
|
83
|
-
| Spring Framework 7 APIs | `references/spring-framework-7.md` | Framework-level features: versioning, resilience, JSpecify, SpEL, streaming |
|
|
84
|
-
| Spring Boot 4 features | `references/spring-boot-4.md` | Boot auto-config, Actuator, native images, testing, virtual threads, rate limiting, connection pools, resource metrics, caching, performance tuning |
|
|
89
|
+
| Spring Framework 7 APIs | `references/spring-framework-7.md` | Framework-level features: versioning, resilience, JSpecify, SpEL, streaming, Bean Validation, @Valid |
|
|
90
|
+
| Spring Boot 4 features | `references/spring-boot-4.md` | Boot auto-config, Actuator, native images, testing, virtual threads, rate limiting, connection pools, resource metrics, caching, performance tuning, OpenAPI/springdoc, scheduling |
|
|
85
91
|
| Spring Security 7 | `references/spring-security-7.md` | OAuth2 Resource Server, JWT, method security, CORS, authentication/authorization |
|
|
92
|
+
| Redis | `references/spring-redis.md` | Redis, cache distribuido, session store |
|
|
93
|
+
| MongoDB | `references/spring-data-mongodb.md` | MongoDB, document DB, Spring Data MongoDB |
|
|
86
94
|
| Messaging (Kafka) | `references/spring-messaging.md` | Kafka, event-driven, messaging, @KafkaListener, producer/consumer |
|
|
95
|
+
| GraphQL | `references/spring-graphql.md` | GraphQL API, Spring for GraphQL |
|
|
87
96
|
| Spring Modulith | `references/spring-modulith.md` | Domain-driven module design, event-driven architecture |
|
|
88
97
|
| Build templates | `references/build-templates.md` | Gradle KTS or Maven POM scaffolding with 2026 BOM versions |
|
|
89
98
|
| Troubleshooting & migration | `references/troubleshooting-migration.md` | Migration from Boot 3, compile/runtime errors (javax/jakarta, RestTemplate, native, null-safety) |
|
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
11. [Reactive Stack (R2DBC + WebFlux)](#11-reactive-stack-r2dbc--webflux)
|
|
19
19
|
12. [Rate Limiting](#12-rate-limiting)
|
|
20
20
|
13. [Resources & Performance](#13-resources--performance)
|
|
21
|
+
14. [API Documentation (OpenAPI / springdoc)](#14-api-documentation-openapi--springdoc)
|
|
22
|
+
15. [Scheduling](#15-scheduling)
|
|
21
23
|
|
|
22
24
|
---
|
|
23
25
|
|
|
@@ -598,3 +600,87 @@ Use short TTLs or size limits to avoid stale or unbounded caches.
|
|
|
598
600
|
- **Pool sizing:** Set HikariCP (or R2DBC pool) size based on observed metrics (connections in use, pending); avoid over-provisioning.
|
|
599
601
|
- **N+1:** Avoid N+1 queries in JPA (e.g. `@EntityGraph`, fetch joins, or DTO projections) so a single request does not open many statements.
|
|
600
602
|
- **Observability:** Use Actuator/Prometheus and traces (section 4) to find bottlenecks (slow endpoints, DB, or external calls) and tune accordingly. No JVM-level tuning (heap, GC) is covered here.
|
|
603
|
+
|
|
604
|
+
---
|
|
605
|
+
|
|
606
|
+
## 14. API Documentation (OpenAPI / springdoc)
|
|
607
|
+
|
|
608
|
+
Use **springdoc-openapi** to expose OpenAPI 3 spec and Swagger UI from your REST controllers. The JSON/YAML spec is consumed by clients and code generators; the UI is useful in development.
|
|
609
|
+
|
|
610
|
+
**Dependency:**
|
|
611
|
+
|
|
612
|
+
```kotlin
|
|
613
|
+
// build.gradle.kts
|
|
614
|
+
dependencies {
|
|
615
|
+
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0") // align with Boot 4
|
|
616
|
+
}
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
**application.yaml:**
|
|
620
|
+
|
|
621
|
+
```yaml
|
|
622
|
+
springdoc:
|
|
623
|
+
api-docs:
|
|
624
|
+
path: /v3/api-docs
|
|
625
|
+
swagger-ui:
|
|
626
|
+
path: /swagger-ui.html
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
Document controllers with `@Operation` and `@Parameter`:
|
|
630
|
+
|
|
631
|
+
```java
|
|
632
|
+
import io.swagger.v3.oas.annotations.Operation;
|
|
633
|
+
import io.swagger.v3.oas.annotations.Parameter;
|
|
634
|
+
import io.swagger.v3.oas.annotations.tags.Tag;
|
|
635
|
+
|
|
636
|
+
@RestController
|
|
637
|
+
@RequestMapping("/api/products")
|
|
638
|
+
@Tag(name = "Products", description = "Product API")
|
|
639
|
+
public class ProductController {
|
|
640
|
+
|
|
641
|
+
@GetMapping("/{id}")
|
|
642
|
+
@Operation(summary = "Get product by ID")
|
|
643
|
+
public Product get(@Parameter(description = "Product ID") @PathVariable String id) {
|
|
644
|
+
return productService.findById(id);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
```
|
|
648
|
+
|
|
649
|
+
The OpenAPI spec is available at `springdoc.api-docs.path` (default `/v3/api-docs`); Swagger UI at `springdoc.swagger-ui.path`. Protect these endpoints in production via Spring Security 7 (e.g. allow only for authenticated users or internal network). See [springdoc documentation](https://springdoc.org/) for grouping, security schemes, and more.
|
|
650
|
+
|
|
651
|
+
---
|
|
652
|
+
|
|
653
|
+
## 15. Scheduling
|
|
654
|
+
|
|
655
|
+
Use `@EnableScheduling` and `@Scheduled` for periodic tasks (cron, fixed rate, or fixed delay).
|
|
656
|
+
|
|
657
|
+
**Enable scheduling:**
|
|
658
|
+
|
|
659
|
+
```java
|
|
660
|
+
@SpringBootApplication
|
|
661
|
+
@EnableScheduling
|
|
662
|
+
public class Application { ... }
|
|
663
|
+
```
|
|
664
|
+
|
|
665
|
+
**Scheduled component:**
|
|
666
|
+
|
|
667
|
+
```java
|
|
668
|
+
import org.springframework.scheduling.annotation.Scheduled;
|
|
669
|
+
import org.springframework.stereotype.Component;
|
|
670
|
+
|
|
671
|
+
@Component
|
|
672
|
+
public class SyncJob {
|
|
673
|
+
|
|
674
|
+
@Scheduled(fixedRate = 60000) // every 60 seconds
|
|
675
|
+
public void sync() {
|
|
676
|
+
// run task
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
@Scheduled(cron = "0 0 * * * *") // every hour at minute 0
|
|
680
|
+
public void hourlyReport() {
|
|
681
|
+
// run task
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
```
|
|
685
|
+
|
|
686
|
+
With **virtual threads** enabled (section 6), scheduled tasks run on virtual threads when the default task scheduler is used; blocking work in the task does not block platform threads. For a custom `TaskScheduler` (e.g. thread pool size), define a `TaskScheduler` bean and configure as needed. Spring Batch is not covered here; for batch jobs (chunk-based reading/writing), see Spring Batch documentation.
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# Spring Data MongoDB — Boot 4
|
|
2
|
+
|
|
3
|
+
**Spring Boot**: 4.0.x | **Spring Data MongoDB**: aligned with Boot BOM | **Jakarta EE**: 11
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
1. [Dependencies](#1-dependencies)
|
|
10
|
+
2. [application.yaml](#2-applicationyaml)
|
|
11
|
+
3. [Documents and @Document](#3-documents-and-document)
|
|
12
|
+
4. [MongoRepository](#4-mongorepository)
|
|
13
|
+
5. [MongoTemplate](#5-mongotemplate)
|
|
14
|
+
6. [Transactions and indexes](#6-transactions-and-indexes)
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 1. Dependencies
|
|
19
|
+
|
|
20
|
+
Spring Boot 4 BOM manages Spring Data MongoDB. Add the starter:
|
|
21
|
+
|
|
22
|
+
```kotlin
|
|
23
|
+
// build.gradle.kts
|
|
24
|
+
dependencies {
|
|
25
|
+
implementation("org.springframework.boot:spring-boot-starter-data-mongodb")
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## 2. application.yaml
|
|
32
|
+
|
|
33
|
+
```yaml
|
|
34
|
+
spring:
|
|
35
|
+
data:
|
|
36
|
+
mongodb:
|
|
37
|
+
uri: mongodb://localhost:27017/mydb
|
|
38
|
+
# or separately:
|
|
39
|
+
# host: localhost
|
|
40
|
+
# port: 27017
|
|
41
|
+
# database: mydb
|
|
42
|
+
# username: app
|
|
43
|
+
# password: secret
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
For connection pool tuning (MongoDB driver):
|
|
47
|
+
|
|
48
|
+
```yaml
|
|
49
|
+
spring:
|
|
50
|
+
data:
|
|
51
|
+
mongodb:
|
|
52
|
+
uri: mongodb://localhost:27017/mydb
|
|
53
|
+
auto-index-creation: true
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## 3. Documents and @Document
|
|
59
|
+
|
|
60
|
+
Use a Java record or class with `@Document` and `@Id`. Prefer records for DTOs; for entities that need lazy loading or complex mapping, a class is fine.
|
|
61
|
+
|
|
62
|
+
```java
|
|
63
|
+
import org.springframework.data.annotation.Id;
|
|
64
|
+
import org.springframework.data.mongodb.core.mapping.Document;
|
|
65
|
+
import org.springframework.data.mongodb.core.index.Indexed;
|
|
66
|
+
|
|
67
|
+
@Document(collection = "products")
|
|
68
|
+
public record Product(
|
|
69
|
+
@Id String id,
|
|
70
|
+
String name,
|
|
71
|
+
@Indexed String sku,
|
|
72
|
+
java.math.BigDecimal price
|
|
73
|
+
) {}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
For mutable entities (e.g. append-only fields), use a class with getters/setters and `@Id` on the identifier field.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## 4. MongoRepository
|
|
81
|
+
|
|
82
|
+
Extend `MongoRepository<Entity, IdType>` for CRUD and query methods. Use `String` as ID type for MongoDB ObjectId-backed ids.
|
|
83
|
+
|
|
84
|
+
```java
|
|
85
|
+
import org.springframework.data.mongodb.repository.MongoRepository;
|
|
86
|
+
import java.util.List;
|
|
87
|
+
|
|
88
|
+
public interface ProductRepository extends MongoRepository<Product, String> {
|
|
89
|
+
|
|
90
|
+
List<Product> findByName(String name);
|
|
91
|
+
|
|
92
|
+
List<Product> findByPriceBetween(java.math.BigDecimal min, java.math.BigDecimal max);
|
|
93
|
+
|
|
94
|
+
boolean existsBySku(String sku);
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Custom queries with `@Query`:
|
|
99
|
+
|
|
100
|
+
```java
|
|
101
|
+
@Query("{ 'name' : { $regex: ?0, $options: 'i' } }")
|
|
102
|
+
List<Product> findByNameRegex(String pattern);
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Use `MongoRepository` when you need standard CRUD and derived queries; use `MongoTemplate` for dynamic queries or aggregations (see [Spring Data MongoDB docs](https://docs.spring.io/spring-data/mongodb/reference/) for aggregation pipelines).
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## 5. MongoTemplate
|
|
110
|
+
|
|
111
|
+
For programmatic or dynamic queries, inject `MongoTemplate`:
|
|
112
|
+
|
|
113
|
+
```java
|
|
114
|
+
import org.springframework.data.mongodb.core.MongoTemplate;
|
|
115
|
+
import org.springframework.data.mongodb.core.query.Criteria;
|
|
116
|
+
import org.springframework.data.mongodb.core.query.Query;
|
|
117
|
+
import org.springframework.data.mongodb.core.query.Update;
|
|
118
|
+
import org.springframework.stereotype.Service;
|
|
119
|
+
|
|
120
|
+
@Service
|
|
121
|
+
public class ProductService {
|
|
122
|
+
|
|
123
|
+
private final MongoTemplate mongo;
|
|
124
|
+
|
|
125
|
+
public ProductService(MongoTemplate mongo) {
|
|
126
|
+
this.mongo = mongo;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
public Product findById(String id) {
|
|
130
|
+
return mongo.findById(id, Product.class);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
public List<Product> findBySku(String sku) {
|
|
134
|
+
return mongo.find(Query.query(Criteria.where("sku").is(sku)), Product.class);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
public Product insert(Product product) {
|
|
138
|
+
return mongo.insert(product);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
public void updatePrice(String id, java.math.BigDecimal price) {
|
|
142
|
+
mongo.updateFirst(
|
|
143
|
+
Query.query(Criteria.where("id").is(id)),
|
|
144
|
+
Update.update("price", price),
|
|
145
|
+
Product.class
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## 6. Transactions and indexes
|
|
154
|
+
|
|
155
|
+
**Transactions:** Supported when MongoDB is run as a replica set. Enable with `@Transactional` on the method (or class). Spring Data MongoDB uses the same transaction manager as other Spring Data stores when configured.
|
|
156
|
+
|
|
157
|
+
```java
|
|
158
|
+
@Transactional
|
|
159
|
+
public void transfer(Product from, Product to, int qty) {
|
|
160
|
+
// multiple read/write operations in one transaction
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
**Indexes:** Use `@Indexed` on fields for single-field indexes. For compound or custom indexes, create them at startup via `MongoTemplate.indexOps(Product.class).ensureIndex(new Index().on("name", Sort.Direction.ASC).on("price", Sort.Direction.DESC))` or with `@CompoundIndex` on the document class. Set `spring.data.mongodb.auto-index-creation: true` in development; in production, manage indexes via migrations or infrastructure as code.
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
**Summary:** Use `spring-boot-starter-data-mongodb` with Boot 4 BOM, configure URI (or host/port/database) in `application.yaml`, and model documents with `@Document` and `@Id`. Use `MongoRepository` for CRUD and derived queries; use `MongoTemplate` for dynamic or programmatic access. Use transactions on replica sets and define indexes via `@Indexed` or programmatic API. For aggregation pipelines, see the official Spring Data MongoDB reference.
|
|
@@ -20,6 +20,7 @@ For **reactive applications (WebFlux + R2DBC)**, combine this reference with the
|
|
|
20
20
|
11. [Enhanced PathPattern](#11-enhanced-pathpattern)
|
|
21
21
|
12. [RestTestClient (Testing)](#12-resttestclient-testing)
|
|
22
22
|
13. [Removed APIs Migration](#13-removed-apis-migration)
|
|
23
|
+
14. [Bean Validation 3.1](#14-bean-validation-31)
|
|
23
24
|
|
|
24
25
|
---
|
|
25
26
|
|
|
@@ -316,3 +317,39 @@ class ProductControllerTest {
|
|
|
316
317
|
| XML Spring MVC config | `WebMvcConfigurer` (Java) |
|
|
317
318
|
| `suffixPatternMatch` | Explicit media types |
|
|
318
319
|
| `trailingSlashMatch` | Explicit URI templates |
|
|
320
|
+
|
|
321
|
+
---
|
|
322
|
+
|
|
323
|
+
## 14. Bean Validation 3.1
|
|
324
|
+
|
|
325
|
+
Spring MVC and WebFlux support **Jakarta Bean Validation 3.1** (Boot 4 brings the dependency). Use `@Valid` (or `@Validated`) on `@RequestBody`, and optionally on `@RequestParam` / path variables with group validation or custom validators.
|
|
326
|
+
|
|
327
|
+
**DTO with constraints:**
|
|
328
|
+
|
|
329
|
+
```java
|
|
330
|
+
import jakarta.validation.constraints.Email;
|
|
331
|
+
import jakarta.validation.constraints.NotBlank;
|
|
332
|
+
import jakarta.validation.constraints.Size;
|
|
333
|
+
|
|
334
|
+
public record CreateUserRequest(
|
|
335
|
+
@NotBlank(message = "Name is required")
|
|
336
|
+
@Size(min = 1, max = 100)
|
|
337
|
+
String name,
|
|
338
|
+
|
|
339
|
+
@NotBlank
|
|
340
|
+
@Email(message = "Must be a valid email")
|
|
341
|
+
String email
|
|
342
|
+
) {}
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
**Controller:**
|
|
346
|
+
|
|
347
|
+
```java
|
|
348
|
+
@PostMapping("/api/users")
|
|
349
|
+
public ResponseEntity<User> create(@Valid @RequestBody CreateUserRequest request) {
|
|
350
|
+
User user = userService.create(request);
|
|
351
|
+
return ResponseEntity.status(HttpStatus.CREATED).body(user);
|
|
352
|
+
}
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
Validation failures trigger `MethodArgumentNotValidException`; handle them in an `@ExceptionHandler` to return 400 with error details. Standard annotations include `@NotNull`, `@NotBlank`, `@Size`, `@Min`, `@Max`, `@Email`, `@Pattern`. For validation groups or custom validators, use `@Validated` with a group class or implement `ConstraintValidator`; see the Bean Validation spec for details.
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# Spring for GraphQL — Boot 4
|
|
2
|
+
|
|
3
|
+
**Spring Boot**: 4.0.x | **Spring for GraphQL**: aligned with Boot BOM | **Jakarta EE**: 11
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
1. [Dependencies](#1-dependencies)
|
|
10
|
+
2. [application.yaml](#2-applicationyaml)
|
|
11
|
+
3. [Schema (SDL)](#3-schema-sdl)
|
|
12
|
+
4. [Query and mutation controllers](#4-query-and-mutation-controllers)
|
|
13
|
+
5. [Records for types](#5-records-for-types)
|
|
14
|
+
6. [Security](#6-security)
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 1. Dependencies
|
|
19
|
+
|
|
20
|
+
Spring Boot 4 BOM manages Spring for GraphQL. Add the starter:
|
|
21
|
+
|
|
22
|
+
```kotlin
|
|
23
|
+
// build.gradle.kts
|
|
24
|
+
dependencies {
|
|
25
|
+
implementation("org.springframework.boot:spring-boot-starter-graphql")
|
|
26
|
+
implementation("org.springframework.boot:spring-boot-starter-web") // or webflux
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 2. application.yaml
|
|
33
|
+
|
|
34
|
+
```yaml
|
|
35
|
+
spring:
|
|
36
|
+
graphql:
|
|
37
|
+
graphiql:
|
|
38
|
+
enabled: true # dev: UI at /graphiql
|
|
39
|
+
path: /graphql # default
|
|
40
|
+
cors:
|
|
41
|
+
allowed-origins: "https://myapp.example.com"
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The GraphQL endpoint is exposed at `spring.graphql.path` (default `/graphql`). Use the same `SecurityFilterChain` as for REST to protect it; see section 6.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## 3. Schema (SDL)
|
|
49
|
+
|
|
50
|
+
Place schema files under `src/main/resources/graphql/**/*.graphqls` (or `.graphqls`). Boot auto-picks them.
|
|
51
|
+
|
|
52
|
+
Example `src/main/resources/graphql/schema.graphqls`:
|
|
53
|
+
|
|
54
|
+
```graphql
|
|
55
|
+
type Query {
|
|
56
|
+
product(id: ID!): Product
|
|
57
|
+
products(first: Int = 10): [Product!]!
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
type Mutation {
|
|
61
|
+
createProduct(input: CreateProductInput!): Product!
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
type Product {
|
|
65
|
+
id: ID!
|
|
66
|
+
name: String!
|
|
67
|
+
sku: String!
|
|
68
|
+
price: Float!
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
input CreateProductInput {
|
|
72
|
+
name: String!
|
|
73
|
+
sku: String!
|
|
74
|
+
price: Float!
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## 4. Query and mutation controllers
|
|
81
|
+
|
|
82
|
+
Use `@Controller` and `@QueryMapping` / `@MutationMapping`. Method names match schema field names by default, or specify with the annotation.
|
|
83
|
+
|
|
84
|
+
```java
|
|
85
|
+
import org.springframework.graphql.data.method.annotation.Argument;
|
|
86
|
+
import org.springframework.graphql.data.method.annotation.MutationMapping;
|
|
87
|
+
import org.springframework.graphql.data.method.annotation.QueryMapping;
|
|
88
|
+
import org.springframework.stereotype.Controller;
|
|
89
|
+
|
|
90
|
+
@Controller
|
|
91
|
+
public class ProductGraphQLController {
|
|
92
|
+
|
|
93
|
+
private final ProductService productService;
|
|
94
|
+
|
|
95
|
+
public ProductGraphQLController(ProductService productService) {
|
|
96
|
+
this.productService = productService;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
@QueryMapping
|
|
100
|
+
public Product product(@Argument String id) {
|
|
101
|
+
return productService.findById(id);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
@QueryMapping
|
|
105
|
+
public java.util.List<Product> products(@Argument Integer first) {
|
|
106
|
+
int limit = first != null ? first : 10;
|
|
107
|
+
return productService.findAll(limit);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
@MutationMapping
|
|
111
|
+
public Product createProduct(@Argument CreateProductInput input) {
|
|
112
|
+
return productService.create(input.name(), input.sku(), input.price());
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
For nested types (e.g. `Product` has a `category` field), use `@SchemaMapping` on a method that takes the parent object and returns the nested one:
|
|
118
|
+
|
|
119
|
+
```java
|
|
120
|
+
@SchemaMapping(typeName = "Product", field = "category")
|
|
121
|
+
public Category category(Product product) {
|
|
122
|
+
return categoryService.findById(product.categoryId());
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## 5. Records for types
|
|
129
|
+
|
|
130
|
+
Use Java records for DTOs and inputs. Spring for GraphQL maps them to the schema:
|
|
131
|
+
|
|
132
|
+
```java
|
|
133
|
+
public record CreateProductInput(String name, String sku, double price) {}
|
|
134
|
+
|
|
135
|
+
public record Product(String id, String name, String sku, double price, String categoryId) {}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Ensure record component names align with schema field names (or use `@JsonProperty` if names differ). Use `jakarta.*` and JSpecify where applicable for nullability in the API layer.
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## 6. Security
|
|
143
|
+
|
|
144
|
+
GraphQL is exposed over HTTP like REST. Protect the GraphQL path in your `SecurityFilterChain` (see `references/spring-security-7.md`): require authentication for `/graphql` and optionally allow anonymous for introspection in dev only. For authenticated GraphQL, clients typically send the same token (e.g. Bearer JWT) in the `Authorization` header; the resolver can access the current user via `SecurityContextHolder` or a custom context in the GraphQL execution.
|
|
145
|
+
|
|
146
|
+
Restrict introspection in production if you do not want to expose the full schema to unauthenticated clients.
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
**Summary:** Use `spring-boot-starter-graphql` with Boot 4 BOM, define the schema in `.graphqls` under `resources/graphql`, and implement queries and mutations with `@Controller`, `@QueryMapping`, and `@MutationMapping`. Use records for input and output types. Secure the GraphQL endpoint with the same Spring Security 7 configuration as REST.
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# Spring Data Redis — Boot 4
|
|
2
|
+
|
|
3
|
+
**Spring Boot**: 4.0.x | **Spring Data Redis**: aligned with Boot BOM | **Jakarta EE**: 11
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
1. [Dependencies](#1-dependencies)
|
|
10
|
+
2. [application.yaml](#2-applicationyaml)
|
|
11
|
+
3. [RedisTemplate and StringRedisTemplate](#3-redistemplate-and-stringredistemplate)
|
|
12
|
+
4. [Redis as cache backend](#4-redis-as-cache-backend)
|
|
13
|
+
5. [Pub/Sub (optional)](#5-pubsub-optional)
|
|
14
|
+
6. [Session store (optional)](#6-session-store-optional)
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 1. Dependencies
|
|
19
|
+
|
|
20
|
+
Spring Boot 4 BOM manages Spring Data Redis. Add the starter:
|
|
21
|
+
|
|
22
|
+
```kotlin
|
|
23
|
+
// build.gradle.kts
|
|
24
|
+
dependencies {
|
|
25
|
+
implementation("org.springframework.boot:spring-boot-starter-data-redis")
|
|
26
|
+
// Optional: use Redis as distributed cache backend (see section 4)
|
|
27
|
+
// implementation("org.springframework.boot:spring-boot-starter-cache")
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
For connection pooling (Lettuce is the default client):
|
|
32
|
+
|
|
33
|
+
```kotlin
|
|
34
|
+
// Lettuce is included transitively; no extra dependency for basic use
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## 2. application.yaml
|
|
40
|
+
|
|
41
|
+
```yaml
|
|
42
|
+
spring:
|
|
43
|
+
data:
|
|
44
|
+
redis:
|
|
45
|
+
host: localhost
|
|
46
|
+
port: 6379
|
|
47
|
+
password: # set in production
|
|
48
|
+
timeout: 2000ms
|
|
49
|
+
lettuce:
|
|
50
|
+
pool:
|
|
51
|
+
max-active: 16
|
|
52
|
+
max-idle: 8
|
|
53
|
+
min-idle: 4
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
For a single Redis URL:
|
|
57
|
+
|
|
58
|
+
```yaml
|
|
59
|
+
spring:
|
|
60
|
+
data:
|
|
61
|
+
redis:
|
|
62
|
+
url: redis://user:password@localhost:6379/0
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## 3. RedisTemplate and StringRedisTemplate
|
|
68
|
+
|
|
69
|
+
Use `StringRedisTemplate` for string keys/values; use `RedisTemplate<String, Object>` (with a configured serializer) for objects. Prefer strings when possible for simplicity and interoperability.
|
|
70
|
+
|
|
71
|
+
```java
|
|
72
|
+
import org.springframework.data.redis.core.StringRedisTemplate;
|
|
73
|
+
import org.springframework.stereotype.Service;
|
|
74
|
+
|
|
75
|
+
@Service
|
|
76
|
+
public class TokenStore {
|
|
77
|
+
|
|
78
|
+
private final StringRedisTemplate redis;
|
|
79
|
+
|
|
80
|
+
public TokenStore(StringRedisTemplate redis) {
|
|
81
|
+
this.redis = redis;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
public void save(String key, String value, java.time.Duration ttl) {
|
|
85
|
+
var ops = redis.opsForValue();
|
|
86
|
+
ops.set(key, value, ttl);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
public String get(String key) {
|
|
90
|
+
return redis.opsForValue().get(key);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Hashes and lists:
|
|
96
|
+
|
|
97
|
+
```java
|
|
98
|
+
redis.opsForHash().put("user:1", "name", "Alice");
|
|
99
|
+
redis.opsForHash().put("user:1", "email", "alice@example.com");
|
|
100
|
+
redis.opsForList().rightPush("queue:tasks", taskId);
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Use Records or simple DTOs when storing JSON; serialize with Jackson and store as string, or use `RedisTemplate<String, YourRecord>` with `GenericJackson2JsonRedisSerializer` (ensure Jakarta and Boot 4 Jackson versions align).
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## 4. Redis as cache backend
|
|
108
|
+
|
|
109
|
+
For distributed caching, use Redis instead of Caffeine. Enable caching and set the cache type to Redis:
|
|
110
|
+
|
|
111
|
+
```yaml
|
|
112
|
+
spring:
|
|
113
|
+
cache:
|
|
114
|
+
type: redis
|
|
115
|
+
cache-names: products,users
|
|
116
|
+
data:
|
|
117
|
+
redis:
|
|
118
|
+
host: localhost
|
|
119
|
+
port: 6379
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
```java
|
|
123
|
+
@SpringBootApplication
|
|
124
|
+
@EnableCaching
|
|
125
|
+
public class Application { ... }
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Use `@Cacheable`, `@CacheEvict`, `@CachePut` as with any Spring Cache backend. TTL can be configured per cache via `spring.cache.redis.time-to-live` or custom `RedisCacheConfiguration` if needed.
|
|
129
|
+
|
|
130
|
+
Dependencies: `spring-boot-starter-cache` and `spring-boot-starter-data-redis`. No extra starter for Redis cache; Boot auto-configures it when `type: redis` is set.
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## 5. Pub/Sub (optional)
|
|
135
|
+
|
|
136
|
+
Publish messages with `RedisTemplate`:
|
|
137
|
+
|
|
138
|
+
```java
|
|
139
|
+
redis.convertAndSend("notifications", "User signed up: " + userId);
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Subscribe with a listener:
|
|
143
|
+
|
|
144
|
+
```java
|
|
145
|
+
import org.springframework.data.redis.connection.Message;
|
|
146
|
+
import org.springframework.data.redis.connection.MessageListener;
|
|
147
|
+
import org.springframework.data.redis.listener.ChannelTopic;
|
|
148
|
+
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
|
|
149
|
+
import org.springframework.context.annotation.Bean;
|
|
150
|
+
import org.springframework.context.annotation.Configuration;
|
|
151
|
+
|
|
152
|
+
@Configuration
|
|
153
|
+
public class RedisPubSubConfig {
|
|
154
|
+
|
|
155
|
+
@Bean
|
|
156
|
+
RedisMessageListenerContainer redisMessageListenerContainer(
|
|
157
|
+
org.springframework.data.redis.connection.RedisConnectionFactory factory,
|
|
158
|
+
NotificationSubscriber subscriber) {
|
|
159
|
+
var container = new RedisMessageListenerContainer();
|
|
160
|
+
container.setConnectionFactory(factory);
|
|
161
|
+
container.addMessageListener(subscriber, new ChannelTopic("notifications"));
|
|
162
|
+
return container;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Implement `MessageListener` and handle `message.getBody()` in `onMessage`.
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## 6. Session store (optional)
|
|
172
|
+
|
|
173
|
+
To store HTTP sessions in Redis, add Spring Session:
|
|
174
|
+
|
|
175
|
+
```kotlin
|
|
176
|
+
implementation("org.springframework.session:spring-session-data-redis")
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Configure session timeout and optionally namespace in `application.yaml`. Sessions are then stored in Redis instead of in-memory; suitable for multi-instance deployments. Security and session configuration remain in Spring Security 7; see `references/spring-security-7.md` for auth.
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
**Summary:** Use `spring-boot-starter-data-redis` with Boot 4 BOM, configure host/port/pool in `application.yaml`, and use `StringRedisTemplate` or `RedisTemplate` for keys/values, hashes, and lists. Use `spring.cache.type=redis` for distributed cache; optionally use pub/sub or Spring Session Data Redis for sessions.
|