<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Steven Jenkins De Haro</title>
    <description>The latest articles on DEV Community by Steven Jenkins De Haro (@stevenjdh).</description>
    <link>https://dev.to/stevenjdh</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F497212%2Feffcc4ea-c5c9-44de-8b14-d3f6a7ca878c.jpeg</url>
      <title>DEV Community: Steven Jenkins De Haro</title>
      <link>https://dev.to/stevenjdh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/stevenjdh"/>
    <language>en</language>
    <item>
      <title>Demystifying Confluent's Schema Registry Wire Format</title>
      <dc:creator>Steven Jenkins De Haro</dc:creator>
      <pubDate>Sun, 09 Nov 2025 11:52:46 +0000</pubDate>
      <link>https://dev.to/stevenjdh/demystifying-confluents-schema-registry-wire-format-5465</link>
      <guid>https://dev.to/stevenjdh/demystifying-confluents-schema-registry-wire-format-5465</guid>
      <description>&lt;p&gt;&lt;strong&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;A brief overview on Avro, JSON, and Protobuf&lt;/li&gt;
&lt;li&gt;What is Confluent's wire format?&lt;/li&gt;
&lt;li&gt;
Debugging the wire format

&lt;ul&gt;
&lt;li&gt;Prepare the source data&lt;/li&gt;
&lt;li&gt;Analyze the message contents&lt;/li&gt;
&lt;li&gt;Debugging challenge&lt;/li&gt;
&lt;li&gt;Additional commands&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Third-party wire format compatibility&lt;/li&gt;

&lt;li&gt;

Manually handling the wire format

&lt;ul&gt;
&lt;li&gt;Producer code snippet&lt;/li&gt;
&lt;li&gt;Consumer code snippet&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Best practices &amp;amp; tips&lt;/li&gt;

&lt;li&gt;Conclusion&lt;/li&gt;

&lt;li&gt;Resources&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;&lt;a&gt;&lt;/a&gt;Within Kafka-based architectures, message serialization is more than just a format choice, it defines the contract between producers and consumers. When that contract is paired with a schema registry, it is enhanced with features like efficient schema evolution, versioning, and compatibility checks. However, these benefits are more easily realized when both sides of the data exchange use it. When a client does not or cannot use the schema registry, or when the integration with Kafka happens via a REST API, there will be additional considerations at the byte level to take into account.&lt;/p&gt;

&lt;p&gt;To address these scenarios, this article provides a detailed breakdown of Confluent's Schema Registry wire format and how to use it without integrating with a schema registry. In addition, it will demonstrate how to inspect and decode Kafka messages using tools like xxd, dd, and low-level byte parsing for clearer insight into how data flows through Kafka topics.&lt;/p&gt;




&lt;h2&gt;
  
  
  A brief overview on Avro, JSON, and Protobuf&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Apache Avro, JSON, and Protobuf formats are widely used data serialization technologies that serve slightly different purposes and offer contrasting trade-offs in efficiency, readability, and interoperability.&lt;/p&gt;

&lt;p&gt;Avro is a row-oriented, binary format that can optionally embed schemas with the data (or reference it externally), enabling fast, compact, and strongly typed data exchange. It's well-suited for high-throughput pipelines and big data applications due to its small payload size and efficient encoding. JSON, in contrast, is a human-readable, text-based format that prioritizes simplicity and interoperability, ideal for web APIs and configuration files, though its verbosity results in larger payloads and slower parsing compared to binary formats. Protobuf (Protocol Buffers), developed by Google, is a compact binary format similar to Avro but uses numeric field tags instead of field names. Schemas are defined in &lt;code&gt;.proto&lt;/code&gt; files that are later compiled into code, providing efficient encoding and making it a popular choice for microservices, APIs, and inter-service communication.&lt;/p&gt;

&lt;p&gt;Below is a simple comparison of the test record &lt;code&gt;{ "message": "Hello World!" }&lt;/code&gt; to illustrate the size differences between formats:&lt;/p&gt;

&lt;p&gt;📊 Size Comparison&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Format&lt;/th&gt;
&lt;th&gt;Raw&lt;/th&gt;
&lt;th&gt;Base64&lt;/th&gt;
&lt;th&gt;Base64 Encoded Raw Payload&lt;/th&gt;
&lt;th&gt;Meta&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;JSON&lt;/td&gt;
&lt;td&gt;29 B&lt;/td&gt;
&lt;td&gt;40 B&lt;/td&gt;
&lt;td&gt;eyAibWVzc2FnZSI6ICJIZWxsbyBXb3JsZCEiIH0=&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Protobuf&lt;/td&gt;
&lt;td&gt;14 B&lt;/td&gt;
&lt;td&gt;20 B&lt;/td&gt;
&lt;td&gt;CgxIZWxsbyBXb3JsZCE=&lt;/td&gt;
&lt;td&gt;0x0a 0x0c&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Avro&lt;/td&gt;
&lt;td&gt;13 B&lt;/td&gt;
&lt;td&gt;20 B&lt;/td&gt;
&lt;td&gt;GEhlbGxvIFdvcmxkIQ==&lt;/td&gt;
&lt;td&gt;0x18&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;📝 &lt;strong&gt;Note:&lt;/strong&gt; For the Avro length prefix, the &lt;code&gt;string&lt;/code&gt; value is encoded as a &lt;code&gt;long&lt;/code&gt; using &lt;a href="https://protobuf.dev/programming-guides/encoding/#types" rel="noopener noreferrer"&gt;ZigZag encoding&lt;/a&gt; (

&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;length in bytes×2\text{length in bytes} × 2&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord text"&gt;&lt;span class="mord"&gt;length in bytes&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;×&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
), and then written as a &lt;a href="https://lucene.apache.org/core/3_5_0/fileformats.html#VInt" rel="noopener noreferrer"&gt;variable-length integer (varint/LEB128)&lt;/a&gt;. The same encoding is applied to all signed numeric types to make negative numbers efficient by using fewer bytes to represent them. See &lt;a href="https://avro.apache.org/docs/1.11.1/specification/#data-serialization-and-deserialization" rel="noopener noreferrer"&gt;Data Serialization and Deserialization&lt;/a&gt; for more information.&lt;/p&gt;

