PageSourceSearch

https://www.asyncapi.com/_next/static/chunks/pages/blog/websocket-part1-ea764753deed01e7.js

js asyncapi.com collected 2026-09-24 09:00:54 UTC 28,562 bytes, 1 lines download raw bytes

1(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[27647],{3105:(e,n,s)=>{"use strict";s.r(n),s.d(n,{default:()=>r});var t=s(37876),o=s(19937);function i(e){let n={a:"a",blockquote:"blockquote",code:"code",em:"em",h2:"h2",h3:"h3",li:"li",ol:"ol",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,o.R)(),...e.components},{Figure:s,YouTube:i}=n;return s||a("Figure",!0),i||a("YouTube",!0),(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(n.p,{children:"This is a pretty subjective post. I'm sharing my perspective, taking into account years of experience building backend and frontend with user experience in mind."}),"\n",(0,t.jsx)(n.p,{children:"If you do not want to read this article, then watch the recording of the live stream about the same:"}),"\n",(0,t.jsx)(i,{id:"8tFBcf31e_c"}),"\n",(0,t.jsxs)(n.blockquote,{children:["\n",(0,t.jsxs)(n.p,{children:["Everything we hear is an opinion, not a fact. Everything we see is a perspective, not the truth.\n― ",(0,t.jsx)(n.a,{href:"https://www.politifact.com/factchecks/2019/sep/26/viral-image/no-marcus-aurelius-didnt-say-about-opinions-and-fa/",children:"Marcus Aurelius"})]}),"\n"]}),"\n",(0,t.jsx)(n.p,{children:"This blog post is the first of a series of blog posts about WebSocket I'm working on."}),"\n",(0,t.jsx)(n.h2,{id:"what-is-websocket",children:"What is WebSocket"}),"\n",(0,t.jsx)(n.p,{children:"It is a pretty old protocol used for duplex communication over TCP connection. It was standardized in 2011. Yes, ten years ago means it is old, super old."}),"\n",(0,t.jsx)(n.p,{children:"So why do I even mention it in 2021?"}),"\n",(0,t.jsx)(n.p,{children:"It is very widely adopted and will not go away anytime soon because tooling support is excellent and serves its purpose well. Just remind yourself when HTTP/2 showed up and how many years it took everyone to migrate. It would not happen without the strong support and push from all the big players."}),"\n",(0,t.jsxs)(n.p,{children:["Sure, there is ",(0,t.jsx)(n.a,{href:"https://developers.google.com/web/fundamentals/performance/http2/#request_and_response_multiplexing",children:"HTTP/2 multiplexing"})," and protocols like ",(0,t.jsx)(n.a,{href:"https://mercure.rocks/docs/mercure",children:"Mercure"})," or ",(0,t.jsx)(n.a,{href:"https://spec.graphql.org/June2018/#sec-Subscription",children:"GraphQL Subscription"}),". There is also ",(0,t.jsx)(n.a,{href:"https://www.rfc-editor.org/rfc/rfc8441",children:"RFC8441"})," for WebSocket and HTTP/2 and some tools already adopted it, like ",(0,t.jsx)(n.a,{href:"https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/http/upgrades",children:"Envoy"})," or ",(0,t.jsx)(n.a,{href:"https://github.com/eclipse/jetty.project/issues/3537",children:"Jetty"}),". Nevertheless, WebSocket is here to stay."]}),"\n",(0,t.jsx)(n.p,{children:"Anyway, the future of WebSocket has nothing to do with this post. This post is for the AsyncAPI community looking into the AsyncAPI spec because of WebSockets now, no matter the protocol's future."}),"\n",(0,t.jsx)(n.h2,{id:"websocket-use-case",children:"Websocket use case"}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsx)(n.li,{children:"Do you like to see in Slack that someone is typing a response?"}),"\n",(0,t.jsx)(n.li,{children:"Do you like it when a user interface updates without page refresh?"}),"\n",(0,t.jsx)(n.li,{children:"Do you like it when your client app knows there are updates available for display?"}),"\n"]}),"\n",(0,t.jsx)(n.p,{children:"That is what WebSocket is for. You establish a long-living connection between client and server. Through such a connection, the client can send a stream of messages to the server, and this is possible the other way around at the same time."}),"\n",(0,t.jsxs)(n.p,{children:["One could say: ",(0,t.jsx)(n.em,{children:"I don't need WebSocket to achieve that. I could just set up a data polling with REST API. Just ask the API every few seconds if there are updates."})]}),"\n",(0,t.jsx)(n.p,{children:"Sadly this is not a joke. Engineers do it. Some engineers just take shortcuts, mostly because deadlines hunt them down."}),"\n",(0,t.jsxs)(n.p,{children:["HTTP polling was presented very well in Shrek's famous ",(0,t.jsx)(n.em,{children:"Are we there yet?"})," scene."]}),"\n",(0,t.jsx)(i,{id:"basofea2UEs"}),"\n",(0,t.jsx)(n.p,{children:"Don't go that path. Do not perform unnecessary connections to your servers and create more and more traffic with more and more resource consumption. Wasting resources is bad and makes Shrek angry. WebSocket changes a lot there:"}),"\n",(0,t.jsx)(s,{src:"/img/posts/websocket-part1/websocket-shrek.webp",caption:"Figure 1: HTTP Pull vs WebSocket vs Shrek."}),"\n",(0,t.jsx)(n.h2,{id:"why-asyncapi",children:"Why AsyncAPI"}),"\n",(0,t.jsx)(n.p,{children:"When building a WebSocket API on a server, you might have some additional needs:"}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsx)(n.li,{children:"Want to document the API for the team that writes a client app, Web UI, Desktop app, or Mobile app."}),"\n",(0,t.jsx)(n.li,{children:"Want to have a way to specify the format of the messages that the server supports to validate them in the runtime."}),"\n",(0,t.jsx)(n.li,{children:"Want to generate a server or/and a client? If not for final production use, then for sure for prototyping and testing."}),"\n"]}),"\n",(0,t.jsxs)(n.p,{children:["These are just a few common needs. For WebSocket, you only establish a connection over HTTP protocol, and the rest goes over WS, so OpenAPI specification won't help you much here. WebSocket is one of the patterns in event-based systems. In the end, it is all about a stream of messages and asynchronous processing. Yes, it would be best to use AsyncAPI ",(0,t.jsx)(n.span,{role:"img","aria-label":"grinning face with big eyes",children:"\uD83D\uDE03"})]}),"\n",(0,t.jsx)(n.h2,{id:"websocket-described-with-asyncapi",children:"WebSocket described with AsyncAPI"}),"\n",(0,t.jsx)(n.p,{children:"When I google for some public WebSocket API to play with, I find mostly currency trading products:"}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsx)(n.li,{children:(0,t.jsx)(n.a,{href:"https://docs.kraken.com/websockets/",children:"Kraken WebSocket API"})}),"\n",(0,t.jsx)(n.li,{children:(0,t.jsx)(n.a,{href:"https://docs.gemini.com/websocket-api/",children:"Gemini WebSocket API"})}),"\n",(0,t.jsx)(n.li,{children:(0,t.jsx)(n.a,{href:"https://cex.io/websocket-api",children:"CEXIO Websocket API"})}),"\n"]}),"\n",(0,t.jsxs)(n.p,{children:["Currency trading is a topic I know nothing about ",(0,t.jsx)(n.span,{role:"img","aria-label":"man shrugging",children:"\uD83E\uDD37‍♂"})," but it feels interesting to explore more. Documentation of the 1st and 2nd API looks familiar from look&feel perspective. I think we can make a bet they are already using AsyncAPI, and Kraken most probably is still running on version 1. Let's release the Kraken then."]}),"\n",(0,t.jsxs)(n.blockquote,{children:["\n",(0,t.jsx)(n.p,{children:"I'm sorry if you expected me to de
1scribe Shrek's API interface using AsyncAPI. It would be fun, but only fun, and I'd also like to teach you something."}),"\n"]}),"\n",(0,t.jsxs)(n.p,{children:["I will write an AsyncAPI document for Kraken API after playing with the API and basing it on the ",(0,t.jsx)(n.a,{href:"https://docs.kraken.com/websockets/",children:"current documentation"}),"."]}),"\n",(0,t.jsx)(n.h3,{id:"playing-with-websocket-api",children:"Playing with WebSocket API"}),"\n",(0,t.jsxs)(n.p,{children:["The best way to play with a WebSocket API is through a CLI. Who didn't hear about ",(0,t.jsx)(n.strong,{children:"curl"})," in the REST API world? For WebSocket, I would recommend ",(0,t.jsx)(n.strong,{children:"websocat"}),". Kraken's API is partially public without authorization which is just great because to play with it, you do not have to set up an account to get an authorization token."]}),"\n",(0,t.jsxs)(n.ol,{children:["\n",(0,t.jsxs)(n.li,{children:["Install ",(0,t.jsx)(n.strong,{children:"websocat"}),". For other installation options, check out ",(0,t.jsx)(n.a,{href:"https://github.com/vi/websocat#installation",children:"this"})," list."]}),"\n"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-sh",children:"brew install websocat\n"})}),"\n",(0,t.jsxs)(n.ol,{children:["\n",(0,t.jsx)(n.li,{children:"Establish connection with the API:"}),"\n"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-sh",children:"websocat wss://ws.kraken.com\n"})}),"\n",(0,t.jsxs)(n.ol,{children:["\n",(0,t.jsx)(n.li,{children:"Ping the API to see if it responds. Just type the below message and hit Enter:"}),"\n"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-json",children:'{"event": "ping"}\n'})}),"\n",(0,t.jsxs)(n.ol,{children:["\n",(0,t.jsxs)(n.li,{children:["Now subscribe to the event ",(0,t.jsx)(n.strong,{children:"ticker"})," stream that sends messages with currency price. Just type the below message and hit Enter:"]}),"\n"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-json",children:'{  "event": "subscribe",  "pair": [    "XBT/USD",    "XBT/EUR"  ],  "subscription": {    "name": "ticker"  }}\n'})}),"\n",(0,t.jsxs)(n.ol,{children:["\n",(0,t.jsx)(n.li,{children:"You should now see a constant stream of data sent by the server. You do not have to ask the API every second for an update, as the update is pushed to you."}),"\n"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-json",children:'{"event":"heartbeat"}\n[340,{"a":["45520.10000",6,"6.78103490"],"b":["45520.00000",0,"0.00185230"],"c":["45520.10000","0.01643250"],"v":["1397.95434819","5589.12101024"],"p":["44883.49461","44062.07654"],"t":[14350,66782],"l":["43607.60000","42770.80000"],"h":["45811.10000","45811.10000"],"o":["43659.30000","44709.10000"]},"ticker","XBT/EUR"]\n[340,{"a":["45520.10000",5,"5.84803490"],"b":["45492.50000",0,"0.09374582"],"c":["45492.50000","0.00625418"],"v":["1398.10526819","5589.26685876"],"p":["44883.56109","44062.11477"],"t":[14359,66790],"l":["43607.60000","42770.80000"],"h":["45811.10000","45811.10000"],"o":["43659.30000","44709.10000"]},"ticker","XBT/EUR"]\n{"event":"heartbeat"}\n[340,{"a":["45503.80000",1,"1.00000000"],"b":["45496.20000",0,"0.01426600"],"c":["45496.20000","0.00109400"],"v":["1398.10636219","5589.26295766"],"p":["44883.56157","44062.11447"],"t":[14360,66788],"l":["43607.60000","42770.80000"],"h":["45811.10000","45811.10000"],"o":["43659.30000","44709.90000"]},"ticker","XBT/EUR"]\n{"event":"heartbeat"}\n'})}),"\n",(0,t.jsxs)(n.p,{children:['Boy, it is always such fun to do it. Like seriously, I always have fun playing with APIs, any APIs. Just making this API "conversation". I hope nothing is wrong with me ',(0,t.jsx)(n.span,{role:"img","aria-label":"grinning face with sweat",children:"\uD83D\uDE05"})]}),"\n",(0,t.jsx)(n.p,{children:"Now you know how to interact with the Kraken API. Now let's try to describe it using AsyncAPI."}),"\n",(0,t.jsx)(n.h3,{id:"describing-api-using-asyncapi",children:"Describing API using AsyncAPI"}),"\n",(0,t.jsx)(n.p,{children:"I'll explain, in detail, how to describe Websocket API with AsyncAPI in another blog post that will be part of the series. Why? I don't want to make this post super lengthy and discourage others from reading it. Let us learn step by step."}),"\n",(0,t.jsxs)(n.p,{children:["For now, I will throw here a full AsyncAPI document I created for the Kraken API. You can also open it up in the ",(0,t.jsx)(n.a,{href:"https://studio.asyncapi.com/?url=https://gist.githubusercontent.com/derberg/4e419d6ff5870c7c3f5f443e8bd30535/raw/5e9b733b80a0209ba5520e5f41ab18c2a112e0a9/asyncapi-websocket-kraken.yml",children:"AsyncAPI Studio"})," and compare with their ",(0,t.jsx)(n.a,{href:"https://docs.kraken.com/websockets/",children:"current documentation"})]}),"\n",(0,t.jsx)(n.p,{children:"Familiarize with below before you look at the AsyncAPI document:"}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsx)(n.li,{children:"AsyncAPI de
1scribes the API interface between the client and the server. In other words, the AsyncAPI document is for the user of the API. It does not describe what the server does but what the user can do with the API."}),"\n",(0,t.jsx)(n.li,{children:"Kraken API is quite complex. It has some beta servers, some private messages, and messages closely related to vocabulary specific for currency trading. I dropped all of those from my research not to overcomplicate things. In other words, the AsyncAPI file that you can see below is not a complete document."}),"\n",(0,t.jsxs)(n.li,{children:["Websocket protocol is very flexible, and therefore you can implement the server in many different ways. There is no standard way of doing things, like there is no common way of doing things with AsyncAPI. We can only make some generic assumptions looking at existing implementations:","\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:["Your server has one entry point, just one endpoint that you communicate with to gain access to the API. It can be a ",(0,t.jsx)(n.a,{href:"https://ik.imagekit.io/ably/s3/xchg_products/async_api_specs/000/000/019/original/weather.yaml",children:"path with some dynamic values"}),", as some data id. It can also be nothing, no path at all, like in the case of below Kraken API. These entry points are ",(0,t.jsx)(n.strong,{children:"channels"})," in AsyncAPI document. Commonly, Websocket API has just one ",(0,t.jsx)(n.strong,{children:"channel"})," that user can send messages to and receive messages at the same time"]}),"\n",(0,t.jsxs)(n.li,{children:["AsyncAPI publish and subscribe operations translates to ",(0,t.jsx)(n.strong,{children:"messages user can send to the API"})," and ",(0,t.jsx)(n.strong,{children:"messages user will receive from the API"}),". Depending on API complexity, sometimes you have an API that sends ",(0,t.jsx)(n.a,{href:"https://ik.imagekit.io/ably/s3/xchg_products/async_api_specs/000/000/019/original/weather.yaml",children:"only one message"}),". You can also have a situation where you can send to the server multiple different messages, and also receive different messages in response. This is when you need to use ",(0,t.jsx)(n.strong,{children:"oneOf"})," as I did in document for Kraken API."]}),"\n"]}),"\n"]}),"\n",(0,t.jsxs)(n.li,{children:["Current AsyncAPI limitation is that you cannot specify that once the user sends (publish) message ",(0,t.jsx)(n.strong,{children:"ping"}),", the ",(0,t.jsx)(n.strong,{children:"pong"})," message is a reply. Look at this ",(0,t.jsx)(n.a,{href:"https://github.com/asyncapi/spec/issues/94",children:"thread"})," to participate in an ongoing discussion about request/reply pattern support in AsyncAPI. In the below document, you will notice that for such a use case, I use AsyncAPI specification extensions (",(0,t.jsx)(n.strong,{children:"x-response"}),")."]}),"\n"]}),"\n",(0,t.jsxs)(n.blockquote,{children:["\n",(0,t.jsxs)(n.p,{children:[(0,t.jsx)(n.strong,{children:"Message to Kraken API developers and technical writers"})," ",(0,t.jsx)("br",{}),"\nIn case you want to continue the work I started on the AsyncAPI document for Kraken API, feel free to do that. I'm happy to help, just let me know. Reach me out in our ",(0,t.jsx)(n.a,{href:"https://www.asyncapi.com/slack-invite/",children:"AsyncAPI Slack workspace"}),"."]}),"\n"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-yml",children:'asyncapi: 2.0.0\n\ninfo:\n  title: Kraken Websockets API\n  version: \'1.8.0\'\n  description: |\n    WebSockets API offers real-time market data updates. WebSockets is a bidirectional protocol offering fastest real-time data, helping you build real-time applications. The public message types presented below do not require authentication. Private-data messages can be subscribed on a separate authenticated endpoint. \n\n    ### General Considerations\n\n    - TLS with SNI (Server Name Indication) is required in order to establish a Kraken WebSockets API connection. See Cloudflare\'s [What is SNI?](https://www.cloudflare.com/learning/ssl/what-is-sni/) guide for more details.\n    - All messages sent and received via WebSockets are encoded in JSON format\n    - All decimal fields (including timestamps) are quoted to preserve precision.\n    - Timestamps should not be considered unique and not be considered as aliases for transaction IDs. Also, the granularity of timestamps is not representative of transaction rates.\n    - At least one private message should be subscribed to keep the authenticated client connection open.\n    - Please use REST API endpoint [AssetPairs](https://www.kraken.com/features/api#get-tradable-pairs) to fetch the list of pairs which can be subscribed via WebSockets API. For example, field \'wsname\' gives the supported pairs name which can be used to subscribe.\n    - Cloudflare imposes a connection/re-connection rate limit (per IP address) of approximately 150 attempts per rolling 10 minutes. If this is exceeded, the IP is banned for 10 minutes.\n    - Recommended reconnection behaviour is to (1) attempt reconnection instantly up to a handful of times if the websocket is dropped randomly during normal operation but (2) after maintenance or extended downtime, attempt to re
1connect no more quickly than once every 5 seconds. There is no advantage to reconnecting more rapidly after maintenance during cancel_only mode.\n\nservers:\n  public:\n    url: ws.kraken.com\n    protocol: wss\n    description: |\n      Public server available without authorization.\n      Once the socket is open you can subscribe to a public channel by sending a subscribe request message.\n  private:\n    url: ws-auth.kraken.com\n    protocol: wss\n    description: |\n      Private server that requires authorization.\n      Once the socket is open you can subscribe to private-data channels by sending an authenticated subscribe request message.\n\n      The API client must request an authentication "token" via the following REST API endpoint "GetWebSocketsToken" to connect to WebSockets Private endpoints. For more details read https://support.kraken.com/hc/en-us/articles/360034437672-How-to-retrieve-a-WebSocket-authentication-token-Example-code-in-Python-3\n\n      The resulting token must be provided in the "token" field of any new private WebSocket feed subscription: \n'})}),"\n",(0,t.jsx)(n.p,{children:'{\n"event": "subscribe",\n"subscription":\n{\n"name": "ownTrades",\n"token": "WW91ciBhdXRoZW50aWNhdGlvbiB0b2tlbiBnb2VzIGhlcmUu"\n}\n}'}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{children:"\nchannels:\n/:\npublish:\ndescription: Send messages to the API\noperationId: processReceivedMessage\nmessage:\n  oneOf:\n    - $ref: '#/components/messages/ping'\n    - $ref: '#/components/messages/subscribe'\n    - $ref: '#/components/messages/unsubscribe'\n\nsubscribe:\ndescription: Messages that you receive from the API\noperationId: sendMessage\nmessage:\n  oneOf:\n    - $ref: '#/components/messages/pong'\n    - $ref: '#/components/messages/heartbeat'\n    - $ref: '#/components/messages/systemStatus'\n    - $ref: '#/components/messages/subscriptionStatus'\n\ncomponents:\nmessages:\nping:\nsummary: Ping server to determine whether connection is alive\ndescription: Client can ping server to determine whether connection is alive, server responds with pong. This is an application level ping as opposed to default ping in websockets standard which is server initiated\npayload:\n  $ref: '#/components/schemas/ping'\nx-response:\n  $ref: '#/components/messages/pong'\nheartbeat:\ndescription: Server heartbeat sent if no subscription traffic within 1 second (approximately)\npayload:\n  $ref: '#/components/schemas/heartbeat'\npong:\nsummary: Pong is a response to ping message\ndescription: Server pong response to a ping to determine whether connection is alive. This is an application level pong as opposed to default pong in websockets standard which is sent by client in response to a ping\npayload:\n  $ref: '#/components/schemas/pong'\nsystemStatus:\ndescription: Status sent on connection or system status changes.\npayload:\n  $ref: '#/components/schemas/systemStatus'\nexamples:\n  - payload:\n      connectionID: 8628615390848610000\n      event: systemStatus\n      status: online\n      version: 1.0.0\nsubscribe:\ndescription: Subscribe to a topic on a single or multiple currency pairs.\npayload:\n  $ref: '#/components/schemas/subscribe'\nexamples:\n  - payload:\n      event: subscribe\n      pair:\n        - XBT/USD\n        - XBT/EUR\n      subscription:\n        name: ticker\n  - payload:\n      event: subscribe\n      subscription:\n        name: ownTrades\n        token: WW91ciBhdXRoZW50aWNhdGlvbiB0b2tlbiBnb2VzIGhlcmUu\nx-response:\n  $ref: '#/components/messages/subscriptionStatus'\nunsubscribe:\ndescription: Unsubscribe, can specify a channelID or multiple currency pairs.\npayload:\n  $ref: '#/components/schemas/subscribe'\nexamples:\n  - payload:\n      event: unsubscribe\n      pair:\n        - XBT/EUR\n        - XBT/USD\n      subscription:\n        name: ticker\n  - payload:\n      event: unsubscribe\n      subscription:\n        name: ownTrades\n        token: WW91ciBhdXRoZW50aWNhdGlvbiB0b2tlbiBnb2VzIGhlcmUu\nx-response:\n  $ref: '#/components/messages/subscriptionStatus'\nsubscriptionStatus:\ndescription: Subscription status response to subscribe, unsubscribe or exchange initiated unsubscribe.\npayload:\n  $ref: '#/components/schemas/subscriptionStatus'\nexamples:\n  - payload:\n      channelID: 10001\n      channelName: ohlc-5\n      event: subscriptionStatus\n      pair: XBT/EUR\n      reqid: 42\n      status: unsubscribed\n      subscription:\n        interval: 5\n        name: ohlc\n  - payload:\n      errorMessage: Subscription depth not supported\n      event: subscriptionStatus\n      pair: XBT/USD\n      status: error\n      subscription:\n        depth: 42\n        name: book\n\nschemas:\nping:\ntype: object\nproperties:\n  event:\n    type: string\n    const: ping\n  reqid:\n    $ref: '#/components/schemas/reqid'\nrequired:\n  - event\nheartbeat:\ntype: object\nproperties:\n  event:\n    type: string\n    const: heartbeat\npong:\ntype: object\nproperties:\n  event:\n    type: string\n    const: pong\n  reqid:\n    $ref: '#/components/schemas/reqid'\nsystemStatus:\ntype: object\nproperties:\n  event:\n    type: string\n    const: systemStatus\n  connectionID:\n    type: integer\n    description: The ID of the connection\n  status:\n    $ref: '#/components/schemas/status'\n  version:\n    type: string\nstatus:\ntype: string\nenum:\n  - online\n  - maintenance\n  - cancel_only\n  - limit_only\n  - post_only\nsubscribe:\ntype: object\nproperties:\n  event:\n    type: string\n    const: subscribe\n  reqid:\n    $ref: '#/components/schemas/reqid'\n  pair:\n    $ref: '#/components/schemas/pair'\n  subscription:\n    type: object\n    properties:\n      depth:\n        $ref: '#/components/schemas/depth'\n      interval:\n        $ref: '#/components/schemas/interval'\n      name:\n        $ref: '#/components/schemas/name'\n      ratecounter:\n        $ref: '#/components/schemas/ratecounter'\n      snapshot:\n        $ref: '#/components/schemas/snapsh
1ot'\n      token:\n        $ref: '#/components/schemas/token'\n    required:\n      - name\nrequired:\n  - event\nunsubscribe:\ntype: object\nproperties:\n  event:\n    type: string\n    const: unsubscribe\n  reqid:\n    $ref: '#/components/schemas/reqid'\n  pair:\n    $ref: '#/components/schemas/pair'\n  subscription:\n    type: object\n    properties:\n      depth:\n        $ref: '#/components/schemas/depth'\n      interval:\n        $ref: '#/components/schemas/interval'\n      name:\n        $ref: '#/components/schemas/name'\n      token:\n        $ref: '#/components/schemas/token'\n    required:\n      - name\nrequired:\n  - event\nsubscriptionStatus:\ntype: object\noneOf:\n  - $ref: '#/components/schemas/subscriptionStatusError'\n  - $ref: '#/components/schemas/subscriptionStatusSuccess'\nsubscriptionStatusError:\nallOf:\n  - properties:\n      errorMessage:\n        type: string\n    required:\n      - errorMessage\n  - $ref: '#/components/schemas/subscriptionStatusCommon'\nsubscriptionStatusSuccess:\nallOf:\n  - properties:\n      channelID:\n        type: integer\n        description: ChannelID on successful subscription, applicable to public messages only.\n      channelName:\n        type: string\n        description: Channel Name on successful subscription. For payloads 'ohlc' and 'book', respective interval or depth will be added as suffix.\n    required:\n      - channelID\n      - channelName\n  - $ref: '#/components/schemas/subscriptionStatusCommon'\nsubscriptionStatusCommon:\ntype: object\nrequired:\n   - event\nproperties:\n  event:\n    type: string\n    const: subscriptionStatus\n  reqid:\n    $ref: '#/components/schemas/reqid'\n  pair:\n    $ref: '#/components/schemas/pair'\n  status:\n    $ref: '#/components/schemas/status'\n  subscription:\n    required:\n      - name\n    type: object\n    properties:\n      depth:\n        $ref: '#/components/schemas/depth'\n      interval:\n        $ref: '#/components/schemas/interval'\n      maxratecount:\n        $ref: '#/components/schemas/maxratecount'\n      name:\n        $ref: '#/components/schemas/name'\n      token:\n        $ref: '#/components/schemas/token'\ninterval:\ntype: integer\ndescription: Time interval associated with ohlc subscription in minutes.\ndefault: 1\nenum:\n  - 1\n  - 5\n  - 15\n  - 30\n  - 60\n  - 240\n  - 1440\n  - 10080\n  - 21600\nname:\ntype: string\ndescription: The name of the channel you subscribe too.\nenum:\n  - book\n  - ohlc\n  - openOrders\n  - ownTrades\n  - spread\n  - ticker\n  - trade\ntoken:\ntype: string\ndescription: base64-encoded authentication token for private-data endpoints.\ndepth:\ntype: integer\ndefault: 10\nenum:\n  - 10\n  - 25\n  - 100\n  - 500\n  - 1000\ndescription: Depth associated with book subscription in number of levels each side.\nmaxratecount:\ntype: integer\ndescription: Max rate-limit budget. Compare to the ratecounter field in the openOrders updates to check whether you are approaching the rate limit.\nratecounter:\ntype: boolean\ndefault: false\ndescription: Whether to send rate-limit counter in updates (supported only for openOrders subscriptions)\nsnapshot:\ntype: boolean\ndefault: true\ndescription: Whether to send historical feed data snapshot upon subscription (supported only for ownTrades subscriptions)\nreqid:\ntype: integer\ndescription: client originated ID reflected in response message.\npair:\ntype: array\ndescription: Array of currency pairs.\nitems:\n  type: string\n  description: Format of each pair is \"A/B\", where A and B are ISO 4217-A3 for standardized assets and popular unique symbol if not standardized.\n  pattern: '[A-Z\\s]+\\/[A-Z\\s]+'\n"})}),"\n",(0,t.jsxs)(n.blockquote,{children:["\n",(0,t.jsxs)(n.p,{children:[(0,t.jsx)(n.strong,{children:"Personal note"})," ",(0,t.jsx)("br",{}),"\nIf you can, if you are in a planning phase, new project, etc., then start designing your architecture with AsyncAPI. Don't do the mistake of coding first and then trying to figure out how to describe it with AsyncAPI ",(0,t.jsx)(n.span,{role:"img","aria-label":"grinning face with sweat",children:"\uD83D\uDE05"})]}),"\n"]}),"\n",(0,t.jsxs)(n.p,{children:["Stay tuned for the next blog post that guides you step by step through the above document ",(0,t.jsx)(n.span,{role:"img","aria-label":"peace symbol",children:"☮️"})]}),"\n",(0,t.jsxs)(n.blockquote,{children:["\n",(0,t.jsxs)(n.p,{children:["I recommend you also read another article from the series about WebSocket: ",(0,t.jsx)(n.a,{href:"/blog/websocket-part2",children:"Creating AsyncAPI for WebSocket API - Step by Step"}),"."]}),"\n"]})]})}function r(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(i,{...e})}):i(e)}function a(e,n){throw Error("Expected "+(n?"component":"object")+" `"+e+"` to be defined: you likely forgot to import, pass, or provide it.")}},19937:(e,n,s)=>{"use strict";s.d(n,{R:()=>i});var t=s(14232),o=s(4047);function i(e){let n=(0,t.useId)();return{...(0,o.o)(n),...e}}},90602:(e,n,s)=>{(window.__NEXT_P=window.__NEXT_P||[]).push(["/blog/websocket-part1",function(){return s(3105)}])}},e=>{e.O(0,[56166,90096,6729,9583,51550,4047,90636,46593,38792],()=>e(e.s=90602)),_N_E=e.O()}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.