Modify a gRPC Contract
All internal service-to-service calls use gRPC; REST endpoints are for external client access only. See API & gRPC Reference for the current 8 proto contracts and their ports — this page is the task workflow for changing one.
1. Edit the proto file
One file per service in backends/shared/src/main/proto/ (e.g. credential.proto), package
matching the Java package (com.attestpro.credentialissuance):
syntax = "proto3";
package com.attestpro.credentialissuance;
service CredentialIssuanceService {
rpc IssueCredential(IssueCredentialRequest) returns (IssueCredentialResponse);
rpc GetCredential(GetCredentialRequest) returns (CredentialResponse);
}
message IssueCredentialRequest {
string issuer_id = 1;
string subject_name = 2;
string credential_type = 3;
google.protobuf.Struct credential_data = 4;
}
message IssueCredentialResponse {
string credential_id = 1;
string shareable_link = 2;
}2. Regenerate Java stubs
The protobuf Gradle plugin (configured in backends/shared/build.gradle) regenerates stubs
automatically on build:
./gradlew clean build # full rebuild, regenerates all stubs
./gradlew :backends:shared:compileProto # just the shared moduleGenerated output lands in
backends/shared/build/generated/source/proto/main/{java,grpc}/com/attestpro/{service}/ and is
checked into source control to avoid regenerating on every build.
3. Implement the RPC in the owning microservice
public class CredentialIssuanceServiceImpl
extends CredentialIssuanceServiceGrpc.CredentialIssuanceServiceImplBase {
@Override
public void issueCredential(
IssueCredentialRequest request,
StreamObserver<IssueCredentialResponse> responseObserver) {
try {
IssueCredentialResponse response = IssueCredentialResponse.newBuilder()
.setCredentialId(UUID.randomUUID().toString())
.setShareableLink("https://...")
.build();
responseObserver.onNext(response);
responseObserver.onCompleted();
} catch (Exception e) {
responseObserver.onError(e);
}
}
}Register it as a Spring-managed gRPC service bean; the service’s application.yml sets its gRPC
port (grpc.server.port) — see Internal gRPC mTLS for the
mutual-TLS config every server/client channel requires.
4. Update the Core Engine gRPC client
Core Engine instantiates stubs at startup via Spring beans and is the only caller that knows multi-service workflows:
@Bean
public CredentialIssuanceServiceGrpc.CredentialIssuanceServiceStub credentialIssuanceStub(
ManagedChannel channel) {
return CredentialIssuanceServiceGrpc.newStub(channel);
}If the proto changed shape, update the calling code that builds the request/response conversion.
5. Restart — stubs are not hot-reloaded
Unlike REST endpoints, gRPC stub changes require a full JVM restart of both the owning service and Core Engine to pick up regenerated classes.
Gotchas
- Cross-module imports: only import from
backends/sharedor your own module. Generated proto stubs insharedare the one deliberate exception — every service depends on them. - Proto file organization: one file per service, named
{service_name}.proto, package matching the Java package. - Service discovery (
grpc.services.*.host/portinapplication.yml) resolves via Kubernetes DNS or the Docker Compose bridge network in deployed environments, andlocalhostwhen running services individually outside Docker — see Local Development.