&lt;p&gt;As can be seen above, Avro is the most compact in this example. This is mainly because the schema is referenced externally, including the field names. However, had the schema been embedded, it would have been around 159 raw bytes in size. Protobuf, on the other hand, encodes each field in the payload using a tag encoded with &lt;a href="https://protobuf.dev/programming-guides/encoding/#structure" rel="noopener noreferrer"&gt;Protobuf wire format&lt;/a&gt;, which contains the field number and wire type. For length-delimited types, like &lt;code&gt;strings&lt;/code&gt; or Embedded Messages, the tag is immediately followed by a length prefix and then the serialized data. This structure makes Protobuf messages partially self-describing, allowing known fields to be parsed without having the compiled &lt;code&gt;.proto&lt;/code&gt; files in a pinch. With all this in mind, it's now time to discuss the wire format to understand where schemas and payloads fit in.&lt;/p&gt;


&lt;h2&gt;
  
  
  What is Confluent's wire format?&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;The wire format defines a fixed binary layout for serialized messages that will be sent over the network. It’s not tied to Kafka itself, but rather how Confluent's Schema Registry, and compatible systems, encode metadata alongside the actual payload. In other words, instead of producing only a raw Avro, JSON, or Protobuf payload into a topic, an additional header structure is prepended to the payload. This header tells the consumer which schema to use, and in the case of Protobuf, which top-level message within that schema. The payload can then be deserialized correctly regardless of the consumer's language or platform as long as it has access to the Schema Registry and the corresponding schema ID.&lt;/p&gt;

&lt;p&gt;The following is a high-level view of the wire format:&lt;/p&gt;

&lt;p&gt;💾 For Arvo and JSON payloads:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+----------------------+-------------------+
| Byte Offset          | Content           |
+----------------------+-------------------+
| 0                    | Magic byte (0x00) |
| 1 – 4                | Schema ID (int32) |
| 5 – end              | Avro/JSON payload |
+----------------------+-------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;💾 For Protobuf payloads:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+----------------------+----------------------------------------+
| Byte Offset          | Content                                |
+----------------------+----------------------------------------+
| 0                    | Magic byte (0x00)                      |
| 1 – 4                | Schema ID (int32)                      |
| 5 – (5+N-1)          | Protobuf message index (varint)        |
| (5+N) – end          | Protobuf payload (field tags + values) |
+----------------------+----------------------------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Within the wire format, the first byte is called the Magic Byte (&lt;code&gt;0x00&lt;/code&gt;), which is just an indicator to say that the wire format is being used, otherwise, a consumer may throw a serialization exception for an unknown magic byte for raw payloads. This section is followed by 4 bytes to store the schema ID from the schema registry in the form of a big-endian signed 32-bit integer. For Protobuf payloads, the next section is for the message index. It is used to identify which top-level message found in the associated &lt;code&gt;.proto&lt;/code&gt; file is encoded. This byte is also varint-encoded and is usually &lt;code&gt;0x00&lt;/code&gt; if there is only one message. The final section stores the actual raw payload.&lt;/p&gt;




&lt;h2&gt;
  
  
  Debugging the wire format&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;It's useful to understand how to debug a Kafka message to validate the structure/format of the data being exchanged to see if it meets expectations. For example, to be able to identify a message that uses the wire format versus one that does not. For this, the &lt;a href="https://linux.die.net/man/1/xxd" rel="noopener noreferrer"&gt;xxd&lt;/a&gt; CLI tool will be used as it comes preinstalled with most operating systems, including Windows via git-bash.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prepare the source data&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;To begin, an Avro sample message using the wire format is needed. Use one of the following options to generate or dump an existing sample:&lt;/p&gt;

&lt;p&gt;⚙️ Option 1: Generate&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'\x00\x00\x00\x00\x01\x18\x48\x65\x6c\x6c\x6f\x20\x57\x6f\x72\x6c\x64\x21'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; avro-message.bin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The generated message above is associated with the following dummy schema below with a schema ID of 1:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"record"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Greeting"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"namespace"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"com.example.messages"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"fields"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;⚙️ Option 2: Kafka CLI&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Depending on package, it might be './bin/kafka-console-consumer.sh ...'.&lt;/span&gt;
/bin/kafka-console-consumer &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;--bootstrap-server&lt;/span&gt; &amp;lt;BOOTSTRAP_URL&amp;gt;:&amp;lt;PORT&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;--topic&lt;/span&gt; &amp;lt;YOUR_TOPIC&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;--from-beginning&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;--max-messages&lt;/span&gt; 1 &lt;span class="se"&gt;\&lt;/span&gt;
     | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'\n'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; avro-message.bin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;📝 &lt;strong&gt;Note:&lt;/strong&gt; The last part of the command removes the &lt;code&gt;0x0a&lt;/code&gt; byte that may appear at the end due to a new line introduced by the command.&lt;/p&gt;

&lt;p&gt;⚙️ Option 3: kcat CLI (formerly known as kafkacat &amp;lt; 1.7.0)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Alternative 'docker run -it --rm edenhill/kafkacat:1.6.0 kafkacat -b ...'.&lt;/span&gt;
kcat &lt;span class="nt"&gt;-b&lt;/span&gt; &amp;lt;BOOTSTRAP_URL&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-t&lt;/span&gt; &amp;lt;YOUR_TOPIC&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; 1 &lt;span class="nt"&gt;-o&lt;/span&gt; beginning &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; avro-message.bin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Analyze the message contents&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;With the &lt;code&gt;avro-message.bin&lt;/code&gt; now in hand, it's time to debug its contents. Execute the following command to view the hex representation of the data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;xxd &lt;span class="nt"&gt;-g&lt;/span&gt; 1 &lt;span class="nt"&gt;-c&lt;/span&gt; 16 ./avro-message.bin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🔳 &lt;strong&gt;Output&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight mathematica"&gt;&lt;code&gt;&lt;span class="m"&gt;00000000&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;01&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;18&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;48&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;65&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="nv"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="nv"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="nv"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;20&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;57&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="nv"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;72&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="nv"&gt;c&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;......&lt;/span&gt;&lt;span class="nv"&gt;Hello&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Worl&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="m"&gt;00000010&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;64&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;21&lt;/span&gt;&lt;span class="w"&gt;                                            &lt;/span&gt;&lt;span class="nv"&gt;d&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;📝 &lt;strong&gt;Note:&lt;/strong&gt; Depending on the shell used, the meta sections may be color-coded to help identify the sections more easily 🚀.&lt;/p&gt;

