<?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: Louis Fradin</title>
    <description>The latest articles on DEV Community by Louis Fradin (@lerenn).</description>
    <link>https://dev.to/lerenn</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%2F1328638%2F1ff52089-b95a-43e3-a971-7e0f73e692de.jpg</url>
      <title>DEV Community: Louis Fradin</title>
      <link>https://dev.to/lerenn</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lerenn"/>
    <language>en</language>
    <item>
      <title>AsyncAPI Codegen, a code generator from AsyncAPI spec v2 and v3.</title>
      <dc:creator>Louis Fradin</dc:creator>
      <pubDate>Wed, 06 Mar 2024 19:03:43 +0000</pubDate>
      <link>https://dev.to/lerenn/asyncapi-codegen-a-code-generator-from-asyncapi-spec-v2-and-v3-3nfb</link>
      <guid>https://dev.to/lerenn/asyncapi-codegen-a-code-generator-from-asyncapi-spec-v2-and-v3-3nfb</guid>
      <description>&lt;p&gt;This post is about an open-source tool that I’m currently writing &lt;a href="https://github.com/lerenn/asyncapi-codegen"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  AsyncAPI, an initiative to rule them all
&lt;/h1&gt;

&lt;h2&gt;
  
  
  What is AsyncAPI?
&lt;/h2&gt;

&lt;p&gt;As a response to the lack of tools to define contracts between asynchronous services, Fran Méndez started the project AsyncAPI as a set of tools heavily inspired by OpenAPI initiative to define communication inside an Event-Driven Architecture (EDA).&lt;/p&gt;

&lt;p&gt;Like OpenAPI, you have a specification, in YAML or JSON, that can be used to define the interfaces of asynchronous APIs. It can use any type of protocols and brokers (NATS, Kafka, RabbitMQ, gRPC, etc) but also any format (JSON, Protobuf, etc).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F38jbrklxopb2crtax0xo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F38jbrklxopb2crtax0xo.png" alt="AsyncAPI creator’s dream" width="720" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After years of development, it grew, became an important project and joined the Linux Foundation Projects with the goal to be industry standard for defining asynchronous APIs.&lt;/p&gt;

&lt;h2&gt;
  
  
  An AsyncAPI specification example
&lt;/h2&gt;

&lt;p&gt;Here is what it can look like, &lt;a href="https://www.asyncapi.com/docs/tutorials/getting-started/hello-world"&gt;from the official documentation&lt;/a&gt;:&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;asyncapi&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;3.0.0&lt;/span&gt;
&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Hello world application&lt;/span&gt;
  &lt;span class="na"&gt;version&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.1.0'&lt;/span&gt;
&lt;span class="na"&gt;channels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;hello&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hello'&lt;/span&gt;
    &lt;span class="na"&gt;messages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;sayHello&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
          &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;^hello&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;.+$'&lt;/span&gt;
&lt;span class="na"&gt;operations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;receiveHello&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;receive'&lt;/span&gt;
    &lt;span class="na"&gt;channel&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;$ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#/channels/hello'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some elements may look familiar to you, like the components defining schemas or the information part, as they are directly inspired from OpenAPI specifications.&lt;/p&gt;

&lt;p&gt;Some are quite different: the first level &lt;code&gt;channels&lt;/code&gt; defines the channels used by the application. You can also find a &lt;code&gt;messages&lt;/code&gt; definition in the &lt;code&gt;channels&lt;/code&gt; that describes the messages that can be sent and/or received on the channel. There is also an &lt;code&gt;operations&lt;/code&gt; part that describes all actions that the application is doing.&lt;/p&gt;

&lt;p&gt;You can then read this document as an application that is listening on &lt;code&gt;hello&lt;/code&gt; channel and is expecting a &lt;code&gt;sayHelloMessage&lt;/code&gt; on it. This message should contain a payload with&lt;/p&gt;

&lt;p&gt;Messages payload can be displayed in JSON format:&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="s2"&gt;"hello world"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you are curious of the numerous possiblities, you can read &lt;a href="https://www.asyncapi.com/docs/reference/specification/v3.0.0"&gt;the official reference&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  The code, the spec and the maintainability
&lt;/h1&gt;

