Spring Boot with PostgreSQL and JPA
Create a Java API project with Spring Boot, PostgreSQL, Maven, JPA, JUnit 5, and optional Spring libraries using Better Fullstack.
Updated 2026-05-12
Use this stack when you want a Java API with the Spring ecosystem and relational persistence.
npm create better-fullstack@latest my-spring-api -- \
--ecosystem java \
--java-web-framework spring-boot \
--java-build-tool maven \
--database postgres \
--java-orm spring-data-jpa \
--java-testing-libraries junit5What this creates
- A Java project using Spring Boot.
- Maven as the build tool.
- PostgreSQL as the database option.
- JPA for persistence.
- JUnit 5 for testing.
Generated shape
The scaffold is a conventional Spring Boot service with wrapper scripts, source sets, a health controller, and JPA-backed sample user endpoints. The package name is derived from the project name under com.example.
my-spring-api/
├── mvnw
├── .mvn/
├── pom.xml
├── src/main/java/com/example/myspringapi/
│ ├── Application.java
│ ├── controller/
│ │ ├── HealthController.java
│ │ └── UserController.java
│ ├── domain/
│ │ └── AppUser.java
│ ├── repository/
│ │ └── AppUserRepository.java
│ └── service/
│ └── AppUserService.java
├── src/main/resources/
│ └── application.yml
└── src/test/java/com/example/myspringapi/
└── ApplicationTests.javaThe generated local JPA datasource uses an H2 file database in PostgreSQL compatibility mode. Treat --database postgres as the target database selection and switch the datasource URL, driver, credentials, and JDBC dependency for a real PostgreSQL environment.
When to choose it
Choose Spring Boot when you want a conventional Java backend with a large ecosystem, common enterprise integrations, and predictable application structure.
Choose this guide when you want a minimal Spring Boot API with persistence but not security and migration tooling on day one. Choose the secure Spring guide when every endpoint should start behind Spring Security and integration testing with Testcontainers is part of the baseline.
| Decision point | Minimal Spring JPA | Secure Spring API |
|---|---|---|
| Build tool | Maven | Gradle |
| Security | Not selected | Spring Security selected |
| Database changes | Hibernate ddl-auto local loop | Flyway migration path |
| Tests | JUnit 5 | JUnit 5 plus Testcontainers |
| Best for | Learning, prototypes, internal CRUD | Production-oriented API baseline |
Representative snippets
The generated domain model is a standard JPA entity. Add fields here when they belong to the persisted aggregate.
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import java.time.Instant;
@Entity
public class AppUser {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String email;
private String displayName;
private Instant createdAt = Instant.now();
protected AppUser() {
}
public AppUser(String email, String displayName) {
this.email = email;
this.displayName = displayName;
}
}Repositories stay small because Spring Data JPA derives common queries from method names.
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
public interface AppUserRepository extends JpaRepository<AppUser, Long> {
Optional<AppUser> findByEmail(String email);
}The controller exposes GET /users and POST /users through the service layer.
@RestController
@RequestMapping("/users")
public class UserController {
private final AppUserService userService;
public UserController(AppUserService userService) {
this.userService = userService;
}
@GetMapping
public List<UserResponse> listUsers() {
return userService.findAll().stream().map(UserResponse::from).toList();
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public UserResponse createUser(@RequestBody CreateUserRequest request) {
AppUser createdUser = userService.create(request.email(), request.displayName());
return UserResponse.from(createdUser);
}
}Migrations and tests
This minimal stack does not select Flyway or Liquibase. Hibernate can keep the local H2 schema moving during early development, but add a migration library before multiple environments depend on the database shape.
./mvnw test
./mvnw spring-boot:run
./mvnw packageWhen adding production PostgreSQL, move from local defaults to environment-driven datasource settings:
spring:
datasource:
url: ${DATABASE_URL}
username: ${DATABASE_USERNAME}
password: ${DATABASE_PASSWORD}
jpa:
hibernate:
ddl-auto: validateCompatibility notes
--java-orm spring-data-jparequires Spring Boot and a Java build tool.- Flyway and Liquibase are tied to the Spring Data JPA path in Better Fullstack.
- This guide uses
--java-build-tool maven; use Maven wrapper commands unless you intentionally regenerate with Gradle. - The local generated datasource and dependency are H2 even though this stack targets PostgreSQL. Add the PostgreSQL JDBC driver and override
spring.datasource.*before deploying against a managed Postgres database.
Deployment notes
Build the jar with Maven, provide Java 21 or newer, and run the application with environment-specific datasource settings.
./mvnw package
java -jar target/*.jarFor production, add migrations, add the PostgreSQL JDBC driver, configure connection pooling through Spring Boot datasource properties, and avoid relying on ddl-auto: update. Expose /health through your platform health check.
Troubleshooting
| Symptom | Check |
|---|---|
| App starts with H2 instead of Postgres | Override spring.datasource.* in environment-specific config. |
POST /users accepts incomplete input | Add --java-libraries spring-validation in a future scaffold or add Bean Validation manually. |
| JPA entities do not appear | Keep entities under the generated application package so component scanning finds them. |
| Maven commands fail | Use ./mvnw, not a globally installed Maven with a different version. |
Tradeoffs
Spring Boot is heavier than minimal API frameworks, but it pays off when you need established conventions, library depth, and long-lived backend maintainability.
Next steps
- Open the Stack Builder.
- Read the Java ecosystem docs.
- Review the CLI create reference.