&lt;p&gt;🔍 &lt;strong&gt;Avro Breakdown&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight mathematica"&gt;&lt;code&gt;&lt;span class="w"&gt;                   &lt;/span&gt;&lt;span class="nv"&gt;Confluent&lt;/span&gt;&lt;span class="o"&gt;'&lt;/span&gt;&lt;span class="nv"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Wire&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Format&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="o"&gt;____________________________|_____________________________&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;                                        &lt;/span&gt;&lt;span class="nv"&gt;Avro&lt;/span&gt;&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;                       &lt;/span&gt;&lt;span class="o"&gt;__________________|________________|&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;                      &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;                                   &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;01&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;18&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Hello World!"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;UTF&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;bytes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;↑&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="err"&gt;↑&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="err"&gt;↑&lt;/span&gt;&lt;span class="w"&gt;                  &lt;/span&gt;&lt;span class="err"&gt;↑&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;magic&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;B&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;schema&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;id&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nv"&gt;zigzag&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;len&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="nv"&gt;payload&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;data&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above breakdown, the last two sections are specific to the Avro format, which can also be considered the raw payload data. The wire format is the combination of this and the first two sections. This is the main difference between producing with raw payload data and data that can be associated with a particular schema in the Confluent Schema Registry.&lt;/p&gt;

&lt;p&gt;For brevity, let's quickly compare a Protobuf dump against the Avro dump from earlier by running the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'\x00\x00\x00\x00\x01\x00\x0a\x0c\x48\x65\x6c\x6c\x6f\x20\x57\x6f\x72\x6c\x64\x21'&lt;/span&gt; | xxd &lt;span class="nt"&gt;-g&lt;/span&gt; 1 &lt;span class="nt"&gt;-c&lt;/span&gt; 16
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The generated message above is associated with the following dummy schema below with a schema ID of 1:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight protobuf"&gt;&lt;code&gt;&lt;span class="na"&gt;syntax&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"proto3"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;example.messages&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;message&lt;/span&gt; &lt;span class="nc"&gt;Greeting&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="kd"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🔳 &lt;strong&gt;Output&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight mathematica"&gt;&lt;code&gt;&lt;span class="m"&gt;00000000&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;01&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;48&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;65&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="nv"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="nv"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="nv"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;20&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;57&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="nv"&gt;f&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;........&lt;/span&gt;&lt;span class="nv"&gt;Hello&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Wo&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="m"&gt;00000010&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;72&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="nv"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;64&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;21&lt;/span&gt;&lt;span class="w"&gt;                                      &lt;/span&gt;&lt;span class="nv"&gt;rld&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;📝 &lt;strong&gt;Note:&lt;/strong&gt; If using a wrapper type like &lt;code&gt;google.protobuf.StringValue&lt;/code&gt; from &lt;code&gt;google/protobuf/wrappers.proto&lt;/code&gt; instead of &lt;code&gt;string&lt;/code&gt; or &lt;code&gt;optional string&lt;/code&gt; for presence detection with primitive fields, and for more consistent behavior, the output will be slightly different. This is because the wrapper type adds an inner message structure with a single field called value. The end result would be &lt;code&gt;00 00 00 00 01 00 [0a 0e] 0a 0c 48 65 6c 6c 6f 20 57 6f 72 6c 64 21&lt;/code&gt;, where bytes &lt;code&gt;0x0a&lt;/code&gt; and &lt;code&gt;0x0e&lt;/code&gt; for the outer tag and length are prepended to the expected raw payload in this example.&lt;/p&gt;

&lt;p&gt;🔍 &lt;strong&gt;Protobuf Breakdown&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight mathematica"&gt;&lt;code&gt;&lt;span class="w"&gt;                         &lt;/span&gt;&lt;span class="nv"&gt;Confluent&lt;/span&gt;&lt;span class="o"&gt;'&lt;/span&gt;&lt;span class="nv"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Wire&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Format&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="o"&gt;__________________________________|_____________________________________&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;                                                &lt;/span&gt;&lt;span class="nv"&gt;Protobuf&lt;/span&gt;&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;                              &lt;/span&gt;&lt;span class="o"&gt;_____________________|____________________|&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;                             &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;                                          &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;01&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="nv"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Hello World!"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;UTF&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;bytes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;↑&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="err"&gt;↑&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="err"&gt;↑&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="err"&gt;↑&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="err"&gt;↑&lt;/span&gt;&lt;span class="w"&gt;                  &lt;/span&gt;&lt;span class="err"&gt;↑&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;magic&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;B&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;schema&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;id&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;msg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;index&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nv"&gt;tag&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nv"&gt;length&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="nv"&gt;payload&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;data&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The structure is similar to the previous Avro one, except now the wire format introduces one previously omitted section meant only for Protobuf, the message index. Also, notice how Protobuf's length doesn't use ZigZag encoding for the length prefix.&lt;/p&gt;

&lt;h3&gt;
  
  
  Debugging challenge&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;As a small challenge, see if you can spot the differences between the following wire-formatted message carrying a JSON payload and the previous examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'AAAAAAF7ICJtZXNzYWdlIjogIkhlbGxvIFdvcmxkISIgfQ=='&lt;/span&gt; | &lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; | xxd &lt;span class="nt"&gt;-g&lt;/span&gt; 1 &lt;span class="nt"&gt;-c&lt;/span&gt; 16
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alternatively, use the longer byte approach:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'\x00\x00\x00\x00\x01\x7b\x20\x22\x6d\x65\x73\x73\x61\x67\x65\x22\x3a\x20\x22\x48\x65\x6c\x6c\x6f\x20\x57\x6f\x72\x6c\x64\x21\x22\x20\x7d'&lt;/span&gt; | xxd &lt;span class="nt"&gt;-g&lt;/span&gt; 1 &lt;span class="nt"&gt;-c&lt;/span&gt; 16
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The generated message above is associated with the following dummy schema below with a schema ID of 1:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"$id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://example.com/schemas/com.example.messages.Greeting.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"$schema"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://json-schema.org/draft/2020-12/schema"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Greeting"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"additionalProperties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Additional commands&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Finally, here are a few more commands to play with that could come in handy. The commands are mostly for Avro and JSON-based messages, but they can be adapted for Protobuf as well.&lt;/p&gt;

