-
Notifications
You must be signed in to change notification settings - Fork 3
Server API Overwiew
The plaything.io server supports both Web Sockets and regular Http[s] requests.
Web Sockets are required to take full advantage of Subscribe
calls as they allow for real-time updates rather than a one time fetch.
Every API call can be found in Api.ts
Currently, plaything.io only works with http://
(not https://
) and ws://
(not wss://
). This is not secure. It will be addressed in the future.
To connect a Web Socket, simply upgrade the connection to the http[s] server by connecting to ws[s]://address
.
Web Sockets send data over as a JSON
object. Most or all messages sent from the client will include a { type: string }
header
which indicates the message type.
To identify a response to a given message - if one is expected - a Web Socket can include an { id: number }
header, which will be mirrored in the response.
Web Sockets do not need to send session keys over (when a message requires it) because the server automatically maps a Web Socket to a session.
Nevertheless, when a Web Socket disconnects, that mapping is removed but can simply be restored with the RequestSessionReconnect
which accepts a { sessionKey: string }
field. This field is included in all session messages, but is optional for Web Sockets as long as the internal mapping exists.
Http[s] requests have their own request-response identification system and as such do not need the id
header, however it can still be used.
Http[s] requests are realized at the /api/<type>
or /api/<SESSION_KEY>/<type>
endpoint.
All request fields are serialized JSON embedded into the GET
query as properties of an object.
For example /api/login?nickname=myNickname
will send the following object: { type: 'login', nickname: 'myNickname' }
The response to a Http[s] request will have a json/application
MIME type.
An invalid call, such as a Web Socket call that is not a JSON object or a Http[s] call to an invalid location will result in an { error: 'incorrect API call' }
response.
An Api.Request*
call:
- Has a
type: string
field. If this field is an invalid value, the server will respond with{ error: 'Invalid request' }
- Might require a
{ sessionKey: string }
header (API.SessionRequest). If no session key is provided and no internal mapping (for a Web Socket) exists, the server will respond with{ result: 'session not found' }
(API.InvalidSession) - If the call is successful, the server will respond with a corresponding
API.Response*
. There exists a mapping from the request type field to response type -RequestResponseMap
- it can be used to strongly type API call handlers
An API.Subscribe*
call:
- Has a
type: string
field - Might require an API.SessionRequest header
- If the call is successful, the server will respond with a corresponding
API.Response*
. It is part of theRequestResponseMap
. This response will have all current information - If the call is made over a Web Socket, it will subscribe it to
Heartbeat
events which inform about real-time changes to the resource. This kind of subscription is cancelled automatically when the Web Socket closes or you no longer have access to the resource, for example - after leaving a room
An API.Message*
call:
- Has a
type: string
field - Might require an API.SessionRequest header
- Is a fire-and-forget call and will not generate a server response
An API.Response*
:
- Has to mirror the
{ id: number }
request header if present - Might have a
{ result: string }
header indicating if the request succeeded ('ok') or not (anything else, as an informative message from a closed set of options)
An API.Heartbeat*
:
- Is a real-time update the server sends to Web Sockets
- Has a
type: string
field which starts with 'heartbeat-' - Might have a
kind: string
field which indicates what kind of update it is. For example 'added', 'updated' or 'removed'
All API types need to be located inside the API namespace in Api.ts
All API categories need static type checking. For example, the Request
category is organized like this:
export type RequestA = { ... }
export type RequestB = { ... }
export type RequestC = { ... }
type RequestTypes = RequestA | RequestB | RequestC // this type is not exported as it is not checked yet
export type Request = Extract<RequestTypes, { type: string }> // make sure all request types have a type field
All API requests that have a response need to be a part of the RequestResponseMap
When an API call needs an active session, the request type must be an &
union with the API.SessionRequest
type
and the response must be an |
union with the API.InvalidSession
type.