Dynapi is a dynamic schema-driven backend API built with Spring Boot, MongoDB, Kafka, and JWT.
The main idea:
- Define fields and field groups at runtime.
- Accept dynamic form payloads based on those definitions.
- Query stored records with filters, pagination, and sorting.
- Base URL:
http://localhost:8080/api - OpenAPI JSON:
http://localhost:8080/api/api-docs - Swagger UI:
http://localhost:8080/api/swagger-ui.html
- Java 21
- Docker Desktop
- Maven Wrapper (
./mvnw)
MongoDB:
docker run -d --name dynapi-mongo -p 27017:27017 mongo:7Kafka (KRaft single-node):
docker run -d --name dynapi-kafka -p 9092:9092 \
-e KAFKA_NODE_ID=1 \
-e KAFKA_PROCESS_ROLES=broker,controller \
-e KAFKA_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093 \
-e KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://localhost:9092 \
-e KAFKA_CONTROLLER_LISTENER_NAMES=CONTROLLER \
-e KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT \
-e KAFKA_CONTROLLER_QUORUM_VOTERS=1@localhost:9093 \
-e KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1 \
-e KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR=1 \
-e KAFKA_TRANSACTION_STATE_LOG_MIN_ISR=1 \
-e KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS=0 \
-e KAFKA_AUTO_CREATE_TOPICS_ENABLE=true \
-e CLUSTER_ID=MkU3OEVBNTcwNTJENDM2Qk \
confluentinc/cp-kafka:7.6.1./mvnw spring-boot:runOptional smoke check:
curl -i http://localhost:8080/api/api-docs./mvnw -q clean test- Admin schema routes (
/api/admin/**) require JWT withADMINrole. - Public routes (
/api/form,/api/query/**) are currently open. - Local/dev token issuer is available at
POST /api/dev/auth/tokenwhendynapi.dev-auth.enabled=true.
Issue a local admin token:
export BASE_URL="http://localhost:8080/api"
export ADMIN_TOKEN=$(curl -s -X POST "$BASE_URL/dev/auth/token" \
-H "Content-Type: application/json" \
-d '{"subject":"local-admin","roles":["ADMIN"],"ttlSeconds":3600}' | jq -r '.data.token')If you need manual generation instead, JWT settings are:
alg:HS256roles: includeADMIN- signing secret (decoded text):
dynapi-dev-secret-key-change-me-1234567890
Create title field:
curl -s -X POST "$BASE_URL/admin/schema/field-definitions" \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"fieldName": "title",
"type": "STRING",
"required": true,
"version": 1
}'Create priority field:
curl -s -X POST "$BASE_URL/admin/schema/field-definitions" \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"fieldName": "priority",
"type": "NUMBER",
"required": false,
"version": 1
}'This group maps to Mongo collection tasks:
curl -s -X POST "$BASE_URL/admin/schema/field-groups" \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "task-form",
"entity": "tasks",
"fieldNames": ["title", "priority"],
"version": 1
}'Submit and query now use only PUBLISHED schema snapshots.
Publish current group schema:
curl -s -X POST "$BASE_URL/admin/schema/field-groups/task-form/publish" \
-H "Authorization: Bearer $ADMIN_TOKEN"Check version history:
curl -s "$BASE_URL/admin/schema/entities/tasks/versions" \
-H "Authorization: Bearer $ADMIN_TOKEN"Rollback to a previous schema snapshot (optional maintenance action):
curl -s -X POST "$BASE_URL/admin/schema/entities/tasks/rollback/1" \
-H "Authorization: Bearer $ADMIN_TOKEN"curl -s -X POST "$BASE_URL/form" \
-H "Content-Type: application/json" \
-d '{
"group": "task-form",
"data": {
"title": "Ship v1",
"priority": 1
}
}'curl -s -X POST "$BASE_URL/query/tasks" \
-H "Content-Type: application/json" \
-d '{
"filters": [
{ "field": "priority", "operator": "gte", "value": 1 }
],
"page": 0,
"size": 10,
"sortBy": "priority",
"sortDirection": "DESC"
}'Supported filter operators include:
eq,ne,gt,lt,gte,lte,in,regex,and,or,not
POST /api/dev/auth/tokenissue local/dev JWT (feature-flagged bydynapi.dev-auth.enabled)POST /api/formsubmit dynamic data by groupPOST /api/forms/{groupId}/submitsubmit dynamic data with group in pathPOST /api/query/{entity}query dynamic recordsGET/POST/PUT/DELETE /api/admin/schema/field-definitions*manage fieldsGET/POST/PUT/DELETE /api/admin/schema/field-groups*manage groupsPOST /api/admin/schema/field-groups/{groupId}/publishpublish immutable schema snapshotGET /api/admin/schema/entities/{entity}/versionslist schema versionsPOST /api/admin/schema/entities/{entity}/rollback/{version}rollback to a previous schema snapshotPOST /api/admin/schema/entities/{entity}/deprecatedeprecate latest published schema
Main config file: src/main/resources/application.yml
Key settings:
- MongoDB:
spring.data.mongodb.* - Kafka:
spring.kafka.* - JWT secret:
security.jwt.secret(base64-encoded key) - Context path:
server.servlet.context-path=/api
Test config: src/test/resources/application-test.yml
Team progress is tracked in GitHub (Issues/Projects), not local AI notes.
- Open work as GitHub Issues (
Feature,Bug,Task) - Move issue status in GitHub Projects
- Every PR must link an issue (
Closes #...,Fixes #...,Refs #...)
Enforcement in this repo:
- PR template:
.github/pull_request_template.md - Required check:
.github/workflows/require-issue-link.yml
src/main/java/com/dynapi/controlleractive REST controllerssrc/main/java/com/dynapi/serviceapplication servicessrc/main/java/com/dynapi/domaindomain models and validatorssrc/main/java/com/dynapi/repositoryMongo repositoriessrc/main/java/com/dynapi/infrastructuremessaging integrationsrc/main/resourcesapp config and messagessrc/testintegration and context tests