&lt;p&gt;📚 &lt;u&gt;&lt;strong&gt;PARSED VIEW&lt;/strong&gt;&lt;/u&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;xxd &lt;span class="nt"&gt;-p&lt;/span&gt; ./avro-message.bin | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'s/^(.{2})(.{8})(.*)$/Magic:\1 SchemaID:\2 Payload:\3/'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🔳 &lt;strong&gt;Output&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Magic:00 SchemaID:00000001 Payload:1848656c6c6f20576f726c6421
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;📚 &lt;u&gt;&lt;strong&gt;SECTIONAL VIEWS&lt;/strong&gt;&lt;/u&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Magic (first byte)&lt;/span&gt;
&lt;span class="nb"&gt;dd &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;avro-message.bin &lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;none | xxd &lt;span class="nt"&gt;-g&lt;/span&gt; 1
&lt;span class="c"&gt;# Schema ID (4 bytes after 1)&lt;/span&gt;
&lt;span class="nb"&gt;dd &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;avro-message.bin &lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nv"&gt;skip&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4 &lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;none | xxd &lt;span class="nt"&gt;-g&lt;/span&gt; 4
&lt;span class="c"&gt;# Payload (all bytes after 5)&lt;/span&gt;
&lt;span class="nb"&gt;dd &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;avro-message.bin &lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nv"&gt;skip&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5 &lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;none | xxd &lt;span class="nt"&gt;-g&lt;/span&gt; 1 &lt;span class="nt"&gt;-c&lt;/span&gt; 16
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🔳 &lt;strong&gt;Output&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight mathematica"&gt;&lt;code&gt;&lt;span class="m"&gt;00000000&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="m"&gt;00000000&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00000001&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="m"&gt;00000000&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;18&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;48&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;65&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="nv"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="nv"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="nv"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;20&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;57&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="nv"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;72&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="nv"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;64&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;21&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;Hello&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;World&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;📚 &lt;u&gt;&lt;strong&gt;PARSE CONTINUOUS HEX&lt;/strong&gt;&lt;/u&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Avoids having to add \x before each byte, just remove spaces.&lt;/span&gt;
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'00000000011848656c6c6f20576f726c6421'&lt;/span&gt; | xxd &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; | xxd &lt;span class="nt"&gt;-g&lt;/span&gt; 1 &lt;span class="nt"&gt;-c&lt;/span&gt; 16
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🔳 &lt;strong&gt;Output&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight mathematica"&gt;&lt;code&gt;&lt;span class="m"&gt;00000000&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;01&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;18&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;48&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;65&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="nv"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="nv"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="nv"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;20&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;57&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="nv"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;72&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="nv"&gt;c&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;......&lt;/span&gt;&lt;span class="nv"&gt;Hello&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Worl&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="m"&gt;00000010&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;64&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;21&lt;/span&gt;&lt;span class="w"&gt;                                            &lt;/span&gt;&lt;span class="nv"&gt;d&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Third-party wire format compatibility&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;The following tables provide a general overview of schema registries and tools that are compatible with Confluent's wire format. This information is based on first-hand testing and or what could be found in the documentation at the time of this writing.&lt;/p&gt;

&lt;p&gt;🗂️ Schema Registries&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Wire Format Support&lt;/th&gt;
&lt;th&gt;Special Considerations&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Azure Schema Registry&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;Uses a different approach&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apicurio Registry&lt;/td&gt;
&lt;td&gt;✅ Yes, Automatic&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Karapace&lt;/td&gt;
&lt;td&gt;✅ Yes, Automatic&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;☕ Tools/Services&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Wire Format Support&lt;/th&gt;
&lt;th&gt;Special Considerations&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Kafka CLI&lt;/td&gt;
&lt;td&gt;⚠️ Yes, via Addon&lt;/td&gt;
&lt;td&gt;Add Confluent serializers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;kcat CLI / kafkacat CLI&lt;/td&gt;
&lt;td&gt;❌ No, Manual only&lt;/td&gt;
&lt;td&gt;Supports working with bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Strimzi Bridge REST API&lt;/td&gt;
&lt;td&gt;❌ No, Manual only&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Strimzi Connect&lt;/td&gt;
&lt;td&gt;✅ Yes, Automatic&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AKHQ&lt;/td&gt;
&lt;td&gt;✅ Yes, Automatic&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka UI / Kafbat UI&lt;/td&gt;
&lt;td&gt;✅ Yes, Automatic&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vector&lt;/td&gt;
&lt;td&gt;❌ No, Manual only&lt;/td&gt;
&lt;td&gt;Avoid value transformations&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;📝 &lt;strong&gt;Note:&lt;/strong&gt; The list assumes clients are using the correct serializers/converters to integrate with Kafka and the schema registry.&lt;/p&gt;




&lt;h2&gt;
  
  
  Manually handling the wire format&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;This section takes theory into practice by showing how a Native or REST-based Kafka client can produce or consume wire-formatted messages without a direct integration with a compatible schema registry. The goal is to prevent breaking other clients in the data exchange that do use one. The examples below have been created to be framework agnostic and generic enough to apply to any type of client. However, with that said, some adaptation will be needed for the target implementation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Producer code snippet&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The code snippet below shows how a producer can manually tack on the required wire format bytes to an Avro-based payload before sending it to a Kafka topic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Greeting&lt;/span&gt; &lt;span class="n"&gt;greeting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Greeting&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello World!"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="c1"&gt;// Prepend the wire format.&lt;/span&gt;
