@push-rpc/openapi v1.9.0
A framework for organizing bidirectional client-server communication based on JSON, with server-initiated data push.
Client establishes connection to server and then client and server exchange JSON packets.
Structure of the JSON packets is based on WAMP, but not strictly conform to it. Instead, it conforms to OCPP-J RPC Framework. More precisely, Push-RPC protocol is a superset of OCPP-J RPC protocol, with additional push capabilities.
Push-RPC allows you to:
- Bi-directionally invoke remote methods on server and client
- Subscribe client for server-side events and vice versa
- Auto-reconnect with subscription refresh
- Create type-safe contracts of remote APIs using TypeScript code shared between client and server
- Create local proxies for remote objects, including support for transferring Date object
- Supported client envs: Node.JS (with
isomorphic-fetch
), browser, React Native.
Supports multiple pluggable transports:
- WebSocket
- Plain TCP
- REST-like (planned).
Possible Applications
- Data-driven apps with or without server-initiated updates
- OCPP-J clients and servers
- IoT devices connecting to servers
- General client/server apps using WebSockets for communications
Getting Started
Installation
yarn add @push-rpc/core @push-rpc/websocket
For the server, you will also need
yarn add ws
You can use standard browser WebSockets on the client, or use ws
npm package.
Example code
shared.ts:
import {Topic} from "@push-rpc/core"
export interface Services {
todo: TodoService
}
export interface TodoService {
addTodo({text}, ctx?): Promise<void>
todos: Topic<Todo[]>
}
export interface Todo {
id: string
text: string
status: "open" | "closed"
}
client.ts:
import {createRpcClient} from "@push-rpc/core"
import {createWebsocket} from "@push-rpc/websocket"
import {Services} from "./shared"
;(async () => {
const services: Services = (
await createRpcClient(1, () => createWebsocket("ws://localhost:5555"))
).remote
console.log("Client connected")
services.todo.todos.subscribe(todos => {
console.log("Got todo items", todos)
})
await services.todo.addTodo({text: "Buy groceries"})
})()
server.ts:
import {createRpcServer, LocalTopicImpl} from "@push-rpc/core"
import {createWebsocketServer} from "@push-rpc/websocket"
import {Services, Todo, TodoService} from "./shared"
let storage: Todo[] = []
class TodoServiceImpl implements TodoService {
async addTodo({text}) {
storage.push({
id: "" + Math.random(),
text,
status: "open",
})
console.log("New todo item added")
this.todos.trigger()
}
todos = new LocalTopicImpl(async () => storage)
}
const services: Services = {
todo: new TodoServiceImpl(),
}
createRpcServer(services, createWebsocketServer({port: 5555}))
console.log("RPC Server started at ws://localhost:5555")
Run server.ts
and then client.ts
.
Server will send empty todo list on client connecting and then will send updated list on adding new item.
API
TBD
Protocol Details
You can use this information to implement Push-RPC protocol in different languages.
Each message is encoded as JSON array. Each message contain message type, message ID, and multiple payload fields. For example, CALL message:
[2, "dfd9742e-2d44-11ea-978f-2e728ce88125", "getRemoteData", {}]
Possible new features
- Binary data transfer
- Generating OpenAPI (Swagger) YAMLs with API description
REST transport
FAQ
How to add path to WebSockets (routing)
Often there're multiple WebSocket servers using same port but different paths. You can use following code to route connections between those servers.
import * as WebSocket from "ws"
import * as http from "http"
function websocketRouter(httpServer, routes) {
httpServer.on("upgrade", (request, socket, head) => {
const pathname = url.parse(request.url).pathname
const serverKey = Object.keys(routes).find(key => pathname.indexOf(key) == 0)
if (!serverKey) {
socket.destroy()
} else {
const server = routes[serverKey]
server.handleUpgrade(request, socket, head, ws => {
server.emit("connection", ws, request)
})
}
})
}
...
const server = http.createServer(requestListener).listen(5555)
const ocppServer: WebSocket.Server = await createOcppServer()
const clientServer: WebSocket.Server = await createClientServer()
const adminServer: WebSocket.Server = await createAdminServer()
websocketRouter(httpServer, {
["/ocpp:"]: ocppServer,
["/client"]: clientServer,
["/admin"]: adminServer,
})
4 months ago
4 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
7 months ago
11 months ago
11 months ago
1 year ago
1 year ago
1 year ago
1 year ago
2 years ago
2 years ago
1 year ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago