NetScope is a Java Spring Boot library that automatically exposes Spring bean methods as network-accessible APIs via both REST and gRPC.
- Expose any Spring bean method dynamically over REST and gRPC
- Control access via global or per-method API keys
- Automatic API documentation with interactive UI
- Zero manual configuration - just annotate your methods
- Production-ready with full authentication support
NetScope is perfect for microservice communication, RPC, distributed systems, and dynamic API exposure.
NetScope is developed and maintained by FractalX.
- Dual Protocol Support: Every method works with both REST HTTP and gRPC
- Two Annotations:
@NetworkPublicβ Fully open network endpoint@NetworkSecured(key="...")β Protected endpoint requiring authentication
- Dynamic Discovery: Automatically finds and exposes annotated methods
- Interactive Documentation:
/netscope/docsβ JSON API documentation/netscope/docs/uiβ Web-based testing interface
- Flexible Security: Global or per-method API key authentication
- Streaming Support: gRPC bidirectional streaming for real-time communication
| Feature | REST | gRPC |
|---|---|---|
| Method Invocation | β | β |
| Authentication | β | β |
| Documentation | β | β |
| Streaming | β | β |
| Protocol Reflection | β | β |
| Binary Efficiency | β | β |
# Clone the repository
git clone https://github.com/project-FractalX/netscope-grpc.git
cd netscope-grpc
# Install to local .m2 repository
mvn clean install<dependency>
<groupId>com.netscope</groupId>
<artifactId>netscope-grpc</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency><dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency># REST Server (Spring Boot default)
server.port=8080
# gRPC Server
netscope.grpc.enabled=true
netscope.grpc.port=9090
netscope.grpc.maxInboundMessageSize=4194304
# Security
netscope.security.enabled=true
netscope.security.apiKey=super-secret-global-keyserver:
port: 8080
netscope:
grpc:
enabled: true
port: 9090
maxInboundMessageSize: 4194304 # 4MB
security:
enabled: true
apiKey: super-secret-global-keypackage com.example.service;
import com.netscope.annotation.NetworkPublic;
import com.netscope.annotation.NetworkSecured;
import org.springframework.stereotype.Service;
@Service
public class CustomerService {
// Publicly accessible via REST and gRPC
@NetworkPublic
public String getCustomers() {
return "All customers";
}
// Requires global API key
@NetworkSecured
public int getLoyaltyPoints(String customerId) {
// Business logic
return 1250;
}
// Requires method-specific API key
@NetworkSecured(key = "inventory-service-key")
public int checkStock(String itemId) {
// Business logic
return 42;
}
// REST-only endpoint
@NetworkPublic(enableGrpc = false)
public String getWebOnlyData() {
return "REST only";
}
// gRPC-only endpoint
@NetworkSecured(key = "grpc-key", enableRest = false)
public String getGrpcOnlyData() {
return "gRPC only";
}
}curl http://localhost:8080/netscope/CustomerService/getCustomerscurl -H "X-API-KEY: super-secret-global-key" \
http://localhost:8080/netscope/CustomerService/getLoyaltyPoints \
-d '["CUST123"]'curl -H "X-API-KEY: inventory-service-key" \
http://localhost:8080/netscope/CustomerService/checkStock \
-d '["ITEM456"]'# Get JSON documentation
curl http://localhost:8080/netscope/docs
# Open interactive UI in browser
open http://localhost:8080/netscope/docs/uiimport com.netscope.grpc.proto.*;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
public class NetScopeClient {
public static void main(String[] args) {
// Create channel
ManagedChannel channel = ManagedChannelBuilder
.forAddress("localhost", 9090)
.usePlaintext()
.build();
// Create stub
NetScopeServiceGrpc.NetScopeServiceBlockingStub stub =
NetScopeServiceGrpc.newBlockingStub(channel);
// Call public method
GenericRequest publicRequest = GenericRequest.newBuilder()
.setBeanName("CustomerService")
.setMethodName("getCustomers")
.setArgumentsJson("[]")
.build();
GenericResponse response = stub.invokeMethod(publicRequest);
System.out.println("Result: " + response.getResultJson());
// Call restricted method
GenericRequest restrictedRequest = GenericRequest.newBuilder()
.setBeanName("CustomerService")
.setMethodName("getLoyaltyPoints")
.setArgumentsJson("[\"CUST123\"]")
.setApiKey("super-secret-global-key")
.build();
GenericResponse response2 = stub.invokeMethod(restrictedRequest);
System.out.println("Points: " + response2.getResultJson());
// Get documentation
DocsRequest docsRequest = DocsRequest.newBuilder().build();
DocsResponse docs = stub.getDocs(docsRequest);
for (MethodInfo method : docs.getMethodsList()) {
System.out.println(method.getBeanName() + "." + method.getMethodName());
}
channel.shutdown();
}
}import grpc
from netscope_pb2 import GenericRequest, DocsRequest
from netscope_pb2_grpc import NetScopeServiceStub
# Create channel
channel = grpc.insecure_channel('localhost:9090')
stub = NetScopeServiceStub(channel)
# Call public method
request = GenericRequest(
bean_name="CustomerService",
method_name="getCustomers",
arguments_json="[]"
)
response = stub.InvokeMethod(request)
print(f"Result: {response.result_json}")
# Call restricted method
request = GenericRequest(
bean_name="CustomerService",
method_name="getLoyaltyPoints",
arguments_json='["CUST123"]',
api_key="super-secret-global-key"
)
response = stub.InvokeMethod(request)
print(f"Points: {response.result_json}")
# Get documentation
docs_request = DocsRequest()
docs = stub.GetDocs(docs_request)
for method in docs.methods:
print(f"{method.bean_name}.{method.method_name}")# List services
grpcurl -plaintext localhost:9090 list
# Describe service
grpcurl -plaintext localhost:9090 describe netscope.NetScopeService
# Call public method
grpcurl -plaintext -d '{
"bean_name": "CustomerService",
"method_name": "getCustomers",
"arguments_json": "[]"
}' localhost:9090 netscope.NetScopeService/InvokeMethod
# Call restricted method
grpcurl -plaintext -d '{
"bean_name": "CustomerService",
"method_name": "getLoyaltyPoints",
"arguments_json": "[\"CUST123\"]",
"api_key": "super-secret-global-key"
}' localhost:9090 netscope.NetScopeService/InvokeMethod
# Get documentation
grpcurl -plaintext -d '{}' localhost:9090 netscope.NetScopeService/GetDocsAll restricted methods use the same key:
netscope.security.enabled=true
netscope.security.apiKey=global-master-keyDifferent keys for different services:
@Service
public class PaymentService {
@NetworkSecured(key = "payment-service-key")
public Receipt processPayment(Payment payment) {
// ...
}
}
@Service
public class UserService {
@NetworkSecured(key = "user-service-key")
public User getUser(String id) {
// ...
}
}Public and restricted methods in the same service:
@Service
public class ProductService {
@NetworkPublic
public List<Product> getPublicProducts() {
// Anyone can call
}
@NetworkSecured
public List<Product> getAdminProducts() {
// Requires authentication
}
}// Client-side streaming example
StreamObserver<GenericRequest> requestStream =
asyncStub.invokeMethodStream(new StreamObserver<GenericResponse>() {
@Override
public void onNext(GenericResponse response) {
System.out.println("Response: " + response.getResultJson());
}
@Override
public void onError(Throwable t) {
System.err.println("Error: " + t.getMessage());
}
@Override
public void onCompleted() {
System.out.println("Stream completed");
}
});
// Send multiple requests
for (int i = 0; i < 10; i++) {
GenericRequest request = GenericRequest.newBuilder()
.setBeanName("DataService")
.setMethodName("processData")
.setArgumentsJson("[" + i + "]")
.build();
requestStream.onNext(request);
}
requestStream.onCompleted();@Configuration
public class CustomMappingConfig {
@Bean
public UrlMappingStrategy customUrlMappingStrategy() {
return (beanClass, method) -> {
return "/api/v1/" +
beanClass.getSimpleName().toLowerCase() + "/" +
method.getName();
};
}
}Replace REST with gRPC for efficient inter-service communication:
@Service
public class OrderService {
@NetworkSecured(key = "internal-service-key", enableRest = false)
public Order createOrder(OrderRequest request) {
// Internal service communication via gRPC only
}
}Public REST API for external clients, gRPC for internal:
@Service
public class UserService {
@NetworkPublic(enableGrpc = false) // REST only for external
public UserProfile getPublicProfile(String userId) {
// Public REST API
}
@NetworkSecured(key = "internal", enableRest = false) // gRPC only
public UserDetails getInternalDetails(String userId) {
// Internal service communication
}
}Automatically discover all available services:
@Service
public class ServiceDiscovery {
@Autowired
private NetScopeScanner scanner;
public List<String> listAllServices() {
return scanner.scan().stream()
.map(m -> m.getBeanName() + "." + m.getMethodName())
.collect(Collectors.toList());
}
}mvn testmvn clean packagemvn clean installThe proto files are automatically compiled during the Maven build. Generated files are in target/generated-sources/protobuf/.
- Java 21+
- Spring Boot 3.2+
- Maven 3.6+ (for building)
- gRPC 1.61+ (included as dependency)
Check if the port is available:
lsof -i :9090Enable gRPC explicitly:
netscope.grpc.enabled=trueVerify your API key configuration:
curl -v -H "X-API-KEY: your-key" http://localhost:8080/netscope/docsCheck logs for authentication details.
Ensure your service is a Spring bean (@Service, @Component, etc.) and the method has the correct annotation.
@NetworkPublic(
path = "", // Custom REST path (optional)
method = RequestMethod.GET, // HTTP method (default: GET)
enableRest = true, // Enable REST endpoint (default: true)
enableGrpc = true // Enable gRPC endpoint (default: true)
)@NetworkSecured(
key = "", // Per-method API key (optional, uses global if empty)
method = RequestMethod.GET, // HTTP method (default: GET)
path = "", // Custom REST path (optional)
enableRest = true, // Enable REST endpoint (default: true)
enableGrpc = true // Enable gRPC endpoint (default: true)
)service NetScopeService {
rpc InvokeMethod (GenericRequest) returns (GenericResponse);
rpc GetDocs (DocsRequest) returns (DocsResponse);
rpc InvokeMethodStream (stream GenericRequest) returns (stream GenericResponse);
}External contributions are NOT welcomed yet! We're working on establishing our contribution guidelines.
- Sathnindu Kottage - Initial work - sathninduk
- See CONTRIBUTORS.md for the full list of contributors
NetScope is licensed under the Apache License 2.0. See the LICENSE file for details.
- Zero Boilerplate: No manual controller or service definitions
- Polyglot: Works with any gRPC-supported language
- Production Ready: Full authentication and security support
- Developer Friendly: Interactive documentation UI
- Flexible: Choose REST, gRPC, or both for each method
- Type Safe: Automatic parameter marshaling and type conversion
- Discoverable: Built-in service discovery and documentation
- GitHub: https://github.com/project-FractalX/netscope
- Issues: https://github.com/project-FractalX/netscope/issues
- Documentation: https://netscope.fractalx.dev (coming soon)
NetScope - Making every Java method a network API π