&lt;span class="nc"&gt;ByteBuffer&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ByteBuffer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;allocate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;avroPayload&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;magicByte&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;putInt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;schemaId&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;avroPayload&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Ready for sending as a native-based Kafka client.&lt;/span&gt;
&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;array&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// Ready for sending as a REST-based Kafka client.&lt;/span&gt;
&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;base64Message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Base64&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getEncoder&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;encodeToString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Avro Payload Length: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;avroPayload&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Message Length: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Message (base64): "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;base64Message&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Message (hex): "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;hexMessage&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🤖 View full code on &lt;a href="https://gist.github.com/StevenJDH/90814aeb71973ac64184393300439bbf" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🔳 &lt;strong&gt;Output&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Avro Payload Length: 13
Message Length: 18
Message (base64): AAAAAAEYSGVsbG8gV29ybGQh
Message (hex): 00 00 00 00 01 18 48 65 6c 6c 6f 20 57 6f 72 6c 64 21
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The actual sending of the message will depend on the type of producer client being used. For example, a native Kafka client will wrap the message bytes using &lt;code&gt;ProducerRecord&amp;lt;String, byte[]&amp;gt; record = new ProducerRecord&amp;lt;&amp;gt;("my-topic", message);&lt;/code&gt; before using &lt;code&gt;producer.send(record);&lt;/code&gt; to send it. On the other hand, a REST-based client will use the base64 encoded version of the message to POST it via the endpoint &lt;code&gt;/topics/{topic-name}&lt;/code&gt; using the &lt;code&gt;Content-Type: application/vnd.kafka.binary.v2+json&lt;/code&gt; header.&lt;/p&gt;

&lt;h3&gt;
  
  
  Consumer code snippet&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Similar to the producer, this consumer code snippet reverses the wire format encoding process to extract the payload so that it can be deserialized.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Base64&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getDecoder&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;decode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AAAAAAEYSGVsbG8gV29ybGQh"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nc"&gt;ByteBuffer&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ByteBuffer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;wrap&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;byte&lt;/span&gt; &lt;span class="n"&gt;magicByte&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;   &lt;span class="c1"&gt;// Position goes from 0 -&amp;gt; 1.&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;schemaId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getInt&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;  &lt;span class="c1"&gt;// Reads bytes [1..4], position goes from 1 -&amp;gt; 5.&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;remainingBytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;remaining&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;avroPayload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;remainingBytes&lt;/span&gt;&lt;span class="o"&gt;];&lt;/span&gt;
&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;avroPayload&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Writes remaining bytes to byte array.&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="nc"&gt;Greeting&lt;/span&gt; &lt;span class="n"&gt;greeting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reader&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;decoder&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Magic: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;magicByte&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Schema ID: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;schemaId&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Payload [Message Field]: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;greeting&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMessage&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🤖 View full code on &lt;a href="https://gist.github.com/StevenJDH/7ed7b4b3690a40035591f228c1d0eb6b" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🔳 &lt;strong&gt;Output&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Magic: 0
Schema ID: 1
Payload [Message Field]: Hello World!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In practice, a native consumer will use an annotation like &lt;code&gt;@KafkaListener(topics = "my-topic")&lt;/code&gt; over a method like &lt;code&gt;public void consume(ConsumerRecord&amp;lt;String, byte[]&amp;gt; record) {}&lt;/code&gt; to consume messages. REST-based clients will need to poll with GET the &lt;code&gt;/consumers/{consumer-group}/instances/{instance}/records&lt;/code&gt; endpoint using the &lt;code&gt;Accept: application/vnd.kafka.binary.v2+json&lt;/code&gt; header to consume the message as base64 encoded. &lt;/p&gt;




&lt;h2&gt;
  
  
  Best practices &amp;amp; tips&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Ideally, Kafka clients should use libraries that natively integrate with Confluent's Schema Registry or compatible alternatives when exchanging schema-backed data. If this is not possible, then the following list can help avoid some pains when developing solutions that do not have this integration as an option:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prefer schema-aware producers in production where possible, as this will be the safest option for consumers.&lt;/li&gt;
&lt;li&gt;Consumers should check for the magic byte (&lt;code&gt;message.length &amp;gt; 5 &amp;amp;&amp;amp; message[0] == 0x00&lt;/code&gt;) to accept or reject a message. Alternatively, use this check to accept both wire-formatted messages and ones that are not, since most of the code for processing a message is the same.&lt;/li&gt;
&lt;li&gt;Consumers should also consider using the schema ID for manually validating a message after requesting the schema with a schema registry client.&lt;/li&gt;
&lt;li&gt;For native clients, ensure not to accidentally use Confluent's &lt;code&gt;KafkaAvro*&lt;/code&gt;, &lt;code&gt;KafkaJsonSchema*&lt;/code&gt;, or &lt;code&gt;KafkaProtobuf*&lt;/code&gt; based Serializers/Deserializers in clients not using the schema registry, since these libraries depend on one. Instead, use the &lt;code&gt;ByteArraySerializer&lt;/code&gt; and &lt;code&gt;ByteArrayDeserializer&lt;/code&gt; for this scenario.&lt;/li&gt;
&lt;li&gt;For native clients, be explicit about what serializers/deserializers are being used. Frameworks like Quarkus support automatic selection, however, it may default to a String-based one for these types of messages.&lt;/li&gt;
&lt;li&gt;Handle the manual serialization/deserialization logic in a separate class using standard interfaces like &lt;code&gt;Deserializer&amp;lt;T&amp;gt;&lt;/code&gt; to make it more reusable and portable.&lt;/li&gt;
&lt;li&gt;If there are limitations or complexities in handling the wire format manually, create a bridging application or adapter to handle this task on behalf of the clients that need it. This should be preferred over requiring all clients to disable their schema registry integration.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Conclusion&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;In conclusion, understanding Confluent's wire format simplifies producing, consuming, and debugging schema-backed messages even when a client does not integrate with a schema registry. Remember to consider the roles each component contributes to in a data exchange. For example, Kafka transports the bytes, Schema Registry defines how those bytes are structured, and serializers/deserializers implement the wire format convention. Use byte-level tools and simple checks (magic byte and schema ID) to validate and diagnose messages across heterogeneous clients. Using only native clients that are integrated into the schema registry is ideal, but when this is not possible, prepend or strip the wire format header consistently to preserve compatibility. Applying these practices will go a long way towards reducing schema-related surprises that may creep into the solution at some point in the future.&lt;/p&gt;




