1.9.0 • Published 4 months ago

@push-rpc/openapi v1.9.0

Weekly downloads
22
License
MIT
Repository
github
Last release
4 months ago

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,
})
1.9.0

4 months ago

1.8.3

4 months ago

1.8.2

6 months ago

1.8.1

6 months ago

1.8.0

6 months ago

1.7.1

6 months ago

1.7.0

6 months ago

1.6.5

7 months ago

1.6.3

11 months ago

1.6.2

11 months ago

1.6.1

1 year ago

1.6.0

1 year ago

1.5.8

1 year ago

1.5.7

1 year ago

1.5.5

2 years ago

1.5.4

2 years ago

1.5.6

1 year ago

1.5.3

2 years ago

1.5.2

2 years ago

1.5.1

2 years ago

1.4.2

2 years ago

1.5.0

2 years ago

1.4.1

2 years ago

1.4.0

2 years ago

1.3.3

3 years ago

1.3.2

3 years ago

1.3.1

3 years ago

1.3.0

3 years ago

1.2.12

3 years ago

1.2.10

3 years ago

1.2.11

3 years ago

1.2.9

3 years ago

1.2.8

3 years ago

1.2.7

3 years ago

1.2.6

3 years ago

1.2.5

3 years ago

1.2.3

3 years ago

1.2.2

3 years ago

1.2.1

3 years ago

1.2.0

3 years ago

1.1.9

3 years ago

1.1.8

3 years ago

1.1.5

3 years ago

1.1.4

4 years ago

1.1.3

4 years ago

1.1.2

4 years ago

1.0.2

4 years ago

1.0.1

4 years ago