Expert guidance for working with CurioKeep's Spring Boot backend, module system, and metadata providers
Expert assistant for developing and maintaining CurioKeep, a self-hosted web application for cataloging physical collections with extensible modules and metadata import capabilities.
CurioKeep is a Spring Boot 4 application (Java 21) with a bundled Vite/React TypeScript SPA. The frontend is packaged into the JAR via Maven profile and served through custom Spring configuration.
1. **Start Database**
- Run `docker compose up db` to start PostgreSQL
- Database configuration uses environment variables: `DB_HOST`, `DB_PORT`, `DB_NAME`, `DB_USER`, `DB_PASS`
- Defaults are in `src/main/resources/application.yml`
2. **Run Backend Locally**
- Execute `./mvnw spring-boot:run`
- Flyway migrations run automatically on startup
- Module definitions are loaded from `classpath*:modules/*.xml`
3. **Build Full Production Artifact**
- Run `./mvnw clean package -Pfrontend`
- Maven wrapper auto-manages Node/npm for frontend build
- SPA is bundled into the JAR and served via `SpaWebConfig.java`
4. **Frontend Development Mode**
- Navigate to `frontend/` directory
- Run `npm ci` then `npm run dev`
- Vite dev server runs independently for hot reload
Modules are XML contracts that define collection types, fields, states, workflows, and metadata provider integrations. The system:
1. **Scans** classpath for `*.xml` files in `modules/` at startup
2. **Validates** against XSD schema
3. **Parses** XML into intermediate representation
4. **Compiles** to JSON contract
5. **Validates** JSON against `module-contract-v1.schema.json`
6. **Performs** semantic checks (state/field uniqueness, provider references)
7. **Upserts** module_definition, module_state, and module_field database rows
8. **Uses checksums** to avoid redundant writes
Key files:
When authoring or modifying modules, you **MUST** respect these hard rules:
1. **OWNED State Requirement**
- Every module must define an `OWNED` state
- This is the default state for new items
2. **Key Uniqueness**
- State keys must be unique within a module
- Field keys must be unique within a module
- Keys are normalized (typically uppercase)
3. **Provider Mapping Paths**
- Must start with `/`
- Must reference declared providers in the module
- Keys must match provider response structure exactly
4. **Workflow References**
- Must only reference declared fields
- Must only reference declared providers
- Invalid references abort startup
5. **Validation Failures**
- Any validation error aborts application startup
- Errors are aggregated and reported clearly
- Fix all errors before attempting restart
Example module: `src/main/resources/modules/books.xml`
Documentation: `src/main/resources/docs/modules.md`
1. Create `src/main/resources/modules/your-module.xml`
2. Define module metadata (key, name, description)
3. Declare states (must include `OWNED`)
4. Declare fields with types and constraints
5. Configure provider integrations (optional)
6. Define workflows for metadata import (optional)
7. Validate XML against XSD before testing
8. Restart application - module loads automatically
9. Check logs for validation errors
Providers fetch metadata from external APIs and normalize it into module fields. Each provider:
Provider implementations: `src/main/java/org/rostislav/curiokeep/providers/impl/`
Configure in `application.yml`:
```yaml
curiokeep:
providers:
google-books:
api-key: ${GOOGLE_BOOKS_API_KEY:}
open-library:
enabled: true
```
Missing credentials cause provider to be skipped at runtime (not an error).
Provider tests are located in `src/test/java/org/rostislav/curiokeep/providers/`
Test approach:
Flyway migrations in `src/main/resources/db/migration/`:
1. **module_definition** - Compiled JSON module contracts
2. **module_state** - State definitions per module
3. **module_field** - Field definitions per module
4. **collection** - User collections with module references
5. **item** - Collection items with JSONB attributes
6. **users** - User accounts and authentication
Items store flexible attributes in JSONB column `item.attributes`. Structure is validated against module field definitions. The `item.state_key` is a foreign key into `module_state`.
`src/main/java/org/rostislav/curiokeep/items/ItemService.java`
Responsibilities:
`src/main/java/org/rostislav/curiokeep/collections/CollectionService.java`
Responsibilities:
1. Edit the module XML (`src/main/resources/modules/*.xml`)
2. Add field definition with type and constraints
3. If used in provider mapping, add to `<provider>` mapping paths
4. Restart application to reload module
5. Verify compilation in logs
6. Update frontend components to display new field
1. Create implementation in `src/main/java/org/rostislav/curiokeep/providers/impl/`
2. Implement `MetadataProvider` interface
3. Add configuration to `application.yml`
4. Create test class using `MockRestServiceServer`
5. Update module XML with provider mapping
6. Document provider in `src/main/resources/docs/`
1. Check application startup logs for validation errors
2. Verify XML against XSD schema
3. Ensure JSON contract validates against schema
4. Check for duplicate state/field keys
5. Verify provider mapping paths start with `/`
6. Confirm all workflow references exist
7. Test with minimal module to isolate issue
Before committing changes:
1. **Module Changes**
- Validate XML against XSD
- Ensure `OWNED` state exists
- Verify no duplicate keys
- Check provider mappings reference declared providers
- Confirm workflows reference declared fields
2. **Provider Changes**
- Update corresponding tests
- Verify field normalization matches module mappings
- Test with mocked HTTP responses
- Document any new configuration requirements
3. **Schema Changes**
- Create Flyway migration if needed
- Ensure `item.state_key` FK constraints remain valid
- Update `ItemService` validation if adding required fields
- Test migration on clean database
4. **Build**
- Run `./mvnw test` to verify all tests pass
- Build with frontend: `./mvnw clean package -Pfrontend`
- Verify application starts successfully
- Check logs for module load errors
1. **Module Load Failures Block Startup**
- Always validate XML and JSON before deploying
- Module validation errors are fatal
- Check logs for aggregated error messages
2. **State Key Constraints**
- State keys must exist in `module_state` table
- `ItemService` enforces state key validation
- Normalized to uppercase automatically
3. **Provider Mapping Mismatches**
- Mapping keys must exactly match provider response fields
- Paths must start with `/`
- Missing mappings result in null values (not errors)
4. **Required Field Validation**
- `ItemService` validates required fields on create/update
- Adding required fields to existing modules breaks existing items
- Consider migration strategy for required field additions
5. **Frontend Bundle Issues**
- Frontend must be built with `-Pfrontend` profile
- `SpaWebConfig.java` serves SPA from classpath
- Verify `frontend/dist` is packaged in JAR
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/curiokeep-development-assistant/raw