&lt;h2&gt;
  
  
  Resources&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;The following resources provide additional details, examples, specifications, and tools related to the topics covered in this article.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.confluent.io/platform/current/schema-registry/fundamentals/serdes-develop/index.html#wire-format" rel="noopener noreferrer"&gt;Confluent's Wire Format documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://protobuf.dev/programming-guides/encoding/#varints" rel="noopener noreferrer"&gt;How to decode a Variable-Length Integer (varint)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.browserling.com/tools/text-to-hex" rel="noopener noreferrer"&gt;Online Tool: Convert text to hex&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.javainuse.com/bytesize" rel="noopener noreferrer"&gt;Online Tool: Calculate text byte size&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.confluent.io/platform/current/schema-registry/fundamentals/serdes-develop/serdes-avro.html" rel="noopener noreferrer"&gt;Confluent's Avro documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.confluent.io/platform/current/schema-registry/fundamentals/serdes-develop/serdes-json.html" rel="noopener noreferrer"&gt;Confluent's JSON Schema documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.confluent.io/platform/current/schema-registry/fundamentals/serdes-develop/serdes-protobuf.html" rel="noopener noreferrer"&gt;Confluent's Protobuf documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://protobuf.dev/programming-guides/field_presence" rel="noopener noreferrer"&gt;Protobuf 3 field presence overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://protobuf.dev/reference/protobuf/google.protobuf" rel="noopener noreferrer"&gt;Google's Protobuf Well-Known Type wrapper reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/aspnet/core/grpc/protobuf" rel="noopener noreferrer"&gt;Example of google/protobuf/wrappers.proto for presence detection&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>confluent</category>
      <category>schemaregistry</category>
      <category>kafka</category>
      <category>eventdriven</category>
    </item>
    <item>
      <title>Create a Kubernetes NGINX controller with Dapr support</title>
      <dc:creator>Steven Jenkins De Haro</dc:creator>
      <pubDate>Sun, 30 May 2021 22:20:55 +0000</pubDate>
      <link>https://dev.to/stevenjdh/create-a-kubernetes-nginx-controller-with-dapr-support-3e8n</link>
      <guid>https://dev.to/stevenjdh/create-a-kubernetes-nginx-controller-with-dapr-support-3e8n</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe6skw8gb5b3rmg4616p5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe6skw8gb5b3rmg4616p5.png" title="NGINX controller with Dapr support" alt="NGINX dapr architecture"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this post, I will show how to create an NGINX controller that exposes the Dapr enabled applications in a Kubernetes cluster. Essentially, the NGINX controller will be configured with the same standard Dapr annotations to get injected with the daprd sidecar. By exposing this sidecar, it will allow external applications to communicate with the Dapr enabled applications in the cluster, see the &lt;a href="https://docs.dapr.io/reference/api/" rel="noopener noreferrer"&gt;Dapr API reference&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; There have been changes to what repo to use for the NGINX installation, the spec requirements for Kubernetes ingress resources, and the Dapr annotation names, so the information in this post is the updated version that should be followed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Kubernetes 1.19+ cluster with &lt;a href="https://docs.dapr.io/operations/hosting/kubernetes/kubernetes-deploy/" rel="noopener noreferrer"&gt;Dapr&lt;/a&gt; configured.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/helm/helm/releases" rel="noopener noreferrer"&gt;Helm&lt;/a&gt; CLI 3x installed.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://kubernetes.io/docs/tasks/tools/" rel="noopener noreferrer"&gt;Kubectl&lt;/a&gt; CLI installed and configured to access the cluster.&lt;/li&gt;
&lt;li&gt;Optional: &lt;a href="https://wiki.openssl.org/index.php/Binaries" rel="noopener noreferrer"&gt;OpenSSL&lt;/a&gt; for creating self-signed certificates.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prepare helm
&lt;/h2&gt;

&lt;p&gt;Add the latest helm chart repo for the NGINX controller by running the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create an ingress namespace
&lt;/h2&gt;

&lt;p&gt;Ensure that the current kubectl context is pointed to the correct Kubernetes cluster, and run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create namespace nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Install NGINX controller with Dapr support
&lt;/h2&gt;

&lt;p&gt;Create a file called &lt;code&gt;dapr-annotations.yaml&lt;/code&gt; with the following content to set the annotations on the NGINX ingress controller pods:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;controller&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;podAnnotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;dapr.io/enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;
    &lt;span class="na"&gt;dapr.io/app-id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;nginx-ingress"&lt;/span&gt;
    &lt;span class="na"&gt;dapr.io/app-port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;80"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The port above tells the daprd sidecar which port the NGINX controller is listening on. See &lt;a href="https://docs.dapr.io/operations/hosting/kubernetes/kubernetes-annotations/" rel="noopener noreferrer"&gt;Dapr Kubernetes pod annotations spec&lt;/a&gt; for a complete list of supported annotations.&lt;/p&gt;

&lt;p&gt;Run the following command, which references the file above:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm &lt;span class="nb"&gt;install &lt;/span&gt;nginx-ingress ingress-nginx/ingress-nginx &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-f&lt;/span&gt; dapr-annotations.yaml &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--set&lt;/span&gt; controller.replicaCount&lt;span class="o"&gt;=&lt;/span&gt;2 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--namespace&lt;/span&gt; nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add &lt;code&gt;--set controller.service.loadBalancerIP=0.0.0.0&lt;/code&gt; to the above command if a static IP is needed, for example, the last IP from a peered subnet with on-premise.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create NGINX's daprd sidecar ingress resource
&lt;/h2&gt;

&lt;p&gt;Create a file called &lt;code&gt;ingress-dapr.yaml&lt;/code&gt; with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;networking.k8s.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Ingress&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ingress-dapr&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;nginx.ingress.kubernetes.io/rewrite-target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;tls&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;api.example.com&lt;/span&gt;
    &lt;span class="na"&gt;secretName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tls-secret&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api.example.com&lt;/span&gt;
    &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
        &lt;span class="na"&gt;pathType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Prefix&lt;/span&gt;
        &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx-ingress-dapr&lt;/span&gt;
            &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;number&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dapr automatically creates the &lt;code&gt;nginx-ingress-dapr&lt;/code&gt; service being referenced above. Make sure to modify the hostname placeholders. &lt;/p&gt;

