Skip to main content

Simple framework to create awesome WebSocket APIs

Project description

ringding - Simple framework to create awesome WebSocket APIs

What is ringding?

Ringding is a framework to communicate between a Python server and JavaScript client(s) based on WebSocket. Compared to Web API frameworks like Flask or FastAPI, the ringding API is specified in a more "Python-API-style" way using classes rather than using decorators and "magic strings". The call of API functions on the JavaScript side also looks more like function calls.

When do I want to use ringding?

  • When you want to call function on your server and don't want to provide a full-fledged REST-API.
  • When you want to minimize the overhead from HTTP-headers (especially in Single-Page-Applications / SPAs).
  • When you want to publish a Python-API to a JavaScript client.
  • When the server should notify the clients about events.

What technology does ringding use?

Ringding is transmitting the data using WebSockets.

How do I use ringding?

Ringding provides a JavaScript-client and a Python server. The JavaScript client ringdingjs is hosted by the server on the endpoint /client.js. But you can also extract ringdingjs.js from source and put it somewhere in your project. It is located in clients/js/ringdingjs.js. You will want to use the corresponding package.json for Node since Node not support WebSockets by default. If you want to use ringdingjs in browser apps, you don't need any dependencies.

An example Python backend:

import ringding


class MyApi:
    def get_name(self):
        return 'Alfred'
    
    def sum(self, a, b):
        return a + b


if __name__ == '__main__':
    ringding.RdServer(port=8001).serve(MyApi)

In NodeJS, you can use ringding like this:

const { RD, CallApi } = require('ringdingjs')

async function communicate() {
    await RD.connect('ws://localhost:8001')
    
    const name = await CallApi().MyApi.get_name()
    console.log('The name is', name)
    
    // You can provide keyword arguments to a function call.
    let sum = await CallApi().MyApi.sum({a: 5, b: 2})
    
    // You can also use the more flexible direct call:
    let another_sum = await RD.call("MyApi.sum(*)", {a: 4, b: 2}) 
    
    console.log('The sum is: ', sum)
    
    RD.disconnect()
}

communicate()

Alternatively in a browser, you can connect and call the sum-function like this:

<head>
    <meta charset="UTF-8">
    <title>Hello ringding</title>
</head>
<body>
    <div>Calling "API.sum()" with arguments {a: 5, b: 2}"</div>
    <div>Response: <span id="response"></span></div>
</body>
<script data-main="scripts/app" src="http://localhost:8001/client.js"></script>
<script>
    async function run_example() {

        // Connect to server
        await RD.connect('ws://localhost:8001')

        // Send a message
        const response = await CallApi().MyApi.sum({a: 5, b: 2})

        // Read the response
        console.log('Response:', response)
        document.getElementById('response').innerText = String(response)
    }
    run_example()
</script>
</html>

Other features

Chained calls

With chained calls you can call multiple parameterized functions on a server:

RD.call('Api.create_user(*user_data).get_hash(hash_type), {user_data: {name: 'Fido', age: 42}, hash_type: 'md5'}

Advanced return types

You can return every JSON-supported datatype (including lists and dicts with simple datatypes) or dataclasses.

import ringding

import dataclasses


@dataclasses.dataclass
class AdvancedReturnType:
    name: str
    age: int


class Api:
    def get_user(self):
        return AdvancedReturnType('Fido', 42)
    
if __name__ == '__main__':
    ringding.RdServer(port=8001).serve(Api)

The corresponding JavaScript call is this

const response = await CallApi().Api.get_user()
console.log(response.name) // "Fido"
console.log(response.age)  // 42

Nested APIs

I would recommend to use properties for nested Sub-API's on python side

import ringding


class Nested:
    def do_something(self):
        return 'I did something'

    
class Api:
    @property
    def NestedApi(self):
        return Nested()
    
if __name__ == '__main__':
    ringding.RdServer(port=8001).serve(Api)

And the JavaScript-call would be CallApi().Api.NestedApi.do_something()

A word on Typescript

You might want to create an own object of CallApi() and cast you API specification to that object. That way you could get an API with autocompletion powered by Typescript.

Roadmap

  • An interface to browse API's and their documentation

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ringding-0.3.2.tar.gz (19.9 kB view hashes)

Uploaded Source

Built Distribution

ringding-0.3.2-py3-none-any.whl (22.5 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page