&lt;p&gt;Of course, It can be really cumbersome to maintain the source code associated to the specification. That’s why there is two ways of doing that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generate the specification from the code&lt;/li&gt;
&lt;li&gt;Generate the code from the specification&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can already see where we are going, as this post is about code generation!&lt;/p&gt;

&lt;h2&gt;
  
  
  An official tool for code generation… but with Javascript and NPM
&lt;/h2&gt;

&lt;p&gt;For that, the project provides a &lt;a href="https://github.com/asyncapi/generator"&gt;Javascript tool&lt;/a&gt; to generate source code from specification in numerous languages: Python, Java, Markdown, PHP, … and even Go! All you have to do is install the corresponding NPM packages and launch the right command tool.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0v4q55waeytogc4lvyp8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0v4q55waeytogc4lvyp8.png" alt="NPM and JS being like books: useful but heavy" width="647" height="404"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, despite the great advantages of the Javascript ecosystem, a lot of developers (including myself) are a bit reluctant to install NPM packages with the really heavy stack that it implies. Especially for Go project where a specific command exist to run Go programs (and other tools) directly from the official Go command: &lt;code&gt;go generate&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To use it, we only have to add some preprocessing instructions at the top of some files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;//go:generate &amp;lt;insert here the shell command&amp;gt;&lt;/span&gt;

&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;mypkg&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We could use directly the official tool with &lt;code&gt;go generate&lt;/code&gt; but it would require to have the Javascript tools installed and to download the NPM packages.&lt;/p&gt;

&lt;p&gt;Ideally, we would need something in Go to run the &lt;code&gt;go run&lt;/code&gt; command inside the &lt;code&gt;go generate&lt;/code&gt; preprocessing command. It would be the most portable way to generate the code, as you would just have to have the Go stack installed. One language, one stack!&lt;/p&gt;

&lt;h2&gt;
  
  
  The inspiration: a really popular Go tool, but for OpenAPI
&lt;/h2&gt;

&lt;p&gt;During daytime, and especially work time, I used a great tool to generate code from OpenAPI specification: &lt;a href="https://github.com/deepmap/oapi-codegen"&gt;deepmap/oapi-codegen&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This tool takes an openapi specification and generate all the code needed to make any major Go HTTP server (Gin, Echo, Chi, etc) with the given specification.&lt;/p&gt;

&lt;p&gt;On top of that, you can use the &lt;code&gt;//go:generate&lt;/code&gt; preprocessing annotation to automatically generate the source code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen@latest -package mypkg openapi.yaml &amp;gt; openapi.gen.go&lt;/span&gt;
&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;mypkg&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At the time, I craved for this tool on AsyncAPI, and as it was not existant, I decided to wrote it. And a few month later (and few PR also), &lt;code&gt;asyncapi-codegen&lt;/code&gt; was available!&lt;/p&gt;

&lt;h1&gt;
  
  
  AsyncAPI-Codegen, hands on user signup
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Code architecture
&lt;/h2&gt;

&lt;p&gt;This schema describe how the code generated by AsyncAPI Codegen will interact between existing application, user and broker:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmhnkgeuu6wllzhdk9rc9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmhnkgeuu6wllzhdk9rc9.png" alt="The generated code, the gate between the broker and your application." width="641" height="121"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here are the different part of the code that will be autogenerated:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Provided (or custom) code for broker [orange]: you can use one of the provided broker (NATS, Kafka), or provide you own by satisfying BrokerController interface.&lt;/li&gt;
&lt;li&gt;Generated code [yellow]: code generated by &lt;code&gt;asyncapi-codegen&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Calling the generated code [red]: you need to write a few lines of code to call the generated code from your own code.&lt;/li&gt;
&lt;li&gt;App/User code [blue]: this is the code of your existing application and/or user. As it is conform to AsyncAPI spec, you can use it on only one of the two side against the app/user on another language.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Installing AsyncAPI-Codegen
&lt;/h2&gt;

&lt;p&gt;Well let’s take the example we had in the first part of this post, let’s save it in an &lt;code&gt;asyncapi.yaml&lt;/code&gt; and generate the corresponding code.&lt;/p&gt;