&lt;p&gt;Remove the &lt;code&gt;tls&lt;/code&gt; section if TLS termination is not needed, otherwise, run the following commands to create a self-signed certificate for testing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openssl req &lt;span class="nt"&gt;-x509&lt;/span&gt; &lt;span class="nt"&gt;-nodes&lt;/span&gt; &lt;span class="nt"&gt;-days&lt;/span&gt; 365 &lt;span class="nt"&gt;-newkey&lt;/span&gt; rsa:2048 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-keyout&lt;/span&gt; tls.key &lt;span class="nt"&gt;-out&lt;/span&gt; tls.crt &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-subj&lt;/span&gt; &lt;span class="s2"&gt;"/CN=api.example.com/O=TLS Example"&lt;/span&gt;

kubectl create secret tls tls-secret &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--key&lt;/span&gt; tls.key &lt;span class="nt"&gt;--cert&lt;/span&gt; tls.cert &lt;span class="nt"&gt;-n&lt;/span&gt; nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Optionally, inspect the created certificate with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openssl x509 &lt;span class="nt"&gt;-in&lt;/span&gt; tls.crt &lt;span class="nt"&gt;-text&lt;/span&gt; &lt;span class="nt"&gt;-noout&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is recommended to use the FREE Let's Encrypt certificates that are globally recognized by following the steps defined in the &lt;a href="https://cert-manager.io/next-docs/tutorials/acme/ingress/" rel="noopener noreferrer"&gt;Securing NGINX-ingress&lt;/a&gt; or &lt;a href="https://medium.com/avmconsulting-blog/encrypting-the-certificate-for-kubernetes-lets-encrypt-805d2bf88b2a" rel="noopener noreferrer"&gt;Encrypting the certificate for Kubernetes (Let’s Encrypt)&lt;/a&gt; articles.&lt;/p&gt;

&lt;p&gt;Now, run the following command to create the ingress resource:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create &lt;span class="nt"&gt;-f&lt;/span&gt; ingress-dapr.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This will target the sidecar's service, not the NGINX's service for the load balancer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing externally
&lt;/h2&gt;

&lt;p&gt;If a static IP was not specified earlier, then get the external IP created as part of the NGINX controller setup running the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get svc nginx-ingress-ingress-nginx-controller &lt;span class="nt"&gt;-n&lt;/span&gt; nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get service &lt;span class="nt"&gt;--selector&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;nginx-ingress,component&lt;span class="o"&gt;=&lt;/span&gt;controller &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{.items[*].status.loadBalancer.ingress[0].ip}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-n&lt;/span&gt; nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the IP, run the follow test using cURL to see if the Dapr API for the cluster is reachable from outside the cluster:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://&amp;lt;ingress ip&amp;gt;/v1.0/invoke/mathService/method/add &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{ "arg1": 10, "arg2": 23}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above assumes that there is a microservice running using the Dapr app-id &lt;code&gt;mathService&lt;/code&gt; that has a POST endpoint called &lt;code&gt;add&lt;/code&gt;, which is an example taken from the &lt;a href="https://docs.dapr.io/reference/api/service_invocation_api/" rel="noopener noreferrer"&gt;Service invocation API reference&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To test without building an app, install redis, and use the redis state store building block following the &lt;a href="https://docs.dapr.io/getting-started/configure-state-pubsub/" rel="noopener noreferrer"&gt;How-To: Configure state store and pub/sub message broker&lt;/a&gt; article. After, perform the curl commands in this &lt;a href="https://docs.dapr.io/reference/api/state_api/#example" rel="noopener noreferrer"&gt;example&lt;/a&gt; to create and retrieve the key-value data in redis.&lt;/p&gt;

&lt;h2&gt;
  
  
  Delete the NGINX controller
&lt;/h2&gt;

&lt;p&gt;For convenience, here is the command to delete the NGINX controller:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm delete nginx-ingress &lt;span class="nt"&gt;-n&lt;/span&gt; nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Don't forget to delete the ingress resources and the &lt;code&gt;nginx&lt;/code&gt; namespace created earlier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limiting access
&lt;/h2&gt;

&lt;p&gt;If additional security is required around which Dapr APIs or services should be exposed, then the scope of this can be configured globally, or at a very granular level as needed. For more information, see the following articles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.dapr.io/concepts/security-concept/" rel="noopener noreferrer"&gt;Security&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.dapr.io/operations/configuration/api-allowlist/" rel="noopener noreferrer"&gt;How-To: Selectively enable Dapr APIs on the Dapr sidecar&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.dapr.io/operations/configuration/invoke-allowlist/" rel="noopener noreferrer"&gt;How-To: Apply access control list configuration for service invocation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.dapr.io/operations/security/api-token/" rel="noopener noreferrer"&gt;Enable API token authentication in Dapr&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>kubernetes</category>
      <category>nginx</category>
      <category>dapr</category>
      <category>helm</category>
    </item>
    <item>
      <title>Show your Tweets on your GitHub Profile README </title>
      <dc:creator>Steven Jenkins De Haro</dc:creator>
      <pubDate>Sat, 24 Oct 2020 18:58:20 +0000</pubDate>
      <link>https://dev.to/stevenjdh/show-your-tweets-on-your-github-profile-readme-141i</link>
      <guid>https://dev.to/stevenjdh/show-your-tweets-on-your-github-profile-readme-141i</guid>
      <description>&lt;p&gt;GitHub recently released a feature that allows you to create a profile for your GitHub account, which is powered by a simple README.md file. As one might have guessed, people have already found ways to create creative profiles with dynamic content using APIs and GitHub Actions. In this quick tutorial, I will show you one way to dynamically display your tweets on your GitHub profile.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before We Start
&lt;/h2&gt;

&lt;p&gt;You will first need to enable the profile feature on your account by creating a new repo that matches your GitHub username. For example, my username is &lt;code&gt;StevenJDH&lt;/code&gt; so my repo is called &lt;code&gt;StevenJDH&lt;/code&gt;. Once done, you can quickly create a starter profile using the &lt;a href="https://rahuldkjain.github.io/gh-profile-readme-generator" rel="noopener noreferrer"&gt;GitHub Profile README Generator&lt;/a&gt; tool to get the needed markdown as you see here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Frahuldkjain%2Fgithub-profile-readme-generator%2Fmaster%2Fsrc%2Fimages%2Fgithub-profile-readme-generator.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Frahuldkjain%2Fgithub-profile-readme-generator%2Fmaster%2Fsrc%2Fimages%2Fgithub-profile-readme-generator.gif" alt="GitHub Profile README Generator"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Generating an RSS Feed Link
&lt;/h2&gt;

