Rethinkdb tornado graphql chat part 1

Today let's try something different. Slack market capital is currently arround 18.92B of dollars.
Why not make persistent chat with chatrooms using some modern open source technologies like Docker for scalability, tornado for webserver, graphql for data exchange and rethinkdb for persistant layer.
The last one is chosen specifically to produce less code cause of change feeds. From all of those technologies I never made graphql subscriptions and never used rethinkdb in my life and it's going to be interesting.

Let's start with set up backend environment by creating virtualenv and adding dependencies

python3 -m venv tornado-chat-env
pip install tornado graphene-tornado rethinkdb

I will use graphene-tornado to make application and create subscriptions by myself based on code from github repositoryhballard/graphql-python-subscriptions

After I copy / paste code from graphene-tornado example and go to url http://localhost:5000/graphql everything worked. There was even some simple schema with query I could use.

Tornado graphql query

Base backend setup is finished so it's time to setup frontend. I will use basic config from my basic webpack react babeljs es6 setup blog post and add some dependencies according to apollo client documentation

yarn add apollo-boost graphql-tag graphql

And then modify basic application to make request to tornado web server.

import ReactDOM from 'react-dom';
import React, {useState} from 'react';
import ApolloClient from 'apollo-boost';
import gql from 'graphql-tag';


const Test = () => {

  const [msg, setMessage] = useState('Loading...');

  const client = new ApolloClient({
    uri: 'http://localhost:5000/graphql?'
  });

  client.query({
    query: gql`
    query {
      test
    }
  `,
  }).then(data => {
    setMessage(data.data.test);
  })
  .catch(error => console.error(error));
  return (<h1>{msg}</h1>);
}

ReactDOM.render(<Test />, document.getElementById('app'));

And of course this example failed due to stupid CORS that can't be easliy disabled for development purposes. Thank you browser vendros for loving backend developers they love you to.

CORS browser error

To obey it I need to extend TornadoGraphQLHandler from example so my base server looks like that.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import tornado.web
from tornado.ioloop import IOLoop

from graphene_tornado.schema import schema
from graphene_tornado.tornado_graphql_handler import TornadoGraphQLHandler


class CorsHandler(TornadoGraphQLHandler):
    def set_default_headers(self):
        self.set_header("Access-Control-Allow-Origin", "*")
        self.set_header('Access-Control-Allow-Methods', 'POST, PUT, GET, OPTIONS')
        self.set_header('Access-Control-Allow-Headers', 'content-type')

    def options(self):
        self.finish()

class ExampleApplication(tornado.web.Application):

    def __init__(self):
        handlers = [
            (r'/graphql', CorsHandler, dict(graphiql=True, schema=schema)),
            (r'/graphql/batch', CorsHandler, dict(graphiql=True, schema=schema, batch=True)),
            (r'/graphql/graphiql', CorsHandler, dict(graphiql=True, schema=schema))
        ]
        tornado.web.Application.__init__(self, handlers)

if __name__ == '__main__':
    app = ExampleApplication()
    app.listen(5000)
    print("ok")
    IOLoop.instance().start()

And I have this nice basic output.

Hello world

Ok so now it's time to setup some database. I won't install it locally but use docker and I will also create docker-compose file right away so I can configure my frontend and backend there later.
I got lucky because there is official RethinkDB docker image so I can just use it. Unfortunately there is no documentation on hub website where it's storing data so I needed to look inside Dockerfile of this container to know where to mount my volume and what ports I want to expose.

So my compose yaml file with only one service for now looks like that

version: '2'
services:
  chat_db:
    image: rethinkdb:2.3.6
    container_name: chat_db
    hostname: chat_db
    restart: always
    networks:
      - chat_network
    volumes:
      - ./data/chat_db:/data
    expose:
      - 28015
    ports:
      - 8080:8080
      - 28015:28015
      - 29015:29015
networks:
  chat_network:

It worked from first try and I could see web admin interface.

CORS browser error

There is simple error in documentation so instead of

import rethinkdb as r

I used code like below to connect to my database.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import rethinkdb

r = rethinkdb.RethinkDB()
r.set_loop_type("tornado")
connection = r.connect(host='localhost', port=28015)

Yeah it works, now it's time to create database and some graphql schema.

Source code of this post