&lt;p&gt;First, we should install the tool:&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;# Install the tool&lt;/span&gt;
go &lt;span class="nb"&gt;install &lt;/span&gt;github.com/lerenn/asyncapi-codegen/cmd/asyncapi-codegen@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then create the new golang project with the following lines:&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;# Create a new directory for the project and enter&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nv"&gt;$GOPATH&lt;/span&gt;/src/asyncapi-example
&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="nv"&gt;$GOPATH&lt;/span&gt;/src/asyncapi-example

&lt;span class="c"&gt;# Create the go module files&lt;/span&gt;
go mod init

&lt;span class="c"&gt;# Create the asyncapi yaml into the repository&lt;/span&gt;
&lt;span class="nb"&gt;touch &lt;/span&gt;asyncapi.yaml

&lt;span class="c"&gt;# Then edit it to add the YAML content provided earlier&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we can generate the code:&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;# Generate the code from the asyncapi file&lt;/span&gt;
&lt;span class="c"&gt;# One for the application, one for the user and one for the common types&lt;/span&gt;
asyncapi-codegen &lt;span class="nt"&gt;-i&lt;/span&gt; ./asyncapi.yaml &lt;span class="nt"&gt;-p&lt;/span&gt; main &lt;span class="nt"&gt;-g&lt;/span&gt; application &lt;span class="nt"&gt;-o&lt;/span&gt; ./app.gen.go
asyncapi-codegen &lt;span class="nt"&gt;-i&lt;/span&gt; ./asyncapi.yaml &lt;span class="nt"&gt;-p&lt;/span&gt; main &lt;span class="nt"&gt;-g&lt;/span&gt; types &lt;span class="nt"&gt;-o&lt;/span&gt; ./types.gen.go
asyncapi-codegen &lt;span class="nt"&gt;-i&lt;/span&gt; ./asyncapi.yaml &lt;span class="nt"&gt;-p&lt;/span&gt; main &lt;span class="nt"&gt;-g&lt;/span&gt; user &lt;span class="nt"&gt;-o&lt;/span&gt; ./user.gen.go
&lt;span class="c"&gt;# Note: you can generate all in one file by removing the '-g' argument&lt;/span&gt;

&lt;span class="c"&gt;# Install dependencies needed by the generated code&lt;/span&gt;
go get &lt;span class="nt"&gt;-u&lt;/span&gt; github.com/lerenn/asyncapi-codegen/pkg/extensions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deep dive into the generated code
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Application generated code
&lt;/h3&gt;

&lt;p&gt;Let’s analyse what is present in &lt;code&gt;app.gen.go&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// AppController is the structure that provides publishing capabilities to the&lt;/span&gt;
&lt;span class="c"&gt;// developer and and connect the broker with the App.&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;AppController&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;

&lt;span class="c"&gt;// NewAppController links the App to the broker.&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;NewAppController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="c"&gt;/* ... */&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;AppController&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// SubscribeToReceiveHelloOperation waits for 'SayHello' messages from 'hello' channel.&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;AppController&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;SubscribeToReceiveHelloOperation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fn&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="n"&gt;SayHelloMessage&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;

&lt;span class="c"&gt;// UnsubscribeFromReceiveHelloOperation stops subscription on 'SayHello' messages from 'hello' channel.&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ac&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;AppController&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;UnsubscribeFromReceiveHelloOperation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// Close will clean up any existing resources on the controller.&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;AppController&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="c"&gt;/* ... */&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can see that we can create a new &lt;code&gt;AppController&lt;/code&gt; based on a broker controller, that will allow the application to receive &lt;code&gt;SayHello&lt;/code&gt; messages on &lt;code&gt;hello&lt;/code&gt; channel.&lt;/p&gt;

&lt;h3&gt;
  
  
  User generated code
&lt;/h3&gt;

&lt;p&gt;Let’s analyse what is present in &lt;code&gt;user.gen.go&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// UserController is the structure that provides publishing capabilities&lt;/span&gt;
&lt;span class="c"&gt;// to the developer and and connect the broker with the User.&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;UserController&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;

&lt;span class="c"&gt;// NewUserController links the User to the broker.&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;NewUserController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="c"&gt;/* ... */&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;UserController&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// SendToReceiveHelloOperation will publish a hello world message on the "hello" channel.&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;UserController&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;SendToReceiveHelloOperation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="n"&gt;SayHelloMessage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;

&lt;span class="c"&gt;// Close will clean up any existing resources on the controller.&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;UserController&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Like the application controller, we can see that we can create a new &lt;code&gt;UserController&lt;/code&gt; based on a broker controller. It will allow us to send &lt;code&gt;sayHello&lt;/code&gt; messages to the application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use the generated code
&lt;/h2&gt;

&lt;p&gt;Let’s use the generated code to simulate a real system !&lt;/p&gt;

&lt;p&gt;So we will need to have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A broker (for the sake of this example, we will use a NATS broker)&lt;/li&gt;
&lt;li&gt;An application emitting user signup events&lt;/li&gt;
&lt;li&gt;A user receiving user signup events&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The broker
&lt;/h3&gt;

&lt;p&gt;We will add some packages to use NATS directly in our code and launch a docker container to have a running NATS:&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;# Get missing code for NATS&lt;/span&gt;
go get github.com/lerenn/asyncapi-codegen/pkg/extensions/brokers/nats

&lt;span class="c"&gt;# Launch NATS with docker&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; nats &lt;span class="nt"&gt;-p&lt;/span&gt; 4222:4222 nats
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can then create a file named &lt;code&gt;main.go&lt;/code&gt; with the following code as a placeholder for the application and user code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
 &lt;span class="s"&gt;"github.com/lerenn/asyncapi-codegen/pkg/extensions"&lt;/span&gt;
 &lt;span class="s"&gt;"github.com/lerenn/asyncapi-codegen/pkg/extensions/brokers/nats"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;brokerController&lt;/span&gt; &lt;span class="n"&gt;extensions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BrokerController&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;// Will be filled later&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;brokerController&lt;/span&gt; &lt;span class="n"&gt;extensions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BrokerController&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;// Will be filled later&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;// Create a broker controller&lt;/span&gt;
  &lt;span class="n"&gt;brokerController&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;nats&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"nats://localhost:4222"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;// Launch application listening&lt;/span&gt;
  &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;brokerController&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="c"&gt;// Launch user that will periodically send 'hello world'&lt;/span&gt;
  &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;brokerController&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;We can see here that we create a broker controller based on NATS. It could have been any other available broker controller, as they fulfill the &lt;code&gt;extensions.BrokerController&lt;/code&gt; that &lt;code&gt;app()&lt;/code&gt; and &lt;code&gt;user()&lt;/code&gt; need.&lt;/p&gt;

&lt;p&gt;At the time these lines are written, there is only NATS Core/Jetstream and Kafka, but you can take inspiration from these ones to implement your own. In fact, the Kafka one is a PR from a contributor that wanted to use this tool for it. Feel free to explore!&lt;/p&gt;

&lt;p&gt;Then, there is the launch of the user and the start of the application. We will see now what is needed in each function.&lt;/p&gt;

&lt;h3&gt;
  
  
  The user
&lt;/h3&gt;

&lt;p&gt;Here is the code you will need in the user:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;brokerController&lt;/span&gt; &lt;span class="n"&gt;extensions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BrokerController&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;// Create the user controller&lt;/span&gt;
  &lt;span class="n"&gt;userController&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;NewUserController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;brokerController&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;// Publish users signing up events randomly&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Wait a second between sends&lt;/span&gt;
    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// Send message&lt;/span&gt;
    &lt;span class="n"&gt;userController&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SendToReceiveHelloOperation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="n"&gt;SayHelloMessage&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Payload&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sprint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hello "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;)&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;It repeatedly generates incremental &lt;code&gt;sayHello&lt;/code&gt; messages with incrementing payload. It waits one second and publishes each event using the &lt;code&gt;userController&lt;/code&gt;, created at the beginning with the &lt;code&gt;brokerController&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The application
&lt;/h3&gt;

&lt;p&gt;And now, for the application part:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;brokerController&lt;/span&gt; &lt;span class="n"&gt;extensions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BrokerController&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;// Create the user controller&lt;/span&gt;
  &lt;span class="n"&gt;appController&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;NewAppController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;brokerController&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;// Create a callback that will listen&lt;/span&gt;
  &lt;span class="n"&gt;callback&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="n"&gt;SayHelloMessage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Received message:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;appController&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SubscribeToReceiveHelloOperation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;callback&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;Here is what the code do:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It creates an app controller.&lt;/li&gt;
&lt;li&gt;Defines a callback function (fn) to handle &lt;code&gt;sayHello&lt;/code&gt; messages, logging the payload.&lt;/li&gt;
&lt;li&gt;Subscribes to &lt;code&gt;sayHello&lt;/code&gt; messages on &lt;code&gt;hello&lt;/code&gt; channel, using the app controller.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Execute the code
&lt;/h2&gt;

&lt;p&gt;Now that we have all the code, we can execute it:&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;# Run the code&lt;/span&gt;
go run &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the output you should see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2023/10/15 14:08:20 Hello 0
2023/10/15 14:08:21 Hello 1
2023/10/15 14:08:22 Hello 2
2023/10/15 14:08:23 Hello 3
2023/10/15 14:08:24 Hello 4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you use the NATS cli tool, you can also see the messages being sent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nats sub "&amp;gt;"
[#0] Received on "hello"
{"hello 0"}

[#1] Received on "hello"
{"hello 1"}

[#2] Received on "hello"
{"hello 2"}

[#3] Received on "hello"
{"hello 3"}

[#4] Received on "hello"
{"hello 4"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see that the AsyncAPI specification is respected, as the messages are sent and received with the same format.&lt;/p&gt;

&lt;h1&gt;
  
  
  More on AsyncAPI Codegen
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Extensions
&lt;/h2&gt;

&lt;p&gt;Sending and receiving messages is not the only thing you can do with the generated code. You can also use the extensions to add more features to the generated code.&lt;/p&gt;

&lt;p&gt;Here is the list of what can be done, linked to the &lt;code&gt;README.md&lt;/code&gt; (and later a link to specific blog posts):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/lerenn/asyncapi-codegen#requestresponse-example"&gt;Request/Response&lt;/a&gt;: Send a message on a channel and wait for the answer on another.&lt;/li&gt;
&lt;li&gt;Multiples Brokers: &lt;a href="https://github.com/lerenn/asyncapi-codegen#kafka"&gt;Kafka&lt;/a&gt;, &lt;a href="https://github.com/lerenn/asyncapi-codegen#nats"&gt;NATS&lt;/a&gt;, or &lt;a href="https://github.com/lerenn/asyncapi-codegen#custom-broker"&gt;Custom broker&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/lerenn/asyncapi-codegen#middlewares"&gt;Middlewares&lt;/a&gt;: manage the messages before sending or after receiving them.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/lerenn/asyncapi-codegen#context"&gt;Context&lt;/a&gt;: use context keys set by middlewares or by the user to get info on messages (publishing/reception, channel name, etc).&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/lerenn/asyncapi-codegen#logging"&gt;Logging&lt;/a&gt;: log messages sent and received, but also internal generated code logs.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/lerenn/asyncapi-codegen#versioning"&gt;Versioning&lt;/a&gt;: manage different versions of an AsyncAPI specification and plan migrations.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/lerenn/asyncapi-codegen#asyncapi-extensions"&gt;Annotations&lt;/a&gt;: add annotations to your specification for more features to generated code.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What’s next?
&lt;/h2&gt;

&lt;p&gt;Well there is a lot of things that can be done to improve the tool, and I’m currently working on some of them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add more brokers: RabbitMQ, gRPC, etc&lt;/li&gt;
&lt;li&gt;Add more middlewares: openetelemetry logging/tracing/metrics, etc&lt;/li&gt;
&lt;li&gt;Add more formats: Protobuf, etc&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But I’m also open to any contribution, so feel free to open an issue or a PR if you want to add something to the tool!&lt;/p&gt;

</description>
      <category>asyncapi</category>
      <category>go</category>
      <category>eventdriven</category>
    </item>
  </channel>
</rss>
