{"id":22528535,"url":"https://github.com/connectrpc/vanguard-go","last_synced_at":"2025-10-06T02:23:09.590Z","repository":{"id":203611036,"uuid":"665615816","full_name":"connectrpc/vanguard-go","owner":"connectrpc","description":"Support REST, gRPC, gRPC-Web, and Connect clients with one server.","archived":false,"fork":false,"pushed_at":"2025-09-09T18:29:10.000Z","size":812,"stargazers_count":330,"open_issues_count":21,"forks_count":21,"subscribers_count":8,"default_branch":"main","last_synced_at":"2025-10-05T20:48:02.716Z","etag":null,"topics":["connectrpc","golang","grpc","grpc-gateway","protobuf","rest"],"latest_commit_sha":null,"homepage":"https://pkg.go.dev/connectrpc.com/vanguard","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/connectrpc.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":".github/CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-07-12T15:40:12.000Z","updated_at":"2025-10-01T14:46:29.000Z","dependencies_parsed_at":"2024-01-26T00:28:49.691Z","dependency_job_id":"1f0ffdce-b6e3-4846-9b9c-b647fa943cb2","html_url":"https://github.com/connectrpc/vanguard-go","commit_stats":null,"previous_names":["connectrpc/vanguard-go","bufbuild/vanguard-go","bufbuild/vanguard"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/connectrpc/vanguard-go","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/connectrpc%2Fvanguard-go","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/connectrpc%2Fvanguard-go/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/connectrpc%2Fvanguard-go/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/connectrpc%2Fvanguard-go/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/connectrpc","download_url":"https://codeload.github.com/connectrpc/vanguard-go/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/connectrpc%2Fvanguard-go/sbom","scorecard":{"id":302665,"data":{"date":"2025-08-11","repo":{"name":"github.com/connectrpc/vanguard-go","commit":"c0a03e49a02f63fce96f77281e1c977015f1c0d6"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":5.4,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 1 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Code-Review","score":10,"reason":"all changesets reviewed","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/add-to-project.yaml:1","Info: topLevel 'contents' permission set to 'read': .github/workflows/ci.yaml:12","Info: topLevel 'pull-requests' permission set to 'read': .github/workflows/pr-title.yaml:5","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Security-Policy","score":9,"reason":"security policy file detected","details":["Info: security policy file detected: SECURITY.md:1","Info: Found linked content: SECURITY.md:1","Warn: One or no descriptive hints of disclosure, vulnerability, and/or timelines in security policy","Info: Found text in security policy: SECURITY.md:1"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: third-party GitHubAction not pinned by hash: .github/workflows/add-to-project.yaml:20: update your workflow using https://app.stepsecurity.io/secureworkflow/connectrpc/vanguard-go/add-to-project.yaml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yaml:26: update your workflow using https://app.stepsecurity.io/secureworkflow/connectrpc/vanguard-go/ci.yaml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yaml:30: update your workflow using https://app.stepsecurity.io/secureworkflow/connectrpc/vanguard-go/ci.yaml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/pr-title.yaml:18: update your workflow using https://app.stepsecurity.io/secureworkflow/connectrpc/vanguard-go/pr-title.yaml/main?enable=pin","Info:   0 out of   2 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   2 third-party GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 30 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-17T21:07:46.198Z","repository_id":203611036,"created_at":"2025-08-17T21:07:46.198Z","updated_at":"2025-08-17T21:07:46.198Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278548018,"owners_count":26004818,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-06T02:00:05.630Z","response_time":65,"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":["connectrpc","golang","grpc","grpc-gateway","protobuf","rest"],"created_at":"2024-12-07T07:02:33.373Z","updated_at":"2025-10-06T02:23:09.569Z","avatar_url":"https://github.com/connectrpc.png","language":"Go","funding_links":[],"categories":["Go"],"sub_categories":[],"readme":"# ⚔️ Vanguard\n\n[![Build](https://github.com/connectrpc/vanguard-go/actions/workflows/ci.yaml/badge.svg?branch=main)](https://github.com/connectrpc/vanguard-go/actions/workflows/ci.yaml)\n[![Report Card](https://goreportcard.com/badge/connectrpc.com/vanguard)](https://goreportcard.com/report/github.com/connectrpc/vanguard-go)\n[![GoDoc](https://pkg.go.dev/badge/connectrpc.com/vanguard.svg)](https://pkg.go.dev/github.com/connectrpc/vanguard-go)\n[![Slack](https://img.shields.io/badge/slack-buf-%23e01563)][badges_slack]\n\nVanguard is a powerful library for Go `net/http` servers that enables seamless\ntranscoding between REST and RPC protocols. Whether you need to bridge the gap\nbetween gRPC, gRPC-Web, Connect, or REST, Vanguard has got you covered. With support for\nGoogle's [HTTP transcoding options](https://github.com/googleapis/googleapis/blob/master/google/api/http.proto#L44),\nit can effortlessly translate protocols using strongly typed Protobuf definitions.\n\n[See an example in action!](internal/examples/fileserver/main.go)\n\n## Why Vanguard?\n\nVanguard offers a range of compelling use cases that make it an invaluable addition\nto your services:\n\n1. **RESTful Transformation**: By leveraging HTTP transcoding annotations, you can effortlessly \nsupport REST clients. This feature is especially handy during the migration from a REST API \nto a schema-driven RPC API. With the right annotations, your existing REST clients can \nseamlessly access your API, even as you transition your server implementations to Protobuf \nand RPC.\n\n2. **Efficiency and Code Generation**: Unlike traditional approaches like [gRPC-Gateway](https://github.com/grpc-ecosystem/grpc-gateway#readme), \nVanguard operates efficiently within Go servers, compatible with various servers such as \n[Connect](https://github.com/connectrpc/connect-go) and [gRPC](https://github.com/grpc/grpc-go). \nIt doesn't rely on extensive code generation, eliminating the need for additional code \ngeneration steps. This flexibility ensures that your code can adapt dynamically, loading \nservice definitions from configuration, schema registries, or via \n[gRPC Server Reflection](https://github.com/grpc/grpc/blob/master/doc/server-reflection.md), \nmaking it a perfect fit for proxies without the hassle of recompilation and redeployment \neach time an RPC service schema changes.\n\n3. **Legacy Compatibility**: The HTTP transcoding annotations also empower you to support \nlegacy REST API servers when clients are accustomed to using Protobuf RPC. This lets \nyou embrace RPC in specific teams, such as for web or mobile clients, without the \nprerequisite of migrating all backend API services.\n\n4. **Seamless Protocol Bridging**: If your organization is transitioning from gRPC to Connect, \nVanguard acts as a bridge between the protocols. This facilitates the use of your existing \ngRPC service handlers with Connect clients, allowing you to smoothly adapt to Connect's \nenhanced usability and inspectability with web browsers and mobile devices. No need to \noverhaul your server handler logic before migrating clients to Connect.\n\n## Usage\n\nVanguard is straight-forward to configure and is used to wrap other HTTP handlers. In\nthat regard, a Vanguard transcoder acts kind of like HTTP middleware. The kinds of HTTP\nhandlers you'll typically be wrapping are:\n1. gRPC handlers: After configuring a `*grpc.Server`, instead of calling its `Start`\n   method, it can be mounted as a handler of an `http.Server` or `http.ServeMux`.\n   This allows you to decorate the gRPC handler with the Vanguard middleware.\n2. Connect handlers: With Connect, handlers already implement `http.Handler` and are\n   thus trivial to wrap with Vanguard.\n3. Proxy handlers: In some cases, your Go service may act as a proxy and forward\n   requests to a different backend server. To support legacy REST API servers, the\n   Go server can proxy requests to those legacy backends, and Vanguard can transcode\n   incoming RPC requests to a form that the legacy backends can understand.\n\n### Defining Services\n\nA `Service` in Vanguard is the configuration for a single Protobuf RPC service. This\nconfiguration includes a schema for the service, which empowers the format translations\nand is also the typical source HTTP annotations, for mapping an RPC to REST-ful\nconventions. It also includes a handler, which is an `http.Handler` that implements the\nservice. Finally, it can include options that configure the protocols and formats that\nthe handler can accept.\n\nYou create one by calling `vanguard.NewService`, supplying the service's fully-qualified\nname (or URI path, which is the fully-qualified name with a leading and trailing slash)\nand the corresponding handler.\n\nIf you supply no other options, a service's default configuration supports a Connect\nhandler. So if you use the `protoc-gen-connect-go` Protobuf plugin and use the\ngenerated factory function for creating a handler, it will work perfectly with\nVanguard.\n```go\n// A Connect handler factory function returns the service path and the HTTP\n// handler. So you can pass the result directly to NewService:\nmyService := vanguard.NewService(\n\tmyservicev1connect.NewMyServiceHandler(\u0026myServiceImpl{}),\n)\n\n// If not using a Connect handler, you can still use this function and\n// directly refer to the service's name.\nmyService = vanguard.NewService(\n\tmyservicev1connect.MyServiceName,\n\tsomeOtherHTTPHandler,\n)\n```\n\nWith the `vanguard.NewService` function, the service must be known to the program. That\nmeans that Go code has been generated for the Protobuf file that defines the RPC service,\nusing the `protoc-gen-go` plugin, and then imported into the program. That generated Go\ncode registers the service's schema in a global registry named\n[`protoregistry.GlobalFiles`](https://pkg.go.dev/google.golang.org/protobuf/reflect/protoregistry#Files).\n\nIf you are using Connect or gRPC handlers, there is nothing to worry about: the use of\ngenerated Go code for Connect and gRPC stubs and handler interfaces ensures that the\nservices are known.\n\n#### Dynamic Schemas\n\nIf you want to handle a service whose schema is _not_ known to the program at compile-time,\nthen you can use `vanguard.NewServiceWithSchema` and supply a\n[`protoreflect.ServiceDescriptor`](https://pkg.go.dev/google.golang.org/protobuf/reflect/protoreflect#ServiceDescriptor)\nthat defines the RPC schema. This descriptor could have been loaded from a configuration file\nor [file descriptor set](https://pkg.go.dev/google.golang.org/protobuf/reflect/protodesc#NewFiles),\ndownloaded from a server, such as a [Buf Schema Registry](https://github.com/bufbuild/reflect-proto),\nor even [compiled from Protobuf sources](https://github.com/bufbuild/protocompile#readme).\n\n#### Service Options\n\nBoth `vanguard.NewService` and `vanguard.NewServiceWithSchema` functions accept options\nfor configuring how a transcoder will process requests for that service. In particular,\nyou can configure the protocols, the codecs/message formats, and compression formats\nthat the service handler accepts.\n\nAs mentioned previously, the default configuration -- with no options -- is designed to\nwork out-of-the-box with Connect handlers: it assumes the handler can process Connect,\ngRPC, and gRPC-Web protocols; that it can process \"proto\" and \"json\" message formats; and\nthat it can handle \"gzip\" compression.\n\nYou can change this, including adding more message formats or compression algorithms,\nby supplying options:\n\n```go\n// In this example, we'll assume that proxyHandler functions as a reverse proxy\n// to a gRPC backend that only supports the gRPC protocol and the \"proto\" message\n// format.\notherService := vanguard.NewService(\n\t\"some.other.Service\",\n\tproxyHandler,\n\tvanguard.WithTargetProtocols(vanguard.ProtocolGRPC),\n\tvanguard.WithTargetCodecs(vanguard.CodecProto)\n)\n```\n\n### Building the Transcoder\n\nThe thing that does all of the work in Vanguard is a `*vanguard.Transcoder`. When the\ntranscoder is instantiated, you provide the set of services. The transcoder implements\n`http.Handler` and handles dispatching requests to the various configured service\nhandlers, and it also handles translating requests into a form that the handler\nunderstands. It then also translates responses into the form that the client is\nexpecting.\n\n```go\ntranscoder := vanguard.NewTranscoder([]*vanguard.Service{\n\tmyService,\n\totherService,\n})\n```\n\n#### Transcoder Options\n\nWhen creating a transcoder, you can supply options to customize it:\n* You can provide a custom handler for unknown endpoints. Without this, requests\n  for unrecognized URI paths will result in a simple \"404 Not Found\" response.\n* You can provide separate HTTP annotations. This is to provide REST-ful mappings\n  for RPC services that do not define the mapping in their Protobuf sources. It\n  also allows you to _add_ mappings to a service that does already have annotations.\n* You can add support for extra codecs, beyond \"proto\" and \"json\", and compression\n  algorithms, beyond \"gzip\".\n* You can supply a set of default service options that will apply to every service\n  (unless overridden via other options in a call to `NewService`).\n\n```go\ntranscoder = vanguard.NewTranscoder(\n\t[]*vanguard.Service{\n\t\tmyService,\n\t\totherService,\n\t},\n\tvanguard.WithUnknownHandler(custom404handler),\n\tvanguard.WithCodec(myCustomMessageFormat{}),\n)\n```\n\nThe use of `WithCodec` and `WithCompression` can also be used to replace the default\nimplementations of the \"proto\" and \"json\" codec or the \"gzip\" compression algorithm.\nNote that when replacing the \"json\" codec, you should use `*vanguard.JSONCodec` as the\nimplementation if you want to also support the REST protocol. REST-ful mappings can\nrequire message formatting features that are implemented by `*vanguard.JSONCodec` but\nnot in the base `vanguard.Codec` interface.\n\n#### gRPC Handlers\n\nYou can also wrap gRPC handlers with a `Transcoder`. This works a little differently\nthan wrapping Connect handlers. The generated Go code for gRPC does not include a handler\nfactory like Connect uses. It's generated functions instead register handler information\nwith a `*grpc.Server`.\n\nSo you would register the various service implementations with a `*grpc.Server`, which\nimplements `http.Handler` and can then be wrapped with a transcoder. But you can also\nuse the `vanguardgrpc` package to do everything in a single function call:\n\n```go\ntranscoder = vanguardgrpc.NewTranscoder(grpcServer)\n```\nWhen you use `vanguardgrpc.NewTranscoder`, it automatically creates a `*vanguard.Service`\nfor each service registered with the gRPC service, and supplies service options\nthat tell it to transcode to the gRPC protocol and the \"proto\" codec. If you also\ninstall a \"json\" codec for gRPC, it will configure the transcoder to also allow\nsending the \"json\" codec to the gRPC server, which makes handling of JSON Connect\nrequests much more efficient.\n\n### Wiring up to a server\n\nFinally, you can register the transcoder with an `http.Server` or\n`http.ServeMux`.\n\n```go\n// The Mux can be used as the sole handler for an HTTP server.\nerr := http.Serve(listener, transcoder)\n\n// Or it can be used alongside other handlers, all registered with\n// the same http.ServeMux.\nmux := http.NewServeMux()\nmux.Handle(\"/\", transcoder)\nerr := http.Serve(listener, mux)\n```\nThe above example registers the transcoder for the root path. This is useful\nto support REST requests for the service, which could have very different\npath URLs from those used for Connect and gRPC. Using a pattern this broad\nmeans the transcoder can handle all paths that might correspond to a method,\nwithout having to explicitly configure the `ServeMux` for the various paths\nnamed in HTTP transcoding annotations.\n\nFor the same reason, it is best to use a single vanguard transcoder, even if\nyour service supports many exposed RPC services, so that the transcoder can\ndispatch to the correct service based on the request. You can register many\nservices with the same transcoder. And if any need different configuration,\nthat can be handled by providing override options when the service is\ncreated.\n\n\n## Status: Alpha\n\nVanguard is undergoing initial development and is not yet stable.\n\n\n## Legal\n\nOffered under the [Apache 2 license][badges_license].\n\n\n[badges_license]: https://github.com/connectrpc/vanguard-go/blob/main/LICENSE\n[badges_slack]: https://buf.build/links/slack\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fconnectrpc%2Fvanguard-go","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fconnectrpc%2Fvanguard-go","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fconnectrpc%2Fvanguard-go/lists"}