Spring Boot + PostgreSQL + Node.js integration layer for indexing and serving IOTA on-chain container data.
graph LR
FE[React dApp]
API[Spring Boot API]
WS[STOMP WebSocket]
DB[(PostgreSQL)]
NODE[Node Scripts]
IOTA[(IOTA Network)]
FE -->|REST /izipublish/api/*| API
FE -->|ws://.../izipublish/ws-sync| WS
WS --> API
API --> DB
API -->|ProcessBuilder| NODE
NODE --> IOTA
API -->|Sync + indexing| IOTA
- Indexes on-chain objects (
container,data_type,data_item,owner,owner_audit,data_item_verification,child). - Stores normalized data in PostgreSQL through JPA entities/repositories.
- Exposes REST endpoints for item trees, auxiliary browse data, details, user data, sync status, link graphs, and report generation.
- Pushes sync status to clients every 5 seconds through STOMP (
/topic/sync-status). - Parses
easy_publishJSON blocks from on-chain content into local publish/report tables.
.
├── src/main/java/com/easypublish
│ ├── batch/ # Sync + indexing logic
│ ├── controller/ # REST controllers
│ ├── dtos/ # API DTO models
│ ├── entities/ # JPA entities (onchain/offchain/parsed)
│ ├── repositories/ # Spring Data repositories
│ ├── service/ # Query/sync/report services
│ └── websocket/sync/ # STOMP configuration + emitter
├── src/main/resources
│ ├── application.properties # Main runtime config
│ ├── application-test.properties
│ ├── templates/ # HTML + PDF templates
│ └── static/ # Static assets
├── node/ # Node scripts for IOTA object fetches
├── react/iota-dapp_backup_20260323_224441/ # Frontend app source
└── pom.xml # Maven build
| Tool | Version Used By Project |
|---|---|
| Java | 25 |
| Maven | 3.9+ |
| Node.js | 24.1.0 |
| PostgreSQL | 14+ recommended |
GitHub Actions CI also uses Node 24.1.0 and JDK 25.
cd node
npm install
cd ..Default local settings in src/main/resources/application.properties:
spring.datasource.url=jdbc:postgresql://localhost:5432/devdb
spring.datasource.username=dev
spring.datasource.password=devCreate the DB/user (example):
CREATE DATABASE devdb;
CREATE USER dev WITH PASSWORD 'dev';
GRANT ALL PRIVILEGES ON DATABASE devdb TO dev;mvn spring-boot:runDefault base URL:
http://localhost:8084/izipublish
cd react/iota-dapp_backup_20260323_224441
npm install
npm run dev| Key | Purpose | Default |
|---|---|---|
server.port |
Backend port | 8084 |
server.servlet.context-path |
API root prefix | /izipublish |
sync.chain.last |
Update-chain object id used by sync status | configured in file |
sync.chain.DataItem |
Data-item chain object id used by sync status | configured in file |
| Key | Example |
|---|---|
app.cors.allowed-origins |
http://localhost:5173,https://izipublish.com,https://cars.izipublish.com |
app.cors.allowed-methods |
GET,POST,PUT,DELETE,OPTIONS |
app.cors.allowed-headers |
* |
Used by:
- HTTP CORS config:
WebConfig - WebSocket endpoint origins:
WebSocketConfig
All paths below are relative to /izipublish.
| Method | Path | Notes |
|---|---|---|
GET |
/api/items |
Main tree endpoint (include, userAddress, optional containerId, dataTypeId, dataItemId, dataItemVerificationId, dataItemVerificationVerified, recipient scopes, containerScope, data-item filters/sort, domain, page, pageSize) |
GET |
/api/container-child-links |
Auxiliary browse endpoint for container-child links (userAddress, optional containerId, containerScope, query, searchFields, sortBy, sortDirection, domain, page, pageSize) |
GET |
/api/owners |
Auxiliary browse endpoint for owners (userAddress, optional containerId, containerScope, ownerStatus, query, searchFields, sortBy, sortDirection, domain, page, pageSize) |
POST |
/api/link-graph |
Recipients/references recursive graph endpoint (mode, sourceType, sourceContainerId, sourceDataItemId, seeds, maxDepth, maxNodes, preventCycles) |
GET |
/api/containers/{id} |
Container by ID |
GET |
/api/data-types/{id} |
DataType by ID |
GET |
/api/data-items/{id} |
DataItem by ID |
| Method | Path | Notes |
|---|---|---|
POST |
/api/follow-container |
Legacy endpoint: returns HTTP 409 (follow updates are on-chain; use easy_publish.follow_containers) |
DELETE |
/api/follow-container |
Legacy endpoint: returns HTTP 409 (unfollow is on-chain) |
DELETE |
/api/follow-containers |
Legacy endpoint: returns HTTP 409 (bulk unfollow is on-chain) |
GET |
/api/followed-containers |
Paginated followed list (userAddress, page, pageSize) |
| Method | Path | Notes |
|---|---|---|
GET |
/api/user/{address} |
Load user record |
POST |
/api/user/{address}/update |
Create/update user timestamps |
GET |
/api/sync/{chainObjectId} |
Sync status payload |
| Method | Path | Notes |
|---|---|---|
POST |
/api/report/car |
Generates PDF (dataItemId or dataTypeId) |
curl "http://localhost:8084/izipublish/api/items?include=CONTAINER&userAddress=0x123&page=0&pageSize=20"curl "http://localhost:8084/izipublish/api/items?include=CONTAINER,DATA_TYPE,DATA_ITEM,DATA_ITEM_VERIFICATION&userAddress=0x123&containerId=0xcontainer&page=0&pageSize=20"curl "http://localhost:8084/izipublish/api/items?include=CONTAINER,DATA_TYPE,DATA_ITEM,DATA_ITEM_VERIFICATION&userAddress=0x123&containerId=0xcontainer&dataItemVerificationVerified=true&page=0&pageSize=20"curl "http://localhost:8084/izipublish/api/items?include=CONTAINER,DATA_TYPE,DATA_ITEM,DATA_ITEM_VERIFICATION&userAddress=0x123&containerId=0xcontainer&dataItemQuery=oil%20change&dataItemSearchFields=name,description,externalId,externalIndex&dataItemSortBy=created&dataItemSortDirection=desc&page=0&pageSize=20"curl "http://localhost:8084/izipublish/api/container-child-links?userAddress=0x123&containerScope=accessible&query=child&page=0&pageSize=20"curl "http://localhost:8084/izipublish/api/owners?userAddress=0x123&ownerStatus=active&sortBy=address&sortDirection=asc&page=0&pageSize=20"containerlevel: whencontainerIdis not provided.data_typelevel: whencontainerIdis set andincludehasDATA_TYPEbut notDATA_ITEM.data_itemlevel: whencontainerIdis set andincludehasDATA_ITEM.
Response includes a meta object with:
paginationLevel,page,pageSize,totalPages,hasNextincludes,filters- aggregate counters (
totalContainers,totalDataTypes,totalDataItems,totalDataItemVerifications) availableDataTypesindata_itemmode (for frontend filter dropdowns)dataItemVerificationFilteredAfterPaginationflag for frontend handling
Frontend handoff guide:
docs/frontend-items-integration.md
curl -X POST "http://localhost:8084/izipublish/api/report/car?dataTypeId=0x..." \
-o car-report.pdf- Endpoint:
ws://localhost:8084/izipublish/ws-sync - Broker topic:
/topic/sync-status - Publish cadence: every 5 seconds (
SyncEmitterService)
Example STOMP subscription destination:
/topic/sync-status
sequenceDiagram
participant Scheduler as Spring Scheduler
participant API as Spring API
participant Sync as IotaDataSync
participant Node as Node Scripts
participant IOTA as IOTA Network
participant DB as PostgreSQL
Scheduler->>API: scheduledOnchainSync()
API->>Sync: syncUpdateChainRecords(...)
Sync->>Node: getObjectById/getContainerItems scripts
Node->>IOTA: fetch on-chain objects
IOTA-->>Node: object data
Node-->>Sync: normalized JSON
Sync->>DB: upsert entities + progress
API-->>Scheduler: run complete
mvn clean packagemvn testmvn -Pnative packagemvn -Pnative-fast packageOutput binary:
target/native-fast.img
The following values were extracted from hardcoded code paths into application.properties:
app.node.binaryapp.node.items-scriptapp.node.directoryapp.iota.networkapp.iota.rpc-urlsapp.iota.rpc-attempts-per-urlapp.iota.rpc-retry-delay-msapp.iota.node-fetch-max-attemptsapp.iota.node-fetch-retry-delay-msapp.onchain-sync.enabledapp.onchain-sync.initial-delay-msapp.onchain-sync.fixed-delay-msapp.onchain-sync.smart-contract-idapp.onchain-sync.container-chain-idapp.onchain-sync.update-chain-idapp.onchain-sync.data-item-chain-idapp.onchain-sync.data-item-verification-chain-idapp.websocket.broker-prefixapp.websocket.app-prefixapp.websocket.endpointapp.websocket.sync-topicapp.sync.status-push-rate-msapp.easy-publish.offchain-index-enabledapp.easy-publish.offchain-index-msapp.easy-publish.offchain-index-initial-delay-msapp.follow.max-per-userapp.domain.default-public
- No Maven wrapper is currently committed (
mvnwis missing), so Maven must be installed locally. - Node scripts read network/RPC config from
app.iota.*via Java process env propagation. - Frontend code in this workspace is currently in:
react/iota-dapp_backup_20260323_224441
- For Docker/production, use Spring Boot jar:
target/easypublish-0.0.1-SNAPSHOT.jar
- Confirm Node dependencies are installed in
node/. - Confirm Node scripts can run manually:
cd node
node getObjectById.js 0xYOUR_OBJECT_ID- If logs show
Node process failed ... fetch failed, configure explicit RPC URLs:
app.iota.network=testnet
app.iota.rpc-urls=https://api.testnet.iota.cafe- You can also increase retry behavior for unstable network links:
app.iota.rpc-attempts-per-url=3
app.iota.node-fetch-max-attempts=4- Verify
app.cors.allowed-originsincludes your frontend origin. - Restart backend after changing
application.properties.
- Ensure PostgreSQL is running.
- Check credentials in
application.properties. - Confirm
devdbexists.
- This usually means the wrong artifact was launched (for example an assembly/uber jar).
- Run this artifact in Docker:
java -jar /app/easypublish-0.0.1-SNAPSHOT.jar- If your Docker image copies
target/*.jar, pin it to the Spring Boot jar filename explicitly.
- Native build should run with profile
nativeornative-fastonly. - Clean stale artifacts first, then rebuild:
mvn -Pnative-fast clean packageNo license file is currently present in this repository.