Accessing Couchbase with Spring Data Couchbase¶
Couchbase blurs the line between document storage and a richer distributed data platform. It is especially useful when you want JSON documents, flexible indexing, and a lower-latency data layer without giving up the convenience of Spring abstractions.
In Spring, you usually work with Couchbase in one of two ways:
- through Spring Data Couchbase repositories and templates
- directly with the Java SDK's
ClusterandReactiveCollectionwhen you need a more explicit control flow
The sample here focuses on the WebFlux-friendly path and demonstrates the patterns you would use in a real application without adding unnecessary ceremony.
Getting started¶
The quickest way to start is to generate a project from Spring Initializr, as described in the Getting Started chapter. Select Spring WebFlux and Spring Data Couchbase (Reactive).
If you are adding Couchbase to an existing project, include the following dependency in your pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-couchbase-reactive</artifactId>
</dependency>
If you are building a plain Spring project without Boot auto-configuration, use the following dependencies instead:
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-couchbase</artifactId>
</dependency>
<dependency>
<groupId>com.couchbase.client</groupId>
<artifactId>java-client</artifactId>
</dependency>
In that case, you also need a configuration class extending AbstractCouchbaseConfiguration, with @EnableReactiveCouchbaseRepositories to enable reactive Repository support:
@Configuration
@EnableReactiveCouchbaseRepositories(basePackageClasses = {CouchbaseConfig.class})
public class CouchbaseConfig extends AbstractCouchbaseConfiguration {
@Value("${couchbase.user}")
private String user;
@Value("${couchbase.password}")
private String password;
@Value("${couchbase.connection-string}")
private String connectionString;
@Value("${couchbase.bucket-name}")
private String bucket;
@Override
public String getConnectionString() {
return this.connectionString;
}
@Override
public String getUserName() {
return this.user;
}
@Override
public String getPassword() {
return this.password;
}
@Override
public String getBucketName() {
return this.bucket;
}
}
You can explore this setup in data-couchbase.
With Spring Boot, the connection is usually configured automatically from application.properties:
spring.couchbase.connection-string=couchbase://localhost
spring.couchbase.username=Administrator
spring.couchbase.password=password
spring.data.couchbase.bucket-name=default
Defining the document model¶
Create a Post class that maps to a Couchbase document:
@Data
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Document
class Post {
@Id
@GeneratedValue(strategy = GenerationType.UNIQUE)
private String id;
private String title;
@Field("content")
private String content;
}
The annotations in this example are straightforward:
@Documentmarks the class as a Couchbase document.@Iddesignates the document key;@GeneratedValue(strategy = GenerationType.UNIQUE)lets Spring Data generate a unique key for you.@Fieldmaps a differently named field in the document.
Creating a repository¶
Define a repository interface by extending ReactiveCouchbaseRepository:
@ViewIndexed(designDoc = "post", viewName = "all")
interface PostRepository extends ReactiveCouchbaseRepository<Post, String> {}
Once Spring creates the repository bean, you can inject it into controllers or services.
Exposing a REST API¶
A small controller is enough to expose a reactive CRUD API:
@RestController
@RequestMapping("/posts")
class PostController {
private final PostRepository posts;
public PostController(PostRepository posts) {
this.posts = posts;
}
@GetMapping("")
public Flux<Post> all() {
return this.posts.findAll();
}
@PostMapping("")
public Mono<Post> create(@RequestBody Post post) {
post.setId(UUID.randomUUID().toString());
return this.posts.save(post);
}
@GetMapping("/{id}")
public Mono<Post> get(@PathVariable("id") String id) {
return this.posts.findById(id);
}
@PutMapping("/{id}")
public Mono<Post> update(@PathVariable("id") String id, @RequestBody Post post) {
return this.posts.findById(id)
.map(p -> {
p.setTitle(post.getTitle());
p.setContent(post.getContent());
return p;
})
.flatMap(p -> this.posts.save(p));
}
@DeleteMapping("/{id}")
public Mono<Void> delete(@PathVariable("id") String id) {
return this.posts.deleteById(id);
}
}
Seeding sample data¶
You can use a CommandLineRunner to insert a few sample records when the application starts:
public void init() {
log.info("start data initialization ...");
this.posts
.deleteAll()
.thenMany(
Flux
.just("Post one", "Post two")
.flatMap(
title -> this.posts.save(Post.builder().title(title).content("content of " + title).build())
)
)
.log()
.subscribe(
null,
null,
() -> log.info("done initialization...")
);
}
CommandLineRunner ensures the initialization logic runs once the application has started.
Running the application¶
Before starting the app, make sure Couchbase is running locally.
If you prefer Docker, start Couchbase with the Docker Compose file in the repository root:
Then open http://localhost:8091 to create the administrator user account and bucket, and launch the application:
You can test the API with curl:
Working with the native client¶
The spring-boot-starter-couchbase starter auto-configures a Couchbase Cluster bean. By obtaining the collection reference, you can perform document operations directly — call .reactive() on the collection to obtain a ReactiveCollection; the SDK exposes it natively, so no additional reactive driver is needed.
Project setup¶
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-couchbase</artifactId>
</dependency>
Implementing a repository with ReactiveCollection¶
@Component
@RequiredArgsConstructor
@Slf4j
public class ProductRepository {
private final Cluster cluster;
private ReactiveCollection productCollection;
@PostConstruct
public void init() {
this.productCollection = this.cluster
.bucket("demo")
.defaultCollection()
.reactive();
}
public Mono<Product> save(Product product) {
String id = product.id() != null ? product.id() : UUID.randomUUID().toString();
return this.productCollection.upsert(id, product)
.mapNotNull(result -> {
log.debug("saving product result: {}", result);
return new Product(id, product.name(), product.price());
});
}
}
Testing with Testcontainers¶
Define a CouchbaseContainer bean with Spring Boot's @ServiceConnection — the bucket and credentials are provisioned for the ephemeral server:
@TestConfiguration(proxyBeanMethods = false)
class TestcontainersConfiguration {
@Bean
@ServiceConnection
public CouchbaseContainer couchbaseContainer() {
return new CouchbaseContainer("couchbase/server")
.withCredentials("Administrator", "password")
.withBucket(new BucketDefinition("demo").withPrimaryIndex(true))
.withStartupAttempts(5)
.withStartupTimeout(Duration.ofSeconds(120));
}
}
You can explore the native-client example in boot-couchbase, and the repository-based one in boot-data-couchbase.