{"id":51043546,"url":"https://github.com/qoretechnologies/module-grpc","last_synced_at":"2026-06-22T12:01:43.027Z","repository":{"id":339618588,"uuid":"1162296821","full_name":"qoretechnologies/module-grpc","owner":"qoretechnologies","description":"Qore gRPC/protobuf module","archived":false,"fork":false,"pushed_at":"2026-06-05T18:14:36.000Z","size":709,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"develop","last_synced_at":"2026-06-05T20:08:52.390Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"QuakeC","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/qoretechnologies.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"COPYING.MIT","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-20T04:52:09.000Z","updated_at":"2026-06-05T18:14:41.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/qoretechnologies/module-grpc","commit_stats":null,"previous_names":["qoretechnologies/module-grpc"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/qoretechnologies/module-grpc","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qoretechnologies%2Fmodule-grpc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qoretechnologies%2Fmodule-grpc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qoretechnologies%2Fmodule-grpc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qoretechnologies%2Fmodule-grpc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/qoretechnologies","download_url":"https://codeload.github.com/qoretechnologies/module-grpc/tar.gz/refs/heads/develop","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qoretechnologies%2Fmodule-grpc/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34647750,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-22T02:00:06.391Z","response_time":106,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-22T12:01:41.963Z","updated_at":"2026-06-22T12:01:43.012Z","avatar_url":"https://github.com/qoretechnologies.png","language":"QuakeC","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Qore grpc Module\n\n## Introduction\n\nThe `grpc` module provides gRPC client/server and protobuf support for Qore, including:\n\n- Dynamic protobuf schema loading from `.proto` files (no code generation needed)\n- Binary protobuf encoding/decoding via `ProtobufSchema`\n- gRPC client with all four call patterns (unary, server streaming, client streaming, bidirectional)\n- gRPC server with handler registration and async I/O\n- TLS/SSL support on client and server\n- Custom metadata passing (request, initial response, trailing)\n- Timeout/deadline enforcement\n- Connection pooling via `HttpClientConnectionManager`\n- Built-in gRPC server reflection (`GrpcReflectionService`) for client service discovery\n\n## Architecture\n\nThe module has two layers:\n\n- **Binary module (`grpc.so`)**: C++ QPP wrapping `libprotobuf` for schema loading and message encoding/decoding. Only dependency is `libprotobuf` (no libgrpc++).\n- **Qore module (`GrpcUtil`)**: Pure Qore implementing the gRPC protocol natively on Qore's HTTP/2 stack.\n\n## Requirements\n\n- Qore 2.0+ (with HTTP/2 trailer support)\n- CMake 3.5+\n- C++17 compiler\n- `libprotobuf` (protobuf development libraries)\n  - Ubuntu/Debian: `libprotobuf-dev`\n  - Alpine: `protobuf-dev`\n  - Fedora: `protobuf-devel`\n\n## Building\n\n```bash\nmkdir build \u0026\u0026 cd build\ncmake ..\nmake\nmake install\n```\n\n## Quick Start\n\n### Unary Call\n\n```qore\n#!/usr/bin/env qore\n\n%modern\n%requires grpc\n%requires GrpcUtil\n\nProtobufSchema schema(\"./proto/\", \"service.proto\");\nGrpcChannel channel(\"http://localhost:50051\");\nGrpcClient client(channel, schema, \"MyService\");\n\nhash\u003cGrpcCallResult\u003e result = client.call(\"SayHello\", {\"name\": \"World\"});\nprintf(\"Response: %y\\n\", result.body);\nprintf(\"Status: %d %s\\n\", result.status_code, result.status_message);\n\nchannel.shutdown();\n```\n\n### Server\n\n```qore\nProtobufSchema schema(\"./proto/\", \"service.proto\");\nGrpcServer server(schema);\n\nserver.registerHandler(\"MyService\", \"SayHello\",\n    hash\u003cauto\u003e sub(hash\u003cauto\u003e request, hash\u003cstring, string\u003e metadata) {\n        return {\"message\": \"Hello, \" + request.name, \"status\": 0};\n    });\n\nint port = server.addInsecurePort(\"localhost:0\");\nserver.start();\nserver.wait();\n```\n\n### Server Streaming\n\n```qore\n# Client\nGrpcClientStream stream = client.serverStream(\"ListItems\", {\"category\": \"books\"});\nwhile (*hash\u003cauto\u003e item = stream.read(5s)) {\n    printf(\"Item: %y\\n\", item);\n}\nhash\u003cGrpcCallResult\u003e result = stream.finish();\n\n# Server handler\nserver.registerHandler(\"MyService\", \"ListItems\",\n    sub(hash\u003cauto\u003e request, GrpcServerStream stream, hash\u003cstring, string\u003e metadata) {\n        for (int i = 0; i \u003c 10; ++i) {\n            stream.write({\"name\": sprintf(\"item-%d\", i)});\n        }\n    });\n```\n\n### Client Streaming\n\n```qore\n# Client\nGrpcClientStream stream = client.clientStream(\"Upload\");\nstream.write({\"chunk\": \"data1\"});\nstream.write({\"chunk\": \"data2\"});\nhash\u003cGrpcCallResult\u003e result = stream.finish();\n\n# Server handler\nserver.registerHandler(\"MyService\", \"Upload\",\n    hash\u003cauto\u003e sub(GrpcServerStream stream, hash\u003cstring, string\u003e metadata) {\n        int count = 0;\n        while (*hash\u003cauto\u003e msg = stream.read()) {\n            ++count;\n        }\n        return {\"total\": count};\n    });\n```\n\n### Bidirectional Streaming\n\nThe server handler processes messages incrementally -- no pre-buffering of the\nclient stream. Both sides can interleave reads and writes for interactive\ncommunication:\n\n```qore\n# Client - interactive request/response\nGrpcClientStream stream = client.bidiStream(\"Chat\");\nstream.write({\"text\": \"Hello\"});\n*hash\u003cauto\u003e reply = stream.read(5s);   # response arrives before next send\nprintf(\"Reply: %s\\n\", reply.text);\n\nstream.write({\"text\": \"World\"});\nreply = stream.read(5s);\nprintf(\"Reply: %s\\n\", reply.text);\n\nstream.writesDone();\nhash\u003cGrpcCallResult\u003e result = stream.finish();\n\n# Server handler - echo each message immediately\nserver.registerHandler(\"MyService\", \"Chat\",\n    sub(GrpcServerStream stream, hash\u003cstring, string\u003e metadata) {\n        while (*hash\u003cauto\u003e msg = stream.read()) {\n            stream.write({\"text\": \"Echo: \" + msg.text});\n        }\n    });\n```\n\n### TLS\n\n```qore\n# Server with TLS\nstring cert = ReadOnlyFile::readTextFile(\"server.crt\");\nstring key = ReadOnlyFile::readTextFile(\"server.key\");\nint port = server.addSecurePort(\"localhost:0\", \u003cGrpcSslOptions\u003e{\n    \"server_cert\": cert,\n    \"server_key\": key,\n});\n\n# Client with HTTPS\nGrpcChannel channel(\"https://localhost:50051\");\n```\n\n### Metadata and Timeouts\n\n```qore\n# Send metadata and set timeout\nhash\u003cGrpcCallOptions\u003e opts = \u003cGrpcCallOptions\u003e{\n    \"timeout_ms\": 5000,\n    \"metadata\": {\"x-request-id\": \"abc-123\"},\n};\nhash\u003cGrpcCallResult\u003e result = client.call(\"Method\", request, opts);\n\n# Server receives metadata in handler\nserver.registerHandler(\"Service\", \"Method\",\n    hash\u003cauto\u003e sub(hash\u003cauto\u003e request, hash\u003cstring, string\u003e metadata) {\n        string req_id = metadata.\"x-request-id\";\n        return {\"id\": req_id};\n    });\n\n# Streaming handlers can set trailing metadata\nserver.registerHandler(\"Service\", \"Stream\",\n    sub(hash\u003cauto\u003e request, GrpcServerStream stream, hash\u003cstring, string\u003e metadata) {\n        stream.setTrailingMetadata({\"x-count\": \"5\"});\n        # ... write messages ...\n    });\n```\n\n### Server Reflection\n\nEnable clients to discover services without local `.proto` files using the standard\ngRPC server reflection protocol (`grpc.reflection.v1`). This works with tools like\n`grpcurl` and `grpc_cli` as well as programmatic clients.\n\n#### Server Setup\n\nJust pass `enable_reflection: True` in the server options:\n\n```qore\n%modern\n%requires grpc\n%requires GrpcUtil\n\nProtobufSchema schema(\"./proto/\", \"service.proto\");\nGrpcServer server(schema, \u003cGrpcServerOptions\u003e{\"enable_reflection\": True});\n\nserver.registerHandler(\"MyService\", \"MyMethod\",\n    hash\u003cauto\u003e sub(hash\u003cauto\u003e request, hash\u003cstring, string\u003e metadata) {\n        return {\"result\": \"ok\"};\n    });\n\nserver.addInsecurePort(\"localhost:50051\");\nserver.start();\nserver.wait();\n```\n\n#### Client Discovery with grpcurl\n\n```bash\n# List all services exposed by the server\ngrpcurl -plaintext localhost:50051 list\n\n# Describe a specific service\ngrpcurl -plaintext localhost:50051 describe mypackage.MyService\n\n# Make a call without needing the .proto file\ngrpcurl -plaintext -d '{\"name\": \"World\"}' localhost:50051 mypackage.MyService/MyMethod\n```\n\n#### Programmatic Discovery\n\n```qore\n%modern\n%requires grpc\n%requires GrpcUtil\n\nGrpcChannel channel(\"http://localhost:50051\");\n\n# Discover services and schema via reflection\nGrpcReflectionClient rc(channel);\nlist\u003cstring\u003e services = rc.listServices();\nprintf(\"Available services: %y\\n\", services);\n\n# Build a schema from the server's descriptors and make calls\nProtobufSchema discovered = rc.discoverSchema();\nGrpcClient client(channel, discovered, \"MyService\");\nhash\u003cGrpcCallResult\u003e result = client.call(\"MyMethod\", {\"name\": \"World\"});\nprintf(\"Response: %y\\n\", result.body);\n\nchannel.shutdown();\n```\n\n#### Data Provider with Reflection\n\nThe `GrpcDataProvider` module can use reflection for zero-configuration service access:\n\n```qore\n%modern\n%requires GrpcDataProvider\n\nGrpcDataProvider provider({\n    \"url\": \"http://localhost:50051\",\n    \"use_reflection\": True,\n});\n\n# Services are discovered automatically\nauto result = provider.getChildProvider(\"MyService\")\n    .getChildProvider(\"MyMethod\")\n    .doRequest({\"name\": \"World\"});\n```\n\nOr from the command line:\n\n```bash\nqdp 'grpc{url=http://localhost:50051,use_reflection=true}/MyService/MyMethod' dor name=World\n```\n\n### Standalone Protobuf\n\n```qore\n%requires grpc\n\nProtobufSchema schema(\"./proto/\", \"messages.proto\");\nbinary data = schema.encode(\"MyMessage\", {\"field\": \"value\"});\nhash\u003cauto\u003e msg = schema.decode(\"MyMessage\", data);\n\n# JSON conversion\nstring json = schema.toJson(\"MyMessage\", {\"field\": \"value\"});\nhash\u003cauto\u003e parsed = schema.fromJson(\"MyMessage\", json);\n```\n\n## Data Provider Integration\n\nThe `GrpcDataProvider` module integrates gRPC with Qore's data provider framework, enabling\n`qdp` CLI access and Qorus workflow integration.\n\n### Unary Call via `qdp`\n\n```bash\n# Using a proto file\nqdp 'grpc{url=http://localhost:50051,proto_path=./proto,proto_file=service.proto}/MyService/SayHello' dor name=World\n\n# Using server reflection (no proto files needed)\nqdp 'grpc{url=http://localhost:50051,use_reflection=true}/MyService/SayHello' dor name=World\n```\n\n### Observable Server Streaming\n\nServer-streaming methods expose an `events` child provider for event-driven consumption:\n\n```bash\nqdp 'grpc{url=http://localhost:50051,use_reflection=true}/PriceService/WatchPrices/events' listen\n```\n\n### Interactive Bidirectional Streaming\n\nBidirectional-streaming methods also expose an `events` child that supports both\nreceiving events and sending messages, enabling interactive use with `qdp ix`:\n\n```bash\nqdp 'grpc{url=http://localhost:50051,use_reflection=true}/ChatService/Chat/events' ix\n```\n\nProgrammatic usage:\n\n```qore\n%modern\n%requires GrpcDataProvider\n\nGrpcDataProvider provider({\n    \"url\": \"http://localhost:50051\",\n    \"use_reflection\": True,\n});\n\n# Navigate to the events child of a bidi method\nAbstractDataProvider events = provider.getChildProvider(\"ChatService\")\n    .getChildProvider(\"Chat\")\n    .getChildProvider(\"events\");\n\n# Register observer and start the stream\nevents.registerObserver(my_observer);\nevents.observersReady();\n\n# Send messages\nevents.sendMessage(MESSAGE_GRPC_STREAM_SEND, {\"text\": \"hello\"});\nevents.sendMessage(MESSAGE_GRPC_STREAM_SEND, {\"text\": \"world\"});\n\n# Signal writes done (server will finish and close the stream)\nevents.stopEvents();\n```\n\n## License\n\nMIT License - see [LICENSE](LICENSE) for details.\n\n## Copyright\n\nCopyright 2026 Qore Technologies, s.r.o.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fqoretechnologies%2Fmodule-grpc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fqoretechnologies%2Fmodule-grpc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fqoretechnologies%2Fmodule-grpc/lists"}