-
Notifications
You must be signed in to change notification settings - Fork 615
[Feature] Unify REST API response format for server, pd and store modules #2975#3029
[Feature] Unify REST API response format for server, pd and store modules #2975 #3029BrandaoFelipe wants to merge 6 commits into
Conversation
imbajin
commented
May 17, 2026
I think the current implementation does not actually establish one unified runtime path yet. It adds new mappers/advice, but several existing paths either bypass them or now compete with them.
Current shape:
Expected:
server / pd / store
-> one clear response contract
Current PR:
server -> old ExceptionFilter + new mapper providers coexist
pd -> many controller-level catch blocks still return old RestApiResponse
store -> HgStoreException business code is replaced by generic HTTP 400/500 in the body
The main design issues are:
- PD exceptions are often caught before
@RestControllerAdvicecan see them. Many PD REST APIs already catchPDExceptionand directly returnRestApiResponseor the existingtoJSON(e)format, so a newPDExceptionMapperonly covers exceptions that escape the controller.
PD controller
-> catch PDException
-> return old RestApiResponse / old JSON
-> new advice is bypassed
- Server now has two competing Jersey exception-mapping paths. The server module already has
ExceptionFilterwith mappers forHugeException,IllegalArgumentException,NotFoundException,NoSuchElementException,WebApplicationException,HugeGremlinException, and unknown exceptions. This PR adds another provider package underorg.apache.hugegraph.api, which is scanned byApplicationConfig.
ApplicationConfig packages("org.apache.hugegraph.api")
-> existing ExceptionFilter.*Mapper
-> new exceptionshandler.*Mapper
That makes the effective behavior unclear. If the new HugeExceptionMapper wins, it also maps most HugeException cases to HTTP 400 by default, which can incorrectly classify server-side failures as client errors.
- Store loses the original
HgStoreExceptionbusiness code.HgStoreExceptionhas useful domain codes such as1001,1201,1202,1208,1401, etc. But the newStoreExceptionHandlerreturnsstatus.value()as the responsecode, so clients only see generic400or500instead of the actionable store error code.
Before:
HgStoreException code = 1202
After this PR:
response.code = 500
I suggest first defining the response contract and then updating the existing runtime paths instead of adding parallel ones:
PD:
handle existing catch-and-return paths, or migrate/normalize RestApiResponse
Server:
modify the existing ExceptionFilter path instead of adding competing mappers
Store:
keep HgStoreException business code while using HTTP status for transport semantics
Tests:
add REST-level tests that prove actual endpoints return the unified format
One unrelated item: hugegraph-pd/hg-pd-service/pom.xml adds <classifier>exec</classifier>, which changes the PD packaging output and does not seem related to REST response unification. Please split it out or explain why it is required here.
imbajin
commented
May 17, 2026
A larger concern is that this PR changes the public REST API response contract. The server / pd / store REST APIs may already be consumed by external services, so a large response-format change can have significant downstream impact.
Current response shapes include fields such as:
server errors:
exception / message / cause / trace
pd responses:
message / data / status
or status / error
store errors:
module-specific business error codes
The new envelope introduces a different shape:
ApiResponse:
code / message / data / status
That can break clients that parse existing fields like exception, error, status, or store-specific business codes. Even if the new format is cleaner, this kind of API contract change should be designed and discussed carefully before implementation.
I think we should first decide the compatibility and migration strategy, for example:
Questions to settle before implementation:
- Is this intended to be a breaking public API response change?
- If not, how do old clients keep receiving the old format?
- Should the unified format be opt-in through a version/header/parameter?
- Is there a deprecation period with compatibility fields?
- Should normal responses also be wrapped, or only error responses?
- Is `code` the HTTP status or the business error code?
- What should be documented in REST docs / OpenAPI / release notes?
If this is intended to be a breaking change, it should probably go through community discussion and come with docs, migration notes, OpenAPI updates, and REST-level compatibility tests. If it is not intended to be breaking, then the implementation needs an explicit compatibility layer rather than replacing existing response bodies directly.
In short:
First:
define API contract + compatibility/migration plan
Then:
implement the unified response format against that contract
Hi @imbajin,
Thank you for the detailed review. You raised several important concerns regarding runtime consistency, exception flow coverage, and backward compatibility of the public REST API contract.
I agree that the current approach still leaves multiple competing exception-handling paths in place instead of establishing a single unified runtime contract.
The points you raised are all valid and need to be addressed before this can be considered a proper unification.
My current thinking is:
Server: integrate the unified formatting into the existing ExceptionFilter infrastructure.
PD: explore consolidating the existing controller-level catch-and-return patterns so exceptions can follow a consistent handling path.
Store: preserve existing HgStoreException business codes in the response body while keeping HTTP status codes strictly for transport semantics.
I also agree that the larger concern here is API compatibility. Since the current REST responses already expose module-specific response shapes and fields, introducing a new envelope format without a migration strategy could break downstream consumers.
Before continuing implementation, I think we should first align on:
- the target error response contract,
- compatibility expectations,
- migration/deprecation strategy,
- and whether this should be treated as a breaking API change.
Do you think it would be better to continue this discussion in the PR thread, or would this be more appropriate for a broader dev mailing list discussion first?
Thanks again for the thoughtful review — this was very helpful in clarifying the architectural direction the PR should take.
EDIT:
About the hugegraph-pd/hg-pd-service/pom.xml adding <classifier>exec</classifier> I added the <classifier>exec</classifier> during my local testing phase to resolve a multi-module classpath visibility issue where other modules couldn't properly locate classes packed inside the Spring Boot layout during test compilation.
Since this impacts the core packaging behavior and is indeed unrelated to the REST response unification contract, I will remove it from this PR, thanks for pointing that out.
JisoLya
commented
May 19, 2026
IMO, a controller should only be responsible for receiving external requests and returning responses — exception handling logic does not belong there. We should remove the catch blocks from controllers and let a centralized handler take care of error formatting.
For the store business code, rather than replacing it with the HTTP status code, we could simply add a dedicated field (e.g. storeBizCode) to the response envelope to carry the original HgStoreException code alongside the HTTP status. That way transport semantics and domain error codes stay cleanly separated without losing any information.
That said, it's worth classifying controllers by their audience — user-facing, admin-facing, or internal module-facing. Internal module-facing interfaces may reasonably keep their existing response format unchanged, while the unified response format should at minimum be enforced across all user-facing endpoints.
On compatibility: could we treat this as a breaking change and land it in the next major/minor version with proper release notes and migration guidance?
cc @imbajin
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR introduces a shared ApiResponse envelope and adds centralized REST exception handling across server (Jersey), PD, and store-node (Spring Boot) modules to make error responses more consistent and avoid leaking raw internal failures.
Changes:
- Adds
ApiResponse<T>inhugegraph-common. - Adds module-specific and generic exception handlers for server, PD, and store-node REST APIs.
- Adds PD mapper tests and changes PD service Spring Boot repackage output classifier.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
hugegraph-commons/hugegraph-common/src/main/java/org/apache/hugegraph/rest/response/ApiResponse.java |
Adds the shared response envelope type. |
hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/exceptionshandler/HugeExceptionMapper.java |
Adds Jersey handling for HugeException. |
hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/exceptionshandler/GenericExceptionMapper.java |
Adds Jersey fallback handling for generic exceptions. |
hugegraph-pd/hg-pd-service/src/main/java/org/apache/hugegraph/pd/rest/exceptionshandler/PDExceptionMapper.java |
Adds Spring handling for PDException. |
hugegraph-pd/hg-pd-service/src/main/java/org/apache/hugegraph/pd/rest/exceptionshandler/GenericExceptionMapper.java |
Adds Spring fallback handling for PD service exceptions. |
hugegraph-pd/hg-pd-test/src/main/java/org/apache/hugegraph/pd/rest/exceptions/PDExceptionMapperTest.java |
Adds unit coverage for PD exception mappers. |
hugegraph-pd/hg-pd-service/pom.xml |
Changes Spring Boot repackage artifact classifier. |
hugegraph-store/hg-store-node/src/main/java/org/apache/hugegraph/store/node/controller/exceptionhandlers/StoreExceptionHandler.java |
Adds Spring handling for HgStoreException. |
hugegraph-store/hg-store-node/src/main/java/org/apache/hugegraph/store/node/controller/exceptionhandlers/GenericExceptionMapper.java |
Adds Spring fallback handling for store-node exceptions. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
@VGalaxies
VGalaxies
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Review summary
- Blocking: yes
- Summary: The PR can break PD distribution startup and introduces inconsistent error mappings in the new unified response handlers.
- Evidence:
git diff origin/master...HEADmvn -pl hugegraph-commons/hugegraph-common,hugegraph-server/hugegraph-api,hugegraph-pd/hg-pd-service,hugegraph-store/hg-store-node -am -DskipTests -Dmaven.javadoc.skip=true compilepassed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
High: PD executable jar is no longer the artifact used by the distribution
hugegraph-pd/hg-pd-service/pom.xml:178
Evidence
- The new
<classifier>exec</classifier>makes Spring Boot attach the repackaged executable jar as a classified artifact, whilehugegraph-pd/hg-pd-dist/src/assembly/descriptor/server-assembly.xml:52includes the unclassifiedhg-pd-servicedependency andstart-hugegraph-pd.sh:172-176runs${LIB}/hg-pd-service-*.jarwithjava -jar.
Impact
- The PD dist will package/run the plain unclassified jar instead of the executable Spring Boot jar, so packaged PD startup can fail or run the wrong artifact.
Requested fix
- Either remove the classifier so the main artifact remains executable, or update
hg-pd-distto depend on and include theexecclassifier explicitly and make the startup glob select only that jar.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Medium: Real PD error codes are mapped to HTTP 500
hugegraph-pd/hg-pd-service/src/main/java/org/apache/hugegraph/pd/rest/exceptionshandler/PDExceptionMapper.java:67
Evidence
resolveStatus()only treats exact HTTP codes or codes starting with4as client errors; actual PD codes includeSTORE_ID_NOT_EXIST = 101,NOT_FOUND = 103, andPD_UNREACHABLE = 104inhugegraph-pd/hg-pd-grpc/src/main/proto/pdpb.proto:134-139, so those become500.
Impact
- Uncaught domain errors such as not-found/store-not-found are reported as server failures, which breaks HTTP semantics and can mislead clients, retries, and monitoring.
Requested fix
- Map the existing
Pdpb.ErrorTypevalues explicitly to appropriate HTTP statuses, or preserve the previous REST status behavior while only changing the response body format.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Medium: Store response body drops the HgStoreException code
hugegraph-store/hg-store-node/src/main/java/org/apache/hugegraph/store/node/controller/exceptionhandlers/StoreExceptionHandler.java:57
Evidence
- The handler reads
exception.getCode()at line 37, but constructsApiResponsewithstatus.value()at line 57. Store exceptions define machine-readable codes such as1001and1201inHgStoreException.java:23-40.
Impact
- Clients receive only HTTP codes like
400or500in the unifiedcodefield and lose the store-specific error code needed to distinguish validation, closed-store, RocksDB, PD, and metric failures.
Requested fix
- Use
errorCodeforApiResponse.codeand keep the HTTP status only in theResponseEntitystatus.
Purpose of the PR
The purpose of this PR is to standardize and unify the REST API global error response format across the core modules (
pd,serverandstore/hg-store-node) using the centralizedApiResponseenvelope wrapper.Currently, unhandled exceptions or module-specific errors could return inconsistent formats or expose raw stack traces to the client. This tracking change enforces a clean, predictable JSON error contract across different frameworks used in the ecosystem (Jersey and Spring Boot), enhancing API security and client-side error handling.
Main Changes
This PR introduces centralized exception mapping logic across two distinct sub-framework layers within the project:
1.
hugegraph-servermodule (Jersey / JAX-RS)HugeExceptionMapper: Intercepts allHugeExceptionoccurrences. It safely resolves the underlying root cause using a recursiverootCause()lookup and maps internal core errors directly to appropriate HTTP Status Codes (e.g.,IllegalArgumentException-> 400 Bad Request,NoSuchElementException-> 404 Not Found) wrapped cleanly insideApiResponse.GenericExceptionMapper: Implemented as a top-level global catch-all provider forException.classto prevent infrastructure leakages, mapping unexpected exceptions to a secure HTTP 500"An unexpected error occurred"message payload.2.
hugegraph-storemodule (hg-store-nodesub-module / Spring Boot)StoreExceptionHandler: Created using Spring's@RestControllerAdviceto interceptHgStoreException. It dynamically parses the exception's internal errorcodeto differentiate client errors from infrastructure faults based on explicit ranges:[1200, 1300)(e.g., RocksDB storage failures) -> 500 Internal Server Error (logged asSEVERE/ERRORwith full stack trace).[1000, 1200)(e.g., Unsupported data formats) -> 400 Bad Request (logged cleanly as aWARNINGmessage).GenericExceptionMapper: Added as a fallback interceptor for standardException.classinstances inside the store node rest controllers to ensure uniform 500 error envelopes using the Log4j wrapper.3.
hugegraph-pdmodule (hg-pd-servicesub-module / Spring Boot)PdExceptionHandler: Implemented via@RestControllerAdviceto handle metadata and coordination cluster exceptions (PDException). Centralizes cluster control plane failure logging (Raft state, partition routing) and converts them into the unifiedApiResponseschema.Exception.classmapping to gracefully mask unhandled runtime infrastructure glitches with a generic"An unexpected error occurred in the cluster control plane"feedback text.Verifying these changes
mvn clean compile -DskipTestsfrom the root directory to confirm proper multi-module compile-time dependency alignment (validatingApiResponsecross-package visibility insidehg-store-node).{ "status": 400, "message": "Specific validation/error message string", "data": null, "statusText": "Bad Request" }Does this PR potentially affect the following parts?
Documentation Status
Doc - TODODoc - DoneDoc - No Need