&lt;p&gt;The approach being taken today requires an RSS feed link for your tweets, which Twitter does not provide. Fortunately, the &lt;a href="https://rss.app/" rel="noopener noreferrer"&gt;Rss.app&lt;/a&gt; service can provide us with one. To get this link, do the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;From the &lt;a href="https://rss.app/" rel="noopener noreferrer"&gt;Rss.app&lt;/a&gt; website, &lt;a href="https://rss.app/signin" rel="noopener noreferrer"&gt;sign in&lt;/a&gt; or &lt;a href="https://rss.app/signup" rel="noopener noreferrer"&gt;sign up&lt;/a&gt;  with your Twitter account. &lt;/li&gt;
&lt;li&gt;Optional: If this is a new account, you will be automatically enrolled into the 7 day trial for the &lt;a href="https://rss.app/plans" rel="noopener noreferrer"&gt;Premium tier&lt;/a&gt;. Downgrading later to the Free tier will undo the following steps unless you opt to pay for that tier. I recommend downgrading to the Free tier now to avoid a message like &lt;code&gt;[[Action required] Your RSS.app Trial has Expired - Sat Oct 31 2020](https://rss.app)&lt;/code&gt; replacing your tweets, and having to repeat the steps that follow.&lt;/li&gt;
&lt;li&gt;Select the &lt;a href="https://rss.app/new-rss-feed" rel="noopener noreferrer"&gt;&lt;code&gt;+ New Feed&lt;/code&gt;&lt;/a&gt; button on the left after logging in, and then choose the &lt;a href="https://rss.app/new-rss-feed/create-twitter-rss-feed" rel="noopener noreferrer"&gt;Twitter RSS Feed&lt;/a&gt; tile on the right.&lt;/li&gt;
&lt;li&gt;In the first field provided, enter the URL to your Twitter profile and click the &lt;code&gt;Generate&lt;/code&gt; button.&lt;/li&gt;
&lt;li&gt;You should now see your RSS feed link associated with your Twitter account. In trial mode, the data behind this link will refresh every 30 minutes for the first week, but afterward, it will refresh once a day.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setting Up Your GitHub Profile README
&lt;/h2&gt;

&lt;p&gt;Now for the fun part, adding the tweets to the GitHub profile. This is made possible thanks to the &lt;a href="https://github.com/gautamkrishnar/blog-post-workflow" rel="noopener noreferrer"&gt;Blog Post Workflow&lt;/a&gt; that we are adapting to make it work for Twitter.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a folder called &lt;code&gt;.github&lt;/code&gt; at the root of your profile repo. Inside that folder, create another folder called &lt;code&gt;workflows&lt;/code&gt;. You should have something like this &lt;code&gt;.github\workflows&lt;/code&gt; now.&lt;/li&gt;
&lt;li&gt;In the &lt;code&gt;workflows&lt;/code&gt; folder, create a file called &lt;code&gt;twitter-workflow.yml&lt;/code&gt; with the following contents along with your RSS feed link:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Latest Twitter Tweets&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;schedule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Runs once a day because using Free tier in Rss.app&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;cron&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*'&lt;/span&gt;
  &lt;span class="na"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# Run workflow manually (without waiting for the cron to be called), through the Github Actions Workflow page directly.&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;update-readme-with-twitter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Update this repo's README with latest tweets&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gautamkrishnar/blog-post-workflow@master&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;comment_tag_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;TWITTER"&lt;/span&gt;
          &lt;span class="na"&gt;feed_list&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;&amp;lt;[[PLACE&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;YOUR&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;RSS&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;FEED&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;LINK&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;HERE]]&amp;gt;&amp;gt;"&lt;/span&gt;
          &lt;span class="na"&gt;commit_message&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Updated&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;with&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;latest&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;tweets"&lt;/span&gt;
          &lt;span class="na"&gt;committer_username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;twitter-bot"&lt;/span&gt;
          &lt;span class="na"&gt;committer_email&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;twitter-bot@example.com"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Back at the root of your repo, add the following to your &lt;code&gt;README.md&lt;/code&gt; file where you want the tweets to appear:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;### 📱 Latest Tweets&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- TWITTER:START --&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!-- TWITTER:END --&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Once everything is saved, you can manually trigger the GitHub action that will replace the above &lt;code&gt;TWITTER&lt;/code&gt; comments with your tweets so you don't have to wait. Just go to &lt;code&gt;Actions&lt;/code&gt; at the top of your profile repo, select &lt;code&gt;Latest Twitter Tweets&lt;/code&gt; on the left under &lt;code&gt;Workflows&lt;/code&gt;, and then select &lt;code&gt;Run workflow&lt;/code&gt; on the right. Assuming enough time has passed for the &lt;a href="https://rss.app/" rel="noopener noreferrer"&gt;Rss.app&lt;/a&gt; service to parse your tweets, you should see the last 5 tweets on your GitHub profile.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In theory, the &lt;a href="https://github.com/gautamkrishnar/blog-post-workflow" rel="noopener noreferrer"&gt;Blog Post Workflow&lt;/a&gt; can support any RSS feed-based service, but it already has direct, out-the-box support for many popular services. It also provides tons of configuration options, which I used to adapt the output more for Twitter. Other than the Free tier service only parsing once a day, this approach works well, and it is compact. If you know of any other approaches that do not use cards, or has a better Free tier for parsing tweets, please share it in a comment.&lt;/p&gt;

</description>
      <category>github</category>
      <category>profile</category>
      <category>twitter</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>First Post</title>
      <dc:creator>Steven Jenkins De Haro</dc:creator>
      <pubDate>Sat, 24 Oct 2020 05:48:45 +0000</pubDate>
      <link>https://dev.to/stevenjdh/first-post-5c0e</link>
      <guid>https://dev.to/stevenjdh/first-post-5c0e</guid>
      <description>&lt;p&gt;A quick test post for the dynamic feeds in GitHub's README.md Profiles.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
