A comprehensive Pet Shop API demonstrating modern Java/Micronaut application best practices with multi-module structure, TypeSpec-driven API development, JWT authentication, and production-ready patterns.
This project demonstrates a clean, maintainable multi-module architecture with API-first design:
app-bootstrap/
βββ api/ # API definitions and model objects
β βββ src/main/java/
β β βββ io/seqera/api/
β β βββ model/ # Generated Request/Response DTOs
β βββ spec/ # TypeSpec API definitions (source of truth)
β βββ main.tsp # Main API specification
β βββ package.json # TypeSpec build configuration
βββ app/ # Main application implementation
β βββ src/main/
β βββ java/
β β βββ io/seqera/api/
β β βββ spec/ # Generated Controller interfaces
β βββ groovy/
β βββ io/seqera/app/
β βββ controller/ # API implementation
β βββ service/ # Business logic layer
β βββ repository/ # Data access layer
β βββ entity/ # Domain entities
β βββ security/ # Authentication providers
βββ buildSrc/ # Custom Gradle plugins
βββ docs/ # API documentation
- TypeSpec Source:
spec/main.tsp- Single source of truth for API definitions - Generated Models:
src/main/java/io/seqera/api/model/- DTOs for request/response
- Generated Interfaces:
src/main/java/io/seqera/api/spec/- Controller interfaces - Entities:
entity/Pet.groovy- Domain models with Micronaut Data annotations - Repositories:
repository/PetRepository.groovy- Data access with multiple database support - Services: Business logic layer (currently handled by controllers)
- Controllers:
controller/PetController.groovy- API implementation,AuthController.groovy- Authentication handling - Security:
security/BasicAuthenticationProvider.groovy- JWT authentication
- Framework: Micronaut 4.9.7 with Groovy
- Authentication: JWT with HTTP-only cookies
- Database: PostgreSQL (production) + H2 (local development)
- Data Access: Micronaut Data JDBC with multiple dialect support
- API Documentation: TypeSpec β OpenAPI β Swagger UI
- Security: JWT-based authentication with cookie storage
- Build: Gradle with custom convention plugins
- Containerization: Google Jib for optimized container images
- Authentication Provider: Custom
BasicAuthenticationProviderwith hardcoded credentials - Token Storage: HTTP-only cookies for security
- Session Management: JWT tokens with configurable expiration
- Protected Endpoints: All
/pets/**endpoints require authentication - Public Endpoints: Login pages, static resources, API documentation
- Login Flow: POST to
/loginβ JWT cookie set β Redirect to/dashboard - Logout Flow: POST to
/logoutβ Cookie cleared β Redirect to/ - Authentication Mode: Cookie-based (not session-based)
- JWT Secret: 256-bit minimum requirement (configured in
application.yml)
- Admin:
username: admin,password: password - User:
username: user,password: secret
- PostgreSQL: Production database with full SQL features
- H2: Local development with PostgreSQL compatibility mode
- Repository Abstraction:
@Primaryand@Requires(env = "h2")annotations for environment-specific implementations
@MappedEntity("pets")
class Pet {
@Id @GeneratedValue Long id
String name, species, breed, color, description
Integer age
Boolean isAvailable
@DateCreated OffsetDatTime createdAt
@DateUpdated OffsetDatTime updatedAt
}The API uses TypeSpec's native versioning with the @versioned decorator for type-safe API evolution:
-
Define API Contract in
api/spec/main.tspwith versioning:@service(#{ title: "Pet Shop API", }) @versioned(Versions) namespace PetShop { enum Versions { @doc("1.0.0") v1_0: "1.0.0", } @route("/pets") interface Pets { @get list(): Pet[]; @post create(@body pet: CreatePetRequest): Pet; @get("{id}") get(@path id: int64): Pet; } }
-
Generate Versioned OpenAPI Specs:
cd api/spec npm install npm run build # Generates: api/build/openapi/openapi-1.0.0.yaml (versioned) # Generates: api/build/openapi/openapi-latest.yaml (highest version)
-
Generate Java Code:
./gradlew :app:generateApiCode # Generates interfaces in app/src/main/java/io/seqera/api/spec/ # Generates DTOs in api/src/main/java/io/seqera/api/model/
-
Implement Controllers: Controllers implement the generated interfaces
This project uses TypeSpec's native @versioned decorator for type-safe API evolution:
- Version Definition: Versions are defined in the
Versionsenum using semantic versioning - TypeSpec Compilation: Produces
openapi-{version}.yamlfiles (one per version) - Latest File: Bash oneliner finds highest version and creates
openapi-latest.yaml - Backward Compatibility: Existing consumers can continue using
openapi-latest.yaml
// In api/spec/main.tsp
enum Versions {
@doc("1.0.0") v1_0: "1.0.0", // Current version
@doc("1.1.0") v1_1: "1.1.0", // New version with added features
@doc("2.0.0") v2_0: "2.0.0", // Breaking changes
}
// Adding version-specific features
interface PetApi {
@get list(): Pet[];
// New field in v1.1
@added(Versions.v1_1)
@get("/categories") getCategories(): PetCategory[];
// Breaking change in v2.0
@renamedFrom(Versions.v2_0, "list")
@get("/all") getAllPets(): Pet[];
}api/build/openapi/openapi-1.0.0.yaml- Version-specific API specapi/build/openapi/openapi-1.1.0.yaml- New version with added featuresapi/build/openapi/openapi-latest.yaml- Always contains highest version
- Swagger UI: Automatically generated at
/openapi/index.html - OpenAPI Spec: Available at
/openapi/openapi-latest.yaml - Versioned Specs: Versioned files like
/openapi/openapi-1.0.0.yaml - WebJars Integration: Uses Swagger UI WebJar for consistent styling
- Java 21 (with compatibility for Java 17)
- PostgreSQL 15+ (for production mode)
- Node.js (for TypeSpec compilation)
# Run with H2 in-memory database
./gradlew :app:run -PmicronautEnvs=h2
# Access the application
open http://localhost:8080/# Start PostgreSQL with Docker
docker run -d \
--name postgres-pet-shop \
-e POSTGRES_DB=app_bootstrap \
-e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=postgres \
-p 5432:5432 \
postgres:15
# Run the application
./gradlew :app:runWhen running locally, the application:
- H2 Mode: Uses in-memory database, data is lost on restart
- PostgreSQL Mode: Requires external database, data persists
- Authentication: Uses hardcoded credentials (not suitable for production)
- JWT Secret: Uses default secret (should be overridden via
JWT_SECRETenv var) - CORS: Development-friendly CORS settings
- Logging: Debug level enabled for
io.seqerapackages
- Application: http://localhost:8080/
- Login Page: http://localhost:8080/login
- Dashboard: http://localhost:8080/dashboard (requires authentication)
- API Documentation: http://localhost:8080/openapi/index.html
- Pet API: http://localhost:8080/pets (requires authentication)
# Login and save cookies
curl -c cookies.txt -X POST http://localhost:8080/login \
-d "username=admin&password=password" \
-H "Content-Type: application/x-www-form-urlencoded"
# Access protected Pet API
curl -b cookies.txt http://localhost:8080/pets
# Create a new pet
curl -b cookies.txt -X POST http://localhost:8080/pets \
-H "Content-Type: application/json" \
-d '{
"name": "Fluffy",
"species": "Cat",
"breed": "Persian",
"age": 3,
"color": "White",
"description": "Friendly and playful"
}'# Generate OpenAPI spec from TypeSpec source
./gradlew :api:generateOpenApi
# Generate Swagger UI for interactive documentation
./gradlew :app:generateSwaggerUI
# Access generated documentation
open http://localhost:8080/openapi/index.html# Complete API code generation pipeline (recommended)
./gradlew :app:generateApiCode
# This task performs the following steps:
# 1. Generates OpenAPI spec from TypeSpec source (api/spec/main.tsp)
# 2. Generates Java interfaces for controllers (app/src/main/java/io/seqera/api/spec/)
# 3. Generates DTOs/model classes (api/src/main/java/io/seqera/api/model/)
# 4. Copies generated code to appropriate modules# Step 1: Generate OpenAPI specification
cd api/spec
npm install
npm run build
# Step 2: Generate server-side interfaces and models
./gradlew :app:generateServerOpenApiApis # Controller interfaces
./gradlew :app:generateServerOpenApiModels # DTO classes
# Step 3: Copy generated code to proper locations
./gradlew :app:generateApiCode # Automated copy task# Publish API client to local Maven repository
./gradlew :api:publishToMavenLocal
# The API client will be available as:
# Group ID: io.seqera
# Artifact ID: api
# Version: [from VERSION file]# Publish to Seqera Maven repository (requires AWS credentials)
export AWS_ACCESS_KEY_ID=your_access_key
export AWS_SECRET_ACCESS_KEY=your_secret_key
export PUBLISH_REPO_URL=s3://maven.seqera.io/releases # or /snapshots
# Publish the API client
./gradlew :api:publish
# Alternative: Set credentials via Gradle properties
./gradlew :api:publish \
-Paws_access_key_id=your_access_key \
-Paws_secret_access_key=your_secret_key \
-Ppublish_repo_url=s3://maven.seqera.io/releasesAfter publishing, other projects can consume the API client:
// In consuming project's build.gradle
dependencies {
implementation 'io.seqera:api:1.0.0'
}// Using the generated models
import io.seqera.api.model.Pet;
import io.seqera.api.model.CreatePetRequest;
CreatePetRequest request = new CreatePetRequest()
.name("Fluffy")
.species("Cat")
.breed("Persian")
.age(3);api/src/main/java/io/seqera/api/
βββ model/ # Generated DTOs
βββ Pet.java # Pet entity model
βββ CreatePetRequest.java # Create request DTO
βββ UpdatePetRequest.java # Update request DTO
βββ ...
app/src/main/java/io/seqera/api/
βββ spec/ # Generated controller interfaces
βββ PetsApiSpec.java # Pet API interface
βββ AuthApiSpec.java # Auth API interface
βββ ...
- API-First Development: Always start by updating
api/spec/main.tsp - Code Generation: Run
./gradlew :app:generateApiCodeafter TypeSpec changes - Implementation: Implement generated interfaces in controller classes
- Testing: Test both API contracts and implementations
- Publishing: Publish API client for external consumers
# Typical development cycle
vim api/spec/main.tsp # 1. Update API specification
./gradlew :app:generateApiCode # 2. Generate code
vim app/src/main/groovy/... # 3. Implement controllers
./gradlew test # 4. Run tests# Run all tests
./gradlew test
# Run with H2 database
./gradlew :app:test
# Run specific test suites
./gradlew :app:test --tests "*PetRepositorySpec"
./gradlew :app:test --tests "*AuthenticationSpec"# Build application
./gradlew build
# Build container image
./gradlew :app:jibDockerBuild
# Run linting and type checking
./gradlew :app:compileGroovy- PetController: Implements
PetsApiSpecinterface from generated code - AuthController: Handles login/logout, dashboard rendering
- Security Annotations:
@Securedfor authentication requirements
- PetRepository: Abstract base with common CRUD operations
- PostgresPetRepository: PostgreSQL-specific implementation
- H2PetRepository: H2-specific implementation with
@Primaryfor local development
- Pet: Domain entity with Micronaut Data annotations
- Audit Fields:
@DateCreated,@DateUpdatedfor automatic timestamps - Validation: Bean validation annotations for data integrity
- BasicAuthenticationProvider: Implements authentication logic
- JWT Configuration: Cookie-based JWT with proper security headers
- Route Protection: Security rules in
application.yml
For detailed component documentation, see the JavaDoc in each class:
- PetController: RESTful pet management endpoints
- PetRepository: Data access patterns and database abstraction
- BasicAuthenticationProvider: Authentication flow and JWT handling
- Pet Entity: Domain model and data validation rules
application.yml: Base configurationapplication-h2.yml: H2 database settings for local development- Security Settings: JWT configuration, authentication providers
- Database: Multiple datasource support with environment switching
- JWT Secret: Override
JWT_SECRETenvironment variable - Database: Configure proper PostgreSQL connection
- Authentication: Replace hardcoded credentials with proper user management
- CORS: Restrict CORS settings for production domains
- Logging: Adjust log levels for production monitoring