{"id":49822137,"url":"https://github.com/folk-project/folk-plugin-grpc","last_synced_at":"2026-05-15T14:02:14.010Z","repository":{"id":356706865,"uuid":"1233169401","full_name":"Folk-Project/folk-plugin-grpc","owner":"Folk-Project","description":"gRPC plugin for Folk — unary call passthrough to PHP workers via tonic, no Rust protobuf codegen required","archived":false,"fork":false,"pushed_at":"2026-05-09T11:19:56.000Z","size":12,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"develop","last_synced_at":"2026-05-09T11:39:09.367Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Folk-Project.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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-05-08T17:03:17.000Z","updated_at":"2026-05-09T11:20:00.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/Folk-Project/folk-plugin-grpc","commit_stats":null,"previous_names":["folk-project/folk-plugin-grpc"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/Folk-Project/folk-plugin-grpc","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Folk-Project%2Ffolk-plugin-grpc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Folk-Project%2Ffolk-plugin-grpc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Folk-Project%2Ffolk-plugin-grpc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Folk-Project%2Ffolk-plugin-grpc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Folk-Project","download_url":"https://codeload.github.com/Folk-Project/folk-plugin-grpc/tar.gz/refs/heads/develop","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Folk-Project%2Ffolk-plugin-grpc/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32981607,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-13T11:31:52.688Z","status":"ssl_error","status_checked_at":"2026-05-13T11:31:52.072Z","response_time":115,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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-05-13T12:07:24.043Z","updated_at":"2026-05-14T13:02:15.514Z","avatar_url":"https://github.com/Folk-Project.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# folk-plugin-grpc\n\ngRPC plugin for Folk — accepts gRPC calls via tonic/HTTP2 and dispatches to PHP workers. No Rust protobuf codegen required — all protobuf handling happens in PHP.\n\n## Configuration\n\nAdd to `folk.build.toml`:\n\n```toml\n[[plugin]]\ncrate_name = \"folk-plugin-grpc\"\nversion = \"0.1\"\nconfig_key = \"grpc\"\n```\n\nConfigure in `folk.toml`:\n\n```toml\n[grpc]\nlisten = \"0.0.0.0:50051\"\n```\n\n### Settings\n\n| Key | Type | Default | Description |\n|-----|------|---------|-------------|\n| `listen` | `SocketAddr` | `\"0.0.0.0:50051\"` | Address and port to listen on for gRPC connections. |\n| `reflection` | `bool` | `false` | *Planned.* gRPC server reflection (allows `grpcurl` without `-proto` flag). Not yet implemented. |\n\nFull example:\n\n```toml\n[grpc]\nlisten = \"0.0.0.0:50051\"\n```\n\nCurrently, clients must provide the `.proto` file for service discovery:\n\n```bash\ngrpcurl -plaintext -proto proto/greeter.proto localhost:50051 list\ngrpcurl -plaintext -proto proto/greeter.proto localhost:50051 describe greeter.Greeter\n```\n\n## How it works\n\n```\ngRPC client ──HTTP/2──\u003e folk-plugin-grpc (Rust)\n                            │\n                            ├── extracts service, method, payload, metadata from request\n                            ├── encodes as MessagePack {service, method, payload, metadata}\n                            └── dispatches to PHP worker via grpc.call RPC\n                                    │\n                                    ▼\n                              PHP handler\n                                    │\n                                    ├── decodes protobuf request (google/protobuf PHP)\n                                    ├── executes business logic\n                                    ├── encodes protobuf response\n                                    └── returns raw bytes\n                                    │\n                                    ▼\n                            folk-plugin-grpc\n                                    │\n                                    └── wraps in gRPC frame, sends HTTP/2 response\n```\n\nThe Rust plugin handles HTTP/2, gRPC framing, and metadata extraction.\nPHP handles protobuf serialization and business logic.\n\n### Metadata\n\ngRPC metadata (the equivalent of HTTP headers) is extracted from the request and passed to PHP handlers via `Context`. Standard HTTP/2 and gRPC internal headers are filtered out — everything else is available:\n\n```php\n$ctx-\u003egetValue('authorization');  // \"Bearer eyJ...\"\n$ctx-\u003egetValue('x-request-id');   // \"abc-123\"\n$ctx-\u003egetMetadata();              // all metadata as array\n```\n\n## Usage with folk-sdk (plain PHP)\n\n### 1. Define your proto\n\n```protobuf\nsyntax = \"proto3\";\npackage greeter;\n\nservice Greeter {\n  rpc SayHello (HelloRequest) returns (HelloReply);\n}\n\nmessage HelloRequest { string name = 1; }\nmessage HelloReply { string message = 1; }\n```\n\n### 2. Generate PHP classes\n\n```bash\nprotoc --php_out=. proto/greeter.proto\n```\n\nThis generates message classes (`HelloRequest`, `HelloReply`) but not service interfaces. You write the handler manually.\n\n### 3. Write the handler (raw bytes mode)\n\n```php\n\u003c?php\n\nuse Folk\\Sdk\\Grpc\\GrpcModeHandler;\nuse Folk\\Sdk\\Grpc\\Context;\n\nclass GreeterHandler implements GrpcModeHandler\n{\n    public function call(string $service, string $method, string $payload, Context $context): string\n    {\n        return match ($method) {\n            'SayHello' =\u003e $this-\u003esayHello($payload, $context),\n            default =\u003e throw new \\RuntimeException(\"Unknown method: {$method}\"),\n        };\n    }\n\n    private function sayHello(string $payload, Context $context): string\n    {\n        $request = new \\Greeter\\HelloRequest();\n        $request-\u003emergeFromString($payload);\n\n        $token = $context-\u003egetValue('authorization'); // metadata access\n\n        $reply = new \\Greeter\\HelloReply();\n        $reply-\u003esetMessage(\"Hello, {$request-\u003egetName()}!\");\n\n        return $reply-\u003eserializeToString();\n    }\n}\n```\n\n### 4. Register\n\n```php\n$loop = new \\Folk\\Sdk\\Worker\\WorkerLoop();\n$loop-\u003eregisterGrpcHandler(new GreeterHandler());\n$loop-\u003erun();\n```\n\n## Usage with Laravel (folk-laravel)\n\nLaravel integration uses typed handlers with auto-detection of protobuf parameters.\n\n### 1. Define your proto and generate PHP classes\n\n```bash\nprotoc --php_out=app/Proto proto/greeter.proto\n```\n\n### 2. Create a service interface\n\n```php\n\u003c?php\n// app/Proto/Greeter/GreeterInterface.php\n\nnamespace App\\Proto\\Greeter;\n\nuse Folk\\Sdk\\Grpc\\ServiceInterface;\n\ninterface GreeterInterface extends ServiceInterface\n{\n    public const NAME = \"greeter.Greeter\";\n\n    public function SayHello(HelloRequest $in): HelloReply;\n    public function SayGoodbye(HelloRequest $in): HelloReply;\n}\n```\n\nThe `NAME` constant must match the protobuf `package.Service` name — this is how the router maps incoming gRPC calls to your handler.\n\n### 3. Implement the service\n\n```php\n\u003c?php\n// app/Grpc/GreeterService.php\n\nnamespace App\\Grpc;\n\nuse App\\Proto\\Greeter\\GreeterInterface;\nuse App\\Proto\\Greeter\\HelloReply;\nuse App\\Proto\\Greeter\\HelloRequest;\n\nclass GreeterService implements GreeterInterface\n{\n    public function SayHello(HelloRequest $in): HelloReply\n    {\n        $reply = new HelloReply();\n        $reply-\u003esetMessage(\"Hello, {$in-\u003egetName()}!\");\n        return $reply;\n    }\n\n    public function SayGoodbye(HelloRequest $in): HelloReply\n    {\n        $reply = new HelloReply();\n        $reply-\u003esetMessage(\"Goodbye, {$in-\u003egetName()}!\");\n        return $reply;\n    }\n}\n```\n\nThe `GrpcRouter` auto-detects protobuf-typed parameters and handles `mergeFromString()` / `serializeToString()` automatically. You work with typed objects, not raw bytes.\n\n### 4. Register in config\n\n```php\n// config/folk.php\n\nreturn [\n    'grpc' =\u003e [\n        'services' =\u003e [\n            \\App\\Proto\\Greeter\\GreeterInterface::class =\u003e \\App\\Grpc\\GreeterService::class,\n        ],\n    ],\n];\n```\n\nThat's it. `FolkServiceProvider` reads the config, resolves services from the Laravel container, and registers them with the `GrpcRouter`.\n\n### Accessing metadata in typed handlers\n\nAdd `Context` as the first parameter:\n\n```php\nuse Folk\\Sdk\\Grpc\\Context;\n\nclass GreeterService implements GreeterInterface\n{\n    public function SayHello(Context $ctx, HelloRequest $in): HelloReply\n    {\n        $token = $ctx-\u003egetValue('authorization');\n        // ...\n    }\n}\n```\n\nThe router detects `Context` parameters automatically and injects the metadata.\n\n### Multiple services\n\nRegister as many services as you need:\n\n```php\n// config/folk.php\n\n'grpc' =\u003e [\n    'services' =\u003e [\n        \\App\\Proto\\Greeter\\GreeterInterface::class =\u003e \\App\\Grpc\\GreeterService::class,\n        \\App\\Proto\\Auth\\AuthInterface::class       =\u003e \\App\\Grpc\\AuthService::class,\n        \\App\\Proto\\Orders\\OrdersInterface::class   =\u003e \\App\\Grpc\\OrdersService::class,\n    ],\n],\n```\n\n### Compatibility with Spiral/RoadRunner\n\nIf you have services generated by `protoc-gen-php-grpc` (Spiral), they work with Folk:\n\n```php\n// Spiral-generated interface\ninterface InfoInterface extends \\Spiral\\RoadRunner\\GRPC\\ServiceInterface\n{\n    public const NAME = \"myapp.Info\";\n    public function Get(\\Spiral\\RoadRunner\\GRPC\\ContextInterface $ctx, \\Google\\Protobuf\\GPBEmpty $in): ServiceInfo;\n}\n\n// Register as-is — Folk injects its own Context for the $ctx parameter\n'services' =\u003e [\n    InfoInterface::class =\u003e InfoService::class,\n],\n```\n\nThe `NAME` constant is read from any interface the handler implements. Context parameters (regardless of type) receive Folk's `Context` instance.\n\n## Testing with curl\n\n```bash\n# Encode HelloRequest { name: \"Folk\" } as protobuf\n# Field 1 (string): 0x0a + length + bytes\nprintf '\\x00\\x00\\x00\\x00\\x06\\x0a\\x04Folk' | curl --http2-prior-knowledge \\\n  -X POST http://localhost:50051/greeter.Greeter/SayHello \\\n  -H \"content-type: application/grpc\" \\\n  -H \"authorization: Bearer my-token\" \\\n  --data-binary @- -o response.bin\n\n# Decode response (skip 5-byte gRPC frame)\npython3 -c \"\ndata = open('response.bin','rb').read()\nproto = data[5:]\nprint(proto[2:2+proto[1]].decode())\n\"\n# Output: Hello, Folk!\n```\n\n## Testing with grpcurl\n\n```bash\n# List services (requires -proto flag)\ngrpcurl -plaintext -proto proto/greeter.proto localhost:50051 list\n\n# Describe a service\ngrpcurl -plaintext -proto proto/greeter.proto localhost:50051 describe greeter.Greeter\n\n# Note: grpcurl RPC calls require HTTP/2 trailers support (in progress)\n```\n\n## Requirements\n\n- PHP: `google/protobuf` package (`composer require google/protobuf`)\n- `protoc` for generating PHP message classes\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffolk-project%2Ffolk-plugin-grpc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffolk-project%2Ffolk-plugin-grpc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffolk-project%2Ffolk-plugin-grpc/lists"}