Logo

dev-resources.site

for different kinds of informations.

How to document SSE app

Published at
9/7/2023
Categories
asyncapi
openapi
api
Author
pakisan
Categories
3 categories in total
asyncapi
open
openapi
open
api
open
Author
7 person written this
pakisan
open
How to document SSE app

Most interesting moment for me is to compare OpenAPI specification with AsyncAPI specification in case of:

  • re-using of common parts
  • describing of SSE application

and compare results

What will we document

Service which will broadcast received messages through an SSE connection to any subscribed user
app diagram

Common part

Let's collect all required schemas in one file:

schemas.json

{
  "schemas": {
    "Message": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "message",
        "receivedAt"
      ],
      "properties": {
        "message": {
          "type": "string",
          "example": "broadcast this message :rocket:"
        },
        "receivedAt": {
          "type": "string",
          "format": "date-time",
          "example": "2023-08-31T15:28:21.283+00:00",
          "description": "Date-time when application received this message"
        }
      }
    },
    "MessageToBroadcast": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "message"
      ],
      "properties": {
        "message": {
          "type": "string",
          "example": "broadcast this message :rocket:",
          "description": "Ordinary text which will be send"
        }
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

OpenAPI

Broadcast messages

Just basic declaration, nothing special at all

{
  "openapi": "3.0.3",
  "info": {
    "version": "1.0.0",
    "title": "Messages API",
    "description": "Broadcasts received messages through an SSE connection to any subscribed user"
  },
  "servers": [
    {
      "url": "http://localhost:8080"
    }
  ],
  "paths": {
    "/messages": {
      "post": {
        "summary": "Broadcast message",
        "operationId": "broadcastMessage",
        "description": "Send message to broadcast to any subscribed user",
        "tags": [
          "messages"
        ],
        "requestBody": {
          "description": "Message to broadcast",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "../schemas.json#/schemas/MessageToBroadcast"
              },
              "example": "broadcast this message :rocket:"
            }
          }
        },
        "responses": {
          "200": {
            "description": "message received"
          }
        }
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Subscribe to messages stream

More complicated part. Unfortunately, community still figuring out how to describe events by OpenAPI in the right manner.

We can use this GitHub issue as a reference

{
  "openapi": "3.0.3",
  "info": {
    "version": "1.0.0",
    "title": "Messages stream API",
    "description": "Broadcasts received messages through an SSE connection to any subscribed user"
  },
  "servers": [
    {
      "url": "http://localhost:8080"
    }
  ],
  "paths": {
    "/messages": {
      "get": {
        "summary": "Subscribe to stream of messages",
        "operationId": "messagesStreamSubscribe",
        "description": "Receive all incoming messages",
        "tags": [
          "messages"
        ],
        "responses": {
          "200": {
            "description": "Stream of messages",
            "headers": {
              "X-SSE-Content-Type": {
                "schema": {
                  "type": "string",
                  "enum": ["application/json"]
                }
              },
              "transfer-encoding": {
                "schema": {
                  "type": "string",
                  "enum": ["chunked"]
                }
              }
            },
            "content": {
              "text/event-stream": {
                "schema": {
                  "$ref": "#/components/schemas/MessagesStream"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "MessagesStream": {
        "type": "array",
        "format": "event-stream",
        "items": {
          "$ref": "../schemas.json#/schemas/Message"
        }
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Pub + Sub

{
  "openapi": "3.0.3",
  "info": {
    "version": "1.0.0",
    "title": "Messages API",
    "description": "Broadcasts received messages through an SSE connection to any subscribed user"
  },
  "servers": [
    {
      "url": "http://localhost:8080"
    }
  ],
  "paths": {
    "/messages": {
      "get": {
        "summary": "Subscribe to stream of messages",
        "operationId": "messagesStreamSubscribe",
        "description": "Receive all incoming messages",
        "tags": [
          "messages"
        ],
        "responses": {
          "200": {
            "description": "Stream of messages",
            "headers": {
              "X-SSE-Content-Type": {
                "schema": {
                  "type": "string",
                  "enum": ["application/json"]
                }
              },
              "transfer-encoding": {
                "schema": {
                  "type": "string",
                  "enum": ["chunked"]
                }
              }
            },
            "content": {
              "text/event-stream": {
                "schema": {
                  "$ref": "#/components/schemas/MessagesStream"
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Broadcast message",
        "operationId": "broadcastMessage",
        "description": "Send message to broadcast to any subscribed user",
        "tags": [
          "messages"
        ],
        "requestBody": {
          "description": "Message to broadcast",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "../schemas.json#/schemas/MessageToBroadcast"
              },
              "example": "broadcast this message :rocket:"
            }
          }
        },
        "responses": {
          "200": {
            "description": "message received"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "MessagesStream": {
        "type": "array",
        "format": "event-stream",
        "items": {
          "$ref": "../schemas.json#/schemas/Message"
        }
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

AsyncAPI

Broadcast messages

{
  "asyncapi": "2.6.0",
  "info": {
    "title": "Messages stream API",
    "description": "Broadcasts received messages through an SSE connection to any subscribed user",
    "version": "1.0.0"
  },
  "servers": {
    "dev": {
      "url": "http://localhost:8080",
      "protocol": "http"
    }
  },
  "channels": {
    "/messages": {
      "description": "Broadcast message",
      "publish": {
        "description": "Send message to broadcast to any subscribed user",
        "message": {
          "bindings": {
            "http": {
              "headers": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "Content-Type"
                ],
                "properties": {
                  "Content-Type": {
                    "type": "string",
                    "enum": ["application/json"]
                  }
                }
              }
            }
          },
          "$ref": "#/components/messages/MessageToBroadcast"
        },
        "bindings": {
          "http": {
            "type": "request",
            "method": "POST"
          }
        }
      }
    }
  },
  "components": {
    "messages": {
      "MessageToBroadcast": {
        "payload": {
          "$ref": "../schemas.json#/schemas/MessageToBroadcast"
        }
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Subscribe to messages stream

{
  "asyncapi": "2.6.0",
  "info": {
    "title": "Messages stream API",
    "description": "Broadcasts received messages through an SSE connection to any subscribed user",
    "version": "1.0.0"
  },
  "servers": {
    "dev": {
      "url": "http://localhost:8080",
      "protocol": "http"
    }
  },
  "channels": {
    "/messages": {
      "description": "Subscribe to stream of messages",
      "subscribe": {
        "description": "Receive all incoming messages",
        "message": {
          "bindings": {
            "http": {
              "headers": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "Content-Type", "X-SSE-Content-Type", "transfer-encoding"
                ],
                "properties": {
                  "Content-Type": {
                    "type": "string",
                    "enum": ["text/event-stream"]
                  },
                  "X-SSE-Content-Type": {
                    "type": "string",
                    "enum": ["application/json"]
                  },
                  "transfer-encoding": {
                    "type": "string",
                    "enum": ["chunked"]
                  }
                }
              }
            }
          },
          "$ref": "#/components/messages/Message"
        },
        "bindings": {
          "http": {
            "type": "request",
            "method": "GET"
          }
        }
      }
    }
  },
  "components": {
    "messages": {
      "Message": {
        "payload": {
          "$ref": "../schemas.json#/schemas/Message"
        }
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Pub + Sub

{
  "asyncapi": "2.6.0",
  "info": {
    "title": "Messages stream API",
    "description": "Broadcasts received messages through an SSE connection to any subscribed user",
    "version": "1.0.0"
  },
  "servers": {
    "dev": {
      "url": "http://localhost:8080",
      "protocol": "http"
    }
  },
  "channels": {
    "/messages": {
      "description": "Broadcast message",
      "publish": {
        "description": "Send message to broadcast to any subscribed user",
        "message": {
          "bindings": {
            "http": {
              "headers": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "Content-Type"
                ],
                "properties": {
                  "Content-Type": {
                    "type": "string",
                    "enum": ["application/json"]
                  }
                }
              }
            }
          },
          "$ref": "#/components/messages/MessageToBroadcast"
        },
        "bindings": {
          "http": {
            "type": "request",
            "method": "POST"
          }
        }
      },
      "subscribe": {
        "description": "Receive all incoming messages",
        "message": {
          "bindings": {
            "http": {
              "headers": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "Content-Type", "X-SSE-Content-Type", "transfer-encoding"
                ],
                "properties": {
                  "Content-Type": {
                    "type": "string",
                    "enum": ["text/event-stream"]
                  },
                  "X-SSE-Content-Type": {
                    "type": "string",
                    "enum": ["application/json"]
                  },
                  "transfer-encoding": {
                    "type": "string",
                    "enum": ["chunked"]
                  }
                }
              }
            }
          },
          "$ref": "#/components/messages/Message"
        },
        "bindings": {
          "http": {
            "type": "request",
            "method": "GET"
          }
        }
      }
    }
  },
  "components": {
    "messages": {
      "MessageToBroadcast": {
        "payload": {
          "$ref": "../schemas.json#/schemas/MessageToBroadcast"
        }
      },
      "Message": {
        "payload": {
          "$ref": "../schemas.json#/schemas/Message"
        }
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Resume

Both specifications allow to you to describe Pub and Sub for SSE app, but with some tradeoffs

OpenAPI can't offer canonical way how to describe stream.

For example:

{
  "type": "array",
  "format": "event-stream",
  "items": {
    "$ref": "../schemas.json#/schemas/Message"
  }
}

What does it mean? Stream of messages? Stream of arrays with messages?

From other side AsyncAPI as expected can't offer flexible syntax for description of HTTP requests - headers, params location, status codes

It's up to you to choose which specification to use, but looks like it's better to use them both, instead of reinvent the wheel:

References

asyncapi Article's
30 articles in total
Favicon
List of AsyncAPI servers in MuleSoft
Favicon
AsyncAPI โ€” A standard specification for documenting Event-Driven Applications
Favicon
AsyncAPI: a practical look
Favicon
Arquitetura Event-Driven usando AsyncAPI na prรกtica
Favicon
AsyncAPI Codegen, a code generator from AsyncAPI spec v2 and v3.
Favicon
Integration Digest: October 2023
Favicon
An AsyncAPI Example: Building Your First Event-driven API
Favicon
How to document SSE app
Favicon
Perfectly sizing images in your API documentation
Favicon
Using OpenAPI and AsyncAPI Tags to Better Organize API Endpoints
Favicon
API Contracts - an Extended Introduction
Favicon
Why AvioBook switched from Swagger UI to Bump.sh for all of their APIs
Favicon
How to use and document polymorphism in API
Favicon
What are the different API types?
Favicon
Event driven API documentation made simple (Client-Side Rendering).
Favicon
Could AsyncApi Make A Dent on Climate Change?
Favicon
API Diff - Compare in seconds two versions of your API
Favicon
An introduction to the AsyncAPI specification
Favicon
Getting Started with CloudEvents and AsyncAPI
Favicon
Bump diff, the missing piece for an API โ€œdesign-firstโ€ approach
Favicon
Mule support for Async API
Favicon
API documentation in event driven applications
Favicon
AsyncAPI Code Generation: Microservices Using Spring Cloud Stream
Favicon
(Part 2) Full automation of release with GitHub Actions and Conventional Commits for non-JS projects
Favicon
Nunjucks templating explained on the basis of AsyncAPI specification
Favicon
AsyncAPI for documentation and validation of event-driven architecture
Favicon
A Modeling Editor and Code Generator for AsyncAPI
Favicon
Designing, Documenting and Testing Event APIs for IoT Platforms
Favicon
Status update (week 11, 2019)
Favicon
Why Developers Need an Event Portal; Creating Applications that Disseminate Real-Time COVID-19 Data

Featured ones: