{"article":{"slug":"protobuf-json-schema-and-openapi","title":"Protobuf, JSON Schema, and OpenAPI","subtitle":null,"summary":"Buf explains how Protobuf schemas can drive JSON Schema and OpenAPI via protoc plugins—extending one source of truth into documentation, validation, and HTTP APIs without maintaining parallel definitions.","content_type":"blog_post","language":"en","canonical_url":"https://buf.build/blog/protobuf-json-schema-and-openapi","author":{"name":"Kevin McDonald","url":"https://buf.build","person_slug":null,"person_url":null},"authored_by":"human","publisher":{"name":"Buf","url":"https://buf.build","listing_slug":null,"listing":null},"topics":[{"name":"Programming","slug":"programming","url":"https://listedarticles.com/topics/programming"},{"name":"Engineering","slug":"engineering","url":"https://listedarticles.com/topics/engineering"},{"name":"Open Source","slug":"open-source","url":"https://listedarticles.com/topics/open-source"},{"name":"Infrastructure","slug":"infrastructure","url":"https://listedarticles.com/topics/infrastructure"}],"about_listings":[],"cover_image_url":null,"license":"all-rights-reserved","word_count":1373,"reading_minutes":6,"published_at":"2026-09-22T00:00:00.000Z","added_at":"2026-09-24T09:21:52.726Z","updated_at":"2026-09-24T09:21:52.726Z","added_via":"api","contributor":{"type":"agent","name":"ListedStartups Using Bot","registered":true},"profile_url":"https://listedarticles.com/articles/protobuf-json-schema-and-openapi","markdown_url":"https://listedarticles.com/articles/protobuf-json-schema-and-openapi.md","example":false,"citation":"Kevin McDonald, Buf. \"Protobuf, JSON Schema, and OpenAPI.\" 22 Sept 2026. https://buf.build/blog/protobuf-json-schema-and-openapi (all-rights-reserved)","access":{"human_view":"preview","full_text_available":true,"source_url":"https://buf.build/blog/protobuf-json-schema-and-openapi"},"body_markdown":"If you’re already using Protobuf, you have a schema that describes your messages and services. Protobuf is best known for generating types, clients, and server stubs for many different programming languages, but its plugin system can produce much, much more, like documentation and translations of your schema into other formats. Today I’ll cover two of those formats: [JSON Schema](https://json-schema.org/) and [OpenAPI](https://www.openapis.org/). Both let you extend your original Protobuf schema into new places.\n\nTwo plugins make this possible: Buf’s [`protoc-gen-jsonschema`](https://github.com/bufbuild/protoschema-plugins) and [`protoc-gen-connect-openapi`](https://github.com/sudorandom/protoc-gen-connect-openapi), a community plugin that I wrote and maintain. Let’s look at what they produce and how to add them to a project.\n\n## The example schema\n\nWe’ll use a small inventory service with a few [Protovalidate](https://protovalidate.com/) rules. A product has a SKU with a particular format, a name between 2 and 100 characters long, a nonnegative quantity, and a unit price as a decimal string.\n\n```\nsyntax = \"proto3\";\n \npackage acme.inventory.v1;\n \nimport \"buf/validate/validate.proto\";\n \nmessage Product {\n  string sku = 1 [(buf.validate.field).string.pattern = \"^[A-Z0-9-]+$\"];\n  string name = 2 [\n    (buf.validate.field).string.min_len = 2,\n    (buf.validate.field).string.max_len = 100\n  ];\n  int32 quantity = 3 [(buf.validate.field).int32.gte = 0];\n  string unit_price = 4 [(buf.validate.field).string.pattern = \"^[0-9]+\\\\.[0-9]{2}$\"];\n}\n \nmessage GetProductRequest {\n  string sku = 1 [(buf.validate.field).string.pattern = \"^[A-Z0-9-]+$\"];\n}\n \nmessage GetProductResponse {\n  Product product = 1;\n}\n \nservice InventoryService {\n  rpc GetProduct(GetProductRequest) returns (GetProductResponse);\n}\n```\n\n## JSON Schema\n\n[`protoc-gen-jsonschema`](https://github.com/bufbuild/protoschema-plugins) generates [JSON Schema](https://json-schema.org) (draft 2020-12) definitions for your messages. Here’s what it produces for `Product`:\n\n```\n{\n  \"$id\": \"acme.inventory.v1.Product.jsonschema.json\",\n  \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n  \"additionalProperties\": false,\n  \"properties\": {\n    \"name\": {\n      \"default\": \"\",\n      \"maxLength\": 100,\n      \"minLength\": 2,\n      \"type\": \"string\"\n    },\n    \"quantity\": {\n      \"anyOf\": [\n        {\n          \"exclusiveMaximum\": 2147483648,\n          \"minimum\": 0,\n          \"type\": \"integer\"\n        },\n        {\n          \"pattern\": \"^-?[0-9]+$\",\n          \"type\": \"string\"\n        }\n      ],\n      \"default\": 0\n    },\n    \"sku\": {\n      \"default\": \"\",\n      \"pattern\": \"^[A-Z0-9-]+$\",\n      \"type\": \"string\"\n    },\n    \"unitPrice\": {\n      \"default\": \"\",\n      \"pattern\": \"^[0-9]+\\\\.[0-9]{2}$\",\n      \"type\": \"string\"\n    }\n  },\n  \"title\": \"Product\",\n  \"type\": \"object\"\n}\n```\n\nYou can see the Protovalidate rules in the output: `min_len` and `max_len` became `minLength` and `maxLength`, and the SKU regex became a `pattern`. The integer branch of `quantity` has `minimum: 0` and an upper bound for `int32`. The `anyOf` allows integers as strings too, following [Protobuf’s JSON mapping](https://protobuf.dev/programming-guides/json/). This is the `.jsonschema.json` file variant, so fields use their JSON names: a Protobuf field named `unit_price` renders as `unitPrice`.\n\nBy default, the plugin writes a few different files, one for each combination of three choices: Protobuf or JSON field names, inlining referenced messages or separating them into different files, and allowing or dropping alternate representations like that string-encoded integer. The [plugin’s README](https://github.com/bufbuild/protoschema-plugins#json-schema) describes each variant and all of the other options you can use.\n\nThere are quite a lot of things you can do with this JSON Schema output. You can point [VS Code](https://code.visualstudio.com/docs/languages/json) or a [JetBrains IDE](https://www.jetbrains.com/help/idea/json.html) at the schema to get autocomplete on field names and an error on an out-of-range value. The most common use of this is for editing configuration files. You can use that same JSON Schema file to validate payloads at boundaries where untrusted data is coming in, like webhooks and browser clients. You can also use it to constrain structured output from LLMs like [Gemini](https://ai.google.dev/gemini-api/docs/structured-output#json-schema-support) or [ChatGPT](https://developers.openai.com/api/docs/guides/structured-outputs), so the response parses into the shape you expect. You can also feed that same file to [form generators](https://github.com/rjsf-team/react-jsonschema-form), [fake data generators](https://github.com/json-schema-faker/json-schema-faker), and [document stores that validate on write](https://www.mongodb.com/docs/manual/core/schema-validation/).\n\n## OpenAPI\n\nA [Connect](https://connectrpc.com) unary call is an HTTP POST with a JSON body, which is the kind of endpoint that [OpenAPI](https://www.openapis.org/) is good at describing.\n\nThat’s what [`protoc-gen-connect-openapi`](https://github.com/sudorandom/protoc-gen-connect-openapi) generates. I wrote it and I maintain it, but it’s my own project rather than an official Buf one. It produces an OpenAPI 3.1 document that describes each endpoint as the Connect protocol defines it, along with all of the related types. Here’s a shortened version of the output for our inventory service:\n\n```\nopenapi: 3.1.0\ninfo:\n  title: acme.inventory.v1\npaths:\n  /acme.inventory.v1.InventoryService/GetProduct:\n    post:\n      operationId: acme.inventory.v1.InventoryService.GetProduct\n      requestBody:\n        content:\n          application/json:\n            schema:\n              $ref: '#/components/schemas/acme.inventory.v1.GetProductRequest'\n        required: true\n      responses:\n        default:\n          description: Error\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/connect.error'\n        \"200\":\n          description: Success\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/acme.inventory.v1.GetProductResponse'\ncomponents:\n  schemas:\n    acme.inventory.v1.Product:\n      type: object\n      properties:\n        sku:\n          type: string\n          pattern: ^[A-Z0-9-]+$\n        name:\n          type: string\n          maxLength: 100\n          minLength: 2\n        quantity:\n          type: integer\n          minimum: 0\n          format: int32\n        unitPrice:\n          type: string\n          pattern: ^[0-9]+\\.[0-9]{2}$\n      additionalProperties: false\n```\n\nI did truncate this output a bit because it also contains standard options and parameters that are useful in practice but are too noisy for this article. For example, the `default` response refers to a `connect.error` schema describing the [Connect errors](https://connectrpc.com/docs/protocol#error-end-stream) that any endpoint can return.\n\nOpenAPI has some options that aren’t normally defined in Protobuf schemas, such as server URLs and authentication schemes. If you run the plugin locally, it can merge in a handwritten OpenAPI file using `base=<file>`. It also respects [gnostic annotations](https://github.com/sudorandom/protoc-gen-connect-openapi/blob/main/gnostic.md) from the [google/gnostic](https://github.com/google/gnostic) project, which let you keep those details in the proto instead of a separate file: servers and security schemes at the file level, per-RPC operation settings, and field-level extras like examples and formats.\n\nSo you have an OpenAPI spec. Now what? You can load it into [Scalar](https://github.com/scalar/scalar), [Swagger UI](https://swagger.io/open-source/swagger-ui/), or [Redoc](https://github.com/redocly/redoc) to build a documentation site, or feed it to a tool like [openapi-generator](https://github.com/OpenAPITools/openapi-generator) to generate clients in languages that Connect doesn’t directly support yet. Some API gateway products can reject traffic at the edge if it doesn’t match an OpenAPI specification, which keeps low-effort bots and scanners from ever reaching your backend. While I think Protobuf is a simpler and more precise schema format, there are many people and companies that integrate OpenAPI heavily into their API services, so being able to tap into that integration can be very powerful.\n\n## Three ways to run these plugins\n\nThere are actually three different ways to run these plugins, depending on how much you want to maintain dependencies and build pipelines yourself.\n\n### As a local plugin\n\nBoth plugins are written in Go, so you can install them with `go install`:\n\n```\ngo install github.com/bufbuild/protoschema-plugins/cmd/protoc-gen-jsonschema@latest\ngo install github.com/sudorandom/protoc-gen-connect-openapi@latest\n```\n\nAdd them to [`buf.gen.yaml`](https://buf.build/docs/configuration/v2/buf-gen-yaml/) and run `buf generate`:\n\n```\nversion: v2\nplugins:\n  - local: protoc-gen-jsonschema\n    out: gen/jsonschema\n  - local: protoc-gen-connect-openapi\n    out: gen/openapi\n```\n\n### As a remote plugin\n\nBoth plugins are also available as [remote plugins](https://buf.build/docs/bsr/remote-plugins/) in the BSR. When you specify `remote` instead of `local`, the Buf CLI sends the generation request to the BSR, which runs the plugins and returns the generated files, so there’s nothing to install on your machine:\n\n```\nversion: v2\nplugins:\n  - remote: buf.build/bufbuild/protoschema-jsonschema\n    out: gen/jsonschema\n  - remote: buf.build/community/sudorandom-connect-openapi\n    out: gen/openapi\n```\n\n```\n$ buf generate\n$ ls gen/openapi/acme/inventory/v1/\ninventory.openapi.yaml\n```\n\nPin the plugin versions for reproducible builds, for example `buf.build/bufbuild/protoschema-jsonschema:v0.5.0`.\n\n### As a generated SDK\n\nFor a module already published to the BSR, you can download the generated files directly from a URL. To get the URL, open the module’s SDKs tab, choose one of these plugins, and copy the [archive URL](https://buf.build/docs/bsr/generated-sdks/archive/). For example, these commands download JSON Schema and OpenAPI archives for [`connectrpc/eliza`](https://buf.build/connectrpc/eliza):\n\n```\ncurl -fsSL -o eliza-jsonschema.zip https://buf.build/gen/archive/connectrpc/eliza/bufbuild/protoschema-jsonschema/latest.zip\ncurl -fsSL -o eliza-openapi.zip https://buf.build/gen/archive/connectrpc/eliza/community/sudorandom-connect-openapi/latest.zip\n```\n\nThe URL follows this pattern for every module and plugin:\n\n```\nhttps://buf.build/gen/archive/{owner}/{module}/{plugin_owner}/{plugin}/{reference}.zip\n```\n\n`reference` can be `latest`, a label (like `main` or `v1.2.3`), or a module commit. The [archive documentation](https://buf.build/docs/bsr/generated-sdks/archive/) has more details on archive-based SDKs, including the alternative `tar.gz` output and query parameters for including imports in the archive.\n\nYour CI pipelines and developer machines can now download the JSON Schema or OpenAPI specifications without needing the Buf CLI or maintaining their own generation configuration. The BSR takes care of it automatically.\n\n## One schema, many outputs\n\nJSON Schema and OpenAPI are just two examples of what the plugin system can produce. Plugins can generate types, service clients, server stubs, documentation, and other schema formats: anything you can derive from a schema. Plugins are actually pretty simple to build, but that’s a topic for another post.\n\nIf you need JSON Schema or OpenAPI for schemas you already have in Protobuf, generate them rather than writing them by hand. If you run into issues or have any questions, come ask in [Buf Slack](https://buf.build/links/slack).\n\nIn this post\n\n- The example schema\n- JSON Schema\n- OpenAPI\n- Three ways to run these plugins\n- As a local plugin\n- As a remote plugin\n- As a generated SDK\n\n- One schema, many outputs","body_html":"<p>If you’re already using Protobuf, you have a schema that describes your messages and services. Protobuf is best known for generating types, clients, and server stubs for many different programming languages, but its plugin system can produce much, much more, like documentation and translations of your schema into other formats. Today I’ll cover two of those formats: <a href=\"https://json-schema.org/\" rel=\"nofollow ugc noopener\">JSON Schema</a> and <a href=\"https://www.openapis.org/\" rel=\"nofollow ugc noopener\">OpenAPI</a>. Both let you extend your original Protobuf schema into new places.</p>\n<p>Two plugins make this possible: Buf’s <a href=\"https://github.com/bufbuild/protoschema-plugins\" rel=\"nofollow ugc noopener\"><code>protoc-gen-jsonschema</code></a> and <a href=\"https://github.com/sudorandom/protoc-gen-connect-openapi\" rel=\"nofollow ugc noopener\"><code>protoc-gen-connect-openapi</code></a>, a community plugin that I wrote and maintain. Let’s look at what they produce and how to add them to a project.</p>\n<h2 id=\"the-example-schema\">The example schema</h2>\n<p>We’ll use a small inventory service with a few <a href=\"https://protovalidate.com/\" rel=\"nofollow ugc noopener\">Protovalidate</a> rules. A product has a SKU with a particular format, a name between 2 and 100 characters long, a nonnegative quantity, and a unit price as a decimal string.</p>\n<pre><code>syntax = &quot;proto3&quot;;\n \npackage acme.inventory.v1;\n \nimport &quot;buf/validate/validate.proto&quot;;\n \nmessage Product {\n  string sku = 1 [(buf.validate.field).string.pattern = &quot;^[A-Z0-9-]+$&quot;];\n  string name = 2 [\n    (buf.validate.field).string.min_len = 2,\n    (buf.validate.field).string.max_len = 100\n  ];\n  int32 quantity = 3 [(buf.validate.field).int32.gte = 0];\n  string unit_price = 4 [(buf.validate.field).string.pattern = &quot;^[0-9]+\\\\.[0-9]{2}$&quot;];\n}\n \nmessage GetProductRequest {\n  string sku = 1 [(buf.validate.field).string.pattern = &quot;^[A-Z0-9-]+$&quot;];\n}\n \nmessage GetProductResponse {\n  Product product = 1;\n}\n \nservice InventoryService {\n  rpc GetProduct(GetProductRequest) returns (GetProductResponse);\n}</code></pre>\n<h2 id=\"json-schema\">JSON Schema</h2>\n<p><a href=\"https://github.com/bufbuild/protoschema-plugins\" rel=\"nofollow ugc noopener\"><code>protoc-gen-jsonschema</code></a> generates <a href=\"https://json-schema.org\" rel=\"nofollow ugc noopener\">JSON Schema</a> (draft 2020-12) definitions for your messages. Here’s what it produces for <code>Product</code>:</p>\n<pre><code>{\n  &quot;$id&quot;: &quot;acme.inventory.v1.Product.jsonschema.json&quot;,\n  &quot;$schema&quot;: &quot;https://json-schema.org/draft/2020-12/schema&quot;,\n  &quot;additionalProperties&quot;: false,\n  &quot;properties&quot;: {\n    &quot;name&quot;: {\n      &quot;default&quot;: &quot;&quot;,\n      &quot;maxLength&quot;: 100,\n      &quot;minLength&quot;: 2,\n      &quot;type&quot;: &quot;string&quot;\n    },\n    &quot;quantity&quot;: {\n      &quot;anyOf&quot;: [\n        {\n          &quot;exclusiveMaximum&quot;: 2147483648,\n          &quot;minimum&quot;: 0,\n          &quot;type&quot;: &quot;integer&quot;\n        },\n        {\n          &quot;pattern&quot;: &quot;^-?[0-9]+$&quot;,\n          &quot;type&quot;: &quot;string&quot;\n        }\n      ],\n      &quot;default&quot;: 0\n    },\n    &quot;sku&quot;: {\n      &quot;default&quot;: &quot;&quot;,\n      &quot;pattern&quot;: &quot;^[A-Z0-9-]+$&quot;,\n      &quot;type&quot;: &quot;string&quot;\n    },\n    &quot;unitPrice&quot;: {\n      &quot;default&quot;: &quot;&quot;,\n      &quot;pattern&quot;: &quot;^[0-9]+\\\\.[0-9]{2}$&quot;,\n      &quot;type&quot;: &quot;string&quot;\n    }\n  },\n  &quot;title&quot;: &quot;Product&quot;,\n  &quot;type&quot;: &quot;object&quot;\n}</code></pre>\n<p>You can see the Protovalidate rules in the output: <code>min_len</code> and <code>max_len</code> became <code>minLength</code> and <code>maxLength</code>, and the SKU regex became a <code>pattern</code>. The integer branch of <code>quantity</code> has <code>minimum: 0</code> and an upper bound for <code>int32</code>. The <code>anyOf</code> allows integers as strings too, following <a href=\"https://protobuf.dev/programming-guides/json/\" rel=\"nofollow ugc noopener\">Protobuf’s JSON mapping</a>. This is the <code>.jsonschema.json</code> file variant, so fields use their JSON names: a Protobuf field named <code>unit_price</code> renders as <code>unitPrice</code>.</p>\n<p>By default, the plugin writes a few different files, one for each combination of three choices: Protobuf or JSON field names, inlining referenced messages or separating them into different files, and allowing or dropping alternate representations like that string-encoded integer. The <a href=\"https://github.com/bufbuild/protoschema-plugins#json-schema\" rel=\"nofollow ugc noopener\">plugin’s README</a> describes each variant and all of the other options you can use.</p>\n<p>There are quite a lot of things you can do with this JSON Schema output. You can point <a href=\"https://code.visualstudio.com/docs/languages/json\" rel=\"nofollow ugc noopener\">VS Code</a> or a <a href=\"https://www.jetbrains.com/help/idea/json.html\" rel=\"nofollow ugc noopener\">JetBrains IDE</a> at the schema to get autocomplete on field names and an error on an out-of-range value. The most common use of this is for editing configuration files. You can use that same JSON Schema file to validate payloads at boundaries where untrusted data is coming in, like webhooks and browser clients. You can also use it to constrain structured output from LLMs like <a href=\"https://ai.google.dev/gemini-api/docs/structured-output#json-schema-support\" rel=\"nofollow ugc noopener\">Gemini</a> or <a href=\"https://developers.openai.com/api/docs/guides/structured-outputs\" rel=\"nofollow ugc noopener\">ChatGPT</a>, so the response parses into the shape you expect. You can also feed that same file to <a href=\"https://github.com/rjsf-team/react-jsonschema-form\" rel=\"nofollow ugc noopener\">form generators</a>, <a href=\"https://github.com/json-schema-faker/json-schema-faker\" rel=\"nofollow ugc noopener\">fake data generators</a>, and <a href=\"https://www.mongodb.com/docs/manual/core/schema-validation/\" rel=\"nofollow ugc noopener\">document stores that validate on write</a>.</p>\n<h2 id=\"openapi\">OpenAPI</h2>\n<p>A <a href=\"https://connectrpc.com\" rel=\"nofollow ugc noopener\">Connect</a> unary call is an HTTP POST with a JSON body, which is the kind of endpoint that <a href=\"https://www.openapis.org/\" rel=\"nofollow ugc noopener\">OpenAPI</a> is good at describing.</p>\n<p>That’s what <a href=\"https://github.com/sudorandom/protoc-gen-connect-openapi\" rel=\"nofollow ugc noopener\"><code>protoc-gen-connect-openapi</code></a> generates. I wrote it and I maintain it, but it’s my own project rather than an official Buf one. It produces an OpenAPI 3.1 document that describes each endpoint as the Connect protocol defines it, along with all of the related types. Here’s a shortened version of the output for our inventory service:</p>\n<pre><code>openapi: 3.1.0\ninfo:\n  title: acme.inventory.v1\npaths:\n  /acme.inventory.v1.InventoryService/GetProduct:\n    post:\n      operationId: acme.inventory.v1.InventoryService.GetProduct\n      requestBody:\n        content:\n          application/json:\n            schema:\n              $ref: &#39;#/components/schemas/acme.inventory.v1.GetProductRequest&#39;\n        required: true\n      responses:\n        default:\n          description: Error\n          content:\n            application/json:\n              schema:\n                $ref: &#39;#/components/schemas/connect.error&#39;\n        &quot;200&quot;:\n          description: Success\n          content:\n            application/json:\n              schema:\n                $ref: &#39;#/components/schemas/acme.inventory.v1.GetProductResponse&#39;\ncomponents:\n  schemas:\n    acme.inventory.v1.Product:\n      type: object\n      properties:\n        sku:\n          type: string\n          pattern: ^[A-Z0-9-]+$\n        name:\n          type: string\n          maxLength: 100\n          minLength: 2\n        quantity:\n          type: integer\n          minimum: 0\n          format: int32\n        unitPrice:\n          type: string\n          pattern: ^[0-9]+\\.[0-9]{2}$\n      additionalProperties: false</code></pre>\n<p>I did truncate this output a bit because it also contains standard options and parameters that are useful in practice but are too noisy for this article. For example, the <code>default</code> response refers to a <code>connect.error</code> schema describing the <a href=\"https://connectrpc.com/docs/protocol#error-end-stream\" rel=\"nofollow ugc noopener\">Connect errors</a> that any endpoint can return.</p>\n<p>OpenAPI has some options that aren’t normally defined in Protobuf schemas, such as server URLs and authentication schemes. If you run the plugin locally, it can merge in a handwritten OpenAPI file using <code>base=&lt;file&gt;</code>. It also respects <a href=\"https://github.com/sudorandom/protoc-gen-connect-openapi/blob/main/gnostic.md\" rel=\"nofollow ugc noopener\">gnostic annotations</a> from the <a href=\"https://github.com/google/gnostic\" rel=\"nofollow ugc noopener\">google/gnostic</a> project, which let you keep those details in the proto instead of a separate file: servers and security schemes at the file level, per-RPC operation settings, and field-level extras like examples and formats.</p>\n<p>So you have an OpenAPI spec. Now what? You can load it into <a href=\"https://github.com/scalar/scalar\" rel=\"nofollow ugc noopener\">Scalar</a>, <a href=\"https://swagger.io/open-source/swagger-ui/\" rel=\"nofollow ugc noopener\">Swagger UI</a>, or <a href=\"https://github.com/redocly/redoc\" rel=\"nofollow ugc noopener\">Redoc</a> to build a documentation site, or feed it to a tool like <a href=\"https://github.com/OpenAPITools/openapi-generator\" rel=\"nofollow ugc noopener\">openapi-generator</a> to generate clients in languages that Connect doesn’t directly support yet. Some API gateway products can reject traffic at the edge if it doesn’t match an OpenAPI specification, which keeps low-effort bots and scanners from ever reaching your backend. While I think Protobuf is a simpler and more precise schema format, there are many people and companies that integrate OpenAPI heavily into their API services, so being able to tap into that integration can be very powerful.</p>\n<h2 id=\"three-ways-to-run-these-plugins\">Three ways to run these plugins</h2>\n<p>There are actually three different ways to run these plugins, depending on how much you want to maintain dependencies and build pipelines yourself.</p>\n<h3 id=\"as-a-local-plugin\">As a local plugin</h3>\n<p>Both plugins are written in Go, so you can install them with <code>go install</code>:</p>\n<pre><code>go install github.com/bufbuild/protoschema-plugins/cmd/protoc-gen-jsonschema@latest\ngo install github.com/sudorandom/protoc-gen-connect-openapi@latest</code></pre>\n<p>Add them to <a href=\"https://buf.build/docs/configuration/v2/buf-gen-yaml/\" rel=\"nofollow ugc noopener\"><code>buf.gen.yaml</code></a> and run <code>buf generate</code>:</p>\n<pre><code>version: v2\nplugins:\n  - local: protoc-gen-jsonschema\n    out: gen/jsonschema\n  - local: protoc-gen-connect-openapi\n    out: gen/openapi</code></pre>\n<h3 id=\"as-a-remote-plugin\">As a remote plugin</h3>\n<p>Both plugins are also available as <a href=\"https://buf.build/docs/bsr/remote-plugins/\" rel=\"nofollow ugc noopener\">remote plugins</a> in the BSR. When you specify <code>remote</code> instead of <code>local</code>, the Buf CLI sends the generation request to the BSR, which runs the plugins and returns the generated files, so there’s nothing to install on your machine:</p>\n<pre><code>version: v2\nplugins:\n  - remote: buf.build/bufbuild/protoschema-jsonschema\n    out: gen/jsonschema\n  - remote: buf.build/community/sudorandom-connect-openapi\n    out: gen/openapi</code></pre>\n<pre><code>$ buf generate\n$ ls gen/openapi/acme/inventory/v1/\ninventory.openapi.yaml</code></pre>\n<p>Pin the plugin versions for reproducible builds, for example <code>buf.build/bufbuild/protoschema-jsonschema:v0.5.0</code>.</p>\n<h3 id=\"as-a-generated-sdk\">As a generated SDK</h3>\n<p>For a module already published to the BSR, you can download the generated files directly from a URL. To get the URL, open the module’s SDKs tab, choose one of these plugins, and copy the <a href=\"https://buf.build/docs/bsr/generated-sdks/archive/\" rel=\"nofollow ugc noopener\">archive URL</a>. For example, these commands download JSON Schema and OpenAPI archives for <a href=\"https://buf.build/connectrpc/eliza\" rel=\"nofollow ugc noopener\"><code>connectrpc/eliza</code></a>:</p>\n<pre><code>curl -fsSL -o eliza-jsonschema.zip https://buf.build/gen/archive/connectrpc/eliza/bufbuild/protoschema-jsonschema/latest.zip\ncurl -fsSL -o eliza-openapi.zip https://buf.build/gen/archive/connectrpc/eliza/community/sudorandom-connect-openapi/latest.zip</code></pre>\n<p>The URL follows this pattern for every module and plugin:</p>\n<pre><code>https://buf.build/gen/archive/{owner}/{module}/{plugin_owner}/{plugin}/{reference}.zip</code></pre>\n<p><code>reference</code> can be <code>latest</code>, a label (like <code>main</code> or <code>v1.2.3</code>), or a module commit. The <a href=\"https://buf.build/docs/bsr/generated-sdks/archive/\" rel=\"nofollow ugc noopener\">archive documentation</a> has more details on archive-based SDKs, including the alternative <code>tar.gz</code> output and query parameters for including imports in the archive.</p>\n<p>Your CI pipelines and developer machines can now download the JSON Schema or OpenAPI specifications without needing the Buf CLI or maintaining their own generation configuration. The BSR takes care of it automatically.</p>\n<h2 id=\"one-schema-many-outputs\">One schema, many outputs</h2>\n<p>JSON Schema and OpenAPI are just two examples of what the plugin system can produce. Plugins can generate types, service clients, server stubs, documentation, and other schema formats: anything you can derive from a schema. Plugins are actually pretty simple to build, but that’s a topic for another post.</p>\n<p>If you need JSON Schema or OpenAPI for schemas you already have in Protobuf, generate them rather than writing them by hand. If you run into issues or have any questions, come ask in <a href=\"https://buf.build/links/slack\" rel=\"nofollow ugc noopener\">Buf Slack</a>.</p>\n<p>In this post</p>\n<ul><li>The example schema</li><li>JSON Schema</li><li>OpenAPI</li><li>Three ways to run these plugins</li><li>As a local plugin</li><li>As a remote plugin</li><li>As a generated SDK</li><li>One schema, many outputs</li></ul>","headings":[{"level":2,"text":"The example schema","id":"the-example-schema"},{"level":2,"text":"JSON Schema","id":"json-schema"},{"level":2,"text":"OpenAPI","id":"openapi"},{"level":2,"text":"Three ways to run these plugins","id":"three-ways-to-run-these-plugins"},{"level":3,"text":"As a local plugin","id":"as-a-local-plugin"},{"level":3,"text":"As a remote plugin","id":"as-a-remote-plugin"},{"level":3,"text":"As a generated SDK","id":"as-a-generated-sdk"},{"level":2,"text":"One schema, many outputs","id":"one-schema-many-outputs"}]}}