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
BasicAuthenticationProvider
with 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:
@Primary
and@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
}
-
Define API Contract in
api/spec/main.tsp
:@route("/pets") interface Pets { @get list(): Pet[]; @post create(@body pet: CreatePetRequest): Pet; @get("{id}") get(@path id: int64): Pet; }
-
Generate OpenAPI Spec:
cd api/spec npm install npm run build # Generates: api/spec/tsp-output/@typespec/openapi3/openapi.yaml
-
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
- 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:run
When 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_SECRET
env var) - CORS: Development-friendly CORS settings
- Logging: Debug level enabled for
io.seqera
packages
- 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 typespec:compile
# 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/releases
After 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:generateApiCode
after 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
PetsApiSpec
interface from generated code - AuthController: Handles login/logout, dashboard rendering
- Security Annotations:
@Secured
for authentication requirements
- PetRepository: Abstract base with common CRUD operations
- PostgresPetRepository: PostgreSQL-specific implementation
- H2PetRepository: H2-specific implementation with
@Primary
for local development
- Pet: Domain entity with Micronaut Data annotations
- Audit Fields:
@DateCreated
,@DateUpdated
for 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_SECRET
environment 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