Comments (6)
Thanks a lot. I knew I was wrong in my understanding of async, I just need a confirmation and you answered it perfectly.
You rock !
from spec.
Not sure I understand why you chose OpenAPI if you want to do it with messages. What I said previously, is that only the first call to grab the current status of users can be documented using OpenAPI as it makes sense because it's a synchronous operation over HTTP.
Then, the rest of operations can be documented using AsyncAPI because they are events over WebSockets.
If you want to go the full way with events it's also fine and you can do it. So let's break this up into many cases:
OpenAPI + AsyncAPI
OpenAPI file to describe the first operation (grabbing the current status of users):
paths:
/users_status
get:
responses:
200:
content:
schema:
$ref: "#/components/schemas/UserStatusResponse"
components:
schemas:
UserStatusResponse:
type: array
items:
type: object
properties:
username:
type: string
full_name:
type: string
status:
type: string
enum:
- active
- busy
- away
- disconnected
And now the AsyncAPI file to describe the events:
topics:
yourcompany.user.1.0.event.user.status.list.changed:
subscribe:
$ref: "#/components/messages/UserStatusListChangedMessage"
publish:
$ref: "#/components/messages/UserStatusListChangedMessage"
yourcompany.user.1.0.event.user.status.changed:
subscribe:
$ref: "#/components/messages/UserStatusChangedMessage"
publish:
$ref: "#/components/messages/UserStatusChangedMessage"
components:
messages:
UserStatusListChangedMessage:
payload:
type: array
items:
$ref: "#/components/schemas/user"
UserStatusChangedMessage:
payload:
$ref: "#/components/schemas/user"
schemas:
user:
type: object
properties:
username:
type: string
full_name:
type: string
status:
type: string
enum:
- active
- busy
- away
- disconnected
Only AsyncAPI
If you choose to only use events, then you have to change your approach. When you connect to the server using WebSockets you'll send a message saying you just connected. It's very easy to fall into the temptation of creating a message saying: "Give me the list of active users". If you want to use event-driven design the message should never be an action (i.e. Give me) but instead, it should be an event (i.e. I just connected). Here's the AsyncAPI file (roughly) for this:
topics:
yourcompany.user.1.0.event.user.status.list.changed:
subscribe:
$ref: "#/components/messages/UserStatusListChangedMessage"
publish:
$ref: "#/components/messages/UserStatusListChangedMessage"
yourcompany.user.1.0.event.user.status.changed:
subscribe:
$ref: "#/components/messages/UserStatusChangedMessage"
publish:
$ref: "#/components/messages/UserStatusChangedMessage"
yourcompany.frontend.1.0.event.connected:
subscribe:
$ref: "#/components/messages/FrontendConnectedMessage"
publish:
$ref: "#/components/messages/FrontendConnectedMessage"
components:
messages:
UserStatusListChangedMessage:
payload:
type: array
items:
$ref: "#/components/schemas/user"
UserStatusChangedMessage:
payload:
$ref: "#/components/schemas/user"
FrontendConnectedMessage:
payload:
$ref: "#/components/schemas/FrontendConnected"
schemas:
user:
type: object
properties:
username:
type: string
full_name:
type: string
status:
type: string
enum:
- active
- busy
- away
- disconnected
FrontendConnected:
type: object
You might be wondering why I'm always duplicating publish
and subscribe
for each topic. Well, it's because I'm defining the API for your whole system (backend and frontend) in a single file. You could have them in separate files if you want.
Hope it helps.
from spec.
Hi @jogelin, I see a few problems here:
Your code is asynchronous, your approach does not
What you're trying to achieve, as per my understanding, is not asynchronous. You said:
the frontend display the loader and wait
If your app has to wait, then this is the first sign that it's not async. If you still want to do it like this I'll better suggest you use an HTTP API instead of WebSockets communication, because your approach is based on request/response. Of course, you can always implement RPC-style APIs using message-driven communication but that's a land I don't recommend you to walk. However, let's talk about it.
RPC
What I misunderstand is the link between request message and response message.
There's no link between messages. At least not if you don't define it. You can create a unique identifier for every message and respond back with a message referencing it. For instance, you send a message like this one:
{
"id": "1234",
"status": "active"
}
and the server will respond back with a message like this:
{
"id": "5678",
"responseTo": "1234",
"users": [
{ "id": "fmvilas", "name": "Fran" },
{ "id": "jogelin", "name": "Jogelin" }
]
}
That will perfectly work, however, what you would be doing there is exactly what you get with an HTTP API. So I'd not mess around with RPC over WebSockets and would use an HTTP API instead.
Event-driven communication
If you can change how your app works I'd suggest you to events instead of requests and responses. An example could be:
- Your app loads and gets the current list of active users via an HTTP API.
- This is not event-driven communication yet.
- This could be documented using OpenAPI instead of AsyncAPI.
- Your app connects to the server using WebSockets and will listen for user status changes.
- Every time a user changes her status, the server will send a message to the frontend saying: "Hey! this is the new list of active users".
- This event can be documented using AsyncAPI.
- Every time the user who is logged in changes its status, the frontend sends a message to the server saying: "Hey! user
jogelin
has changed his status tobusy
".- This event can be documented using AsyncAPI.
You have to think about the messages in step 3 and 4 as fire and forget. You just communicate that "something happened". You never expect a response.
I hope this sheds some light on your problem. I'm happy to help further if you need it.
from spec.
Thanks! I'm glad it helped. Feel free to ask whenever you have questions 👍
from spec.
Feel free to ask whenever you have questions
Hum..... so I have a question :p
Since your great answer, I am trying to specify my api with OpenApi but I would like to keep a Message Driven way....is that possible ?....:)
I am working on an app using:
- On the frontend: Angular 2+ with a Redux system which is a system of store that used a list of actions (messages) to manage it (google if you don't know ;)).
- On the backend: microservices scala + akka using commands to communicate between them
And the goal is to be able to communicate directly the actions of the frontend to the backend without mapping each of the actions to an http endpoint. The backend will also answer to the frontend using message formats. It is why I choosed websocket but I completly agree, websocket should not be used to synchronous requests.
So I think to have only one POST endpoint on my service which is receiving a message et returning another message.
Example:
POST to /userService/api with application/json
requestBody =
{
headers: {
version: "1.0.0",
jobName: "organisation.userService.query.user.getAll"
},
payload: {
status: "active"
}
}
responseBody 200 =
{
headers: {
version: "1.0.0",
jobName: "organisation.userService.query.user.getAll"
},
payload: [
{
id: "123",
name: "arthur"
},
{
id: "456",
name: "julien"
}
]
}
My first try:
openapi: 3.0.0
info:
title: User API
version: 1.0.0
paths:
'/user/api':
post:
description: Publish a message
requestBody:
content:
'application/json':
schema:
# here we can specify a list of all accepted messages
oneOf:
- $ref: '#/components/schemas/getAllUserMessage'
- $ref: '#/components/schemas/getAllUserDetailMessage'
responses:
200:
description: Response Message
content:
'application/json':
schema:
# here we can specify a list of all response messages
oneOf:
- $ref: '#/components/schemas/getAllUserSuccessMessage'
- $ref: '#/components/schemas/getAllUserDetailSuccessMessage'
components:
schemas:
Headers:
type: object
required:
- jobName
- version
properties:
jobName:
type: string
version:
type: string
Message:
type: object
required:
- headers
- payload
properties:
headers:
type: object
$ref: '#/components/schemas/Headers'
payload:
type: object
getAllUserMessage:
allOf:
- $ref: '#/components/schemas/Message'
- type: object
properties:
headers:
properties:
jobName:
enum: ['organisation.userService.query.user.getAll']
payload:
properties:
status:
type: string
getAllUserSuccessMessage:
allOf:
- $ref: '#/components/schemas/Message'
- type: object
properties:
headers:
properties:
jobName:
enum: ['organisation.userService.query.user.getAll']
payload:
type: array
items:
$ref: '#/components/schemas/User'
getAllUserDetailMessage:
allOf:
- $ref: '#/components/schemas/Message'
- type: object
properties:
headers:
properties:
jobName:
enum: ['organisation.userService.query.userDetail.getAll']
payload:
properties:
status:
type: string
getAllUserDetailSuccessMessage:
allOf:
- $ref: '#/components/schemas/Message'
- type: object
properties:
headers:
properties:
jobName:
enum: ['organisation.userService.query.userDetail.getAll']
payload:
type: array
items:
$ref: '#/components/schemas/UserDetail'
Resource:
type: object
required:
- id
- name
properties:
id:
description: 'Resource identifier'
type: string
name:
description: 'Resource name'
type: string
User:
type: object
$ref: '#/components/schemas/Resource'
UserDetail:
type: object
$ref: '#/components/schemas/Resource'
But it looks very complex (it is why I feel that asyncapi was good for that) so if you have any advices...?
from spec.
You might want to join our Slack channel: https://async-apis-slack.herokuapp.com/
from spec.
Related Issues (20)
- Should the Reply Object extend from the Operation Object? HOT 5
- Channel parameter type HOT 2
- Send/Receive again confusion HOT 3
- AsyncAPI v3 retrospective HOT 1
- Undefined description when using `OneOf`, `AllOf` or `AnyOf` HOT 3
- Undefined behaviour of "messageId" for Message Traits and Messages defined in components. HOT 4
- Avro specification inside AsycnApi file HOT 4
- Server Object host field compatibility with protocols HOT 3
- testing -input command not found HOT 2
- Possible bug with example - adeo-kafka-request-reply-asyncapi HOT 18
- Decide what to do with OAS schema properties HOT 5
- Allow plain `string` in Message Example Object field payload for non-json payloads (like xml, yaml) HOT 7
- when to finish amqp serverBindings HOT 2
- Divide "Maintainer" role into two categories: Triager and Committer HOT 7
- How to define MQTT User Properties in an AsyncAPI document? HOT 2
- Extend Avro and OpenAPI schema versions HOT 13
- Multiple reply addresses HOT 2
- Can i document an endpoint for a cron job? HOT 2
- trait vs. allOf ref vs. component ref - CloudEvents HOT 4
- Use asyncapi for sunc api with redis transport HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from spec.