Tigran Bayburtsyan

Co-Founder and CTO at Hexact Inc. (https://hexometer.com). Software scalability expert!

Real-time React app with GraphQL + Websocket

GraphQL started to be a great option for writing customizable API’s and combining multiple services into one single endpoint. The whole idea of having single endpoint but getting different combination of models is very attractive, especially for companies which are working with large scale multi-platform applications with different frontend/mobile developers and designers.

This is generally for making REST API’s, and for many cases that’s the way to go for making application backends. But what if you want to give more real time feel and look to your application UI or make subscription based data transfers for improving more efficient infrastructure.

Getting project ready 🚀

In this era of Webpack, React and other large JS frameworks it is important to have generically configured project for having scalable codebase.

Here is the combination that we are going to configure

  1. Node.js + GraphQL.js + Websocket Subscriptions + Express.js if we need some generic REST API functionality as well
  2. React.js + Apollo + Websocket Subscriptions Client

For me configuring backend first is a tradition, but generally it doesn’t matter.

Configuring Node.js backend

Let’s just make a basic setup for node.js application with NPM and minimum packages which are required (no junk!).

npm init
npm i --save graphql subscriptions-transport-ws express
touch index.js # making main file for Node server
touch schema.js # creating GraphQL schemas here

NOTE: if you want also ES2015 you need to configure Babel as well, there is a lot of articles and boilerplates around that, so skipping that part here.

After reading documentation and knowing what we need here is how looks like out index.js file

import { Server } from 'http';
import { SubscriptionServer } from 'subscriptions-transport-ws';
import { execute, subscribe } from 'graphql';
import GraphHTTP from 'express-graphql';
import express from 'express';
import { expressAuth, wsAuth } from './auth_jwt';

// Getting base GraphQL Schema
import schema from './schema';

/** BASE Express server definition **/
const app = express();

// main endpoint for GraphQL Express
app.use('/api/ql', GraphHTTP({
schema: schema,
graphiql: true,
}));

// Making plain HTTP server for Websocket usage
const server = Server(app);

/** GraphQL Websocket definition **/
SubscriptionServer.create({
schema,
execute,
subscribe,
}, {
server: server,
path: '/api/ws',
}, );


server.listen(4000, () => {
console.log('Server started here -> http://0.0.0.0:4000');
});

Now we have a server which is going to respond with the same GraphQL scheme, for both Websocket /api/ws and REST Express /api/ql .

Let’s make a basic GraphQL schema and export that from schema.js

import {
GraphQLSchema,
GraphQLObjectType,
GraphQLString
} from 'graphql';

export default new GraphQLSchema ({
query: new GraphQLObjectType ({
name: 'Query',
fields: {
message: {
type: GraphQLString,
resolve() { return 'Hello World!'; }
}
}
})
});

I know, nothing fancy, but still you can get the point, and do more better for your project.

Just start a server and go to localhost:4000/api on your browser, and you will see GraphQL endpoint for getting message

Configuring React App

To get started with React app it is pretty easy and useful to make it with create-react-app CLI tool, which is just giving you a lot of preconfigured things!

create-react-app graphql-ws
cd graphql-ws
npm i --save graphql graphql-tag subscriptions-transport-ws react-apollo apollo-cache-inmemory apollo-client apollo-link-http

Most probably you will need React Router, Redux etc… But that’s a different story.

After making graphql-ws React app, configuration for having GraphQL Websocket connections looks like this in src/index.js file

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import { ApolloProvider } from 'react-apollo';
import { InMemoryCache } from 'apollo-cache-inmemory';
import {SubscriptionClient} from 'subscriptions-transport-ws';
import ApolloClient from 'apollo-client';

// Create WebSocket client
const WSClient = new SubscriptionClient(`ws://localhost:4000/api/ws`, {
reconnect: true,
connectionParams: {
// Connection parameters to pass some validations
// on server side during first handshake
}
});

const GraphQLClient = new ApolloClient({
link: WSClient,
cache: new InMemoryCache()
});

ReactDOM.render(
<ApolloProvider client={GraphQLClient}>
<App />
</ApolloProvider>,
document.getElementById('root'));
registerServiceWorker();

Now when you start your frontend and backend applications you should be able to see Websocket connection from browser development tools. You can watch what kind of data transfer is going on using Networking tab (but I’m sure you already know that!).

Last thing that we need to do is make our Hello world! message available for our users (again, nothing fancy). Let’s connect react-apollo to our main App component. Here is how App.js looks like for this case.

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import { graphql } from 'react-apollo';

const HelloQuery = gql`
query Hello {
hello
}
`;

@graphql(HelloQuery, { name: 'helloQuery' })
class App extends Component {
render() {
const { helloQuery } = this.props;

return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Welcome to React</h1>
</header>
<p className="App-intro">
Message from GraphQL Websocket backend
<string>{ helloQuery.hello }</string>
</p>
</div>
);
}
}

export default App;

Apollo client will query hello field that we defined as a Root query field, and resolve function will return our text. The main beauty of this process is that after configuring websocket client for Apollo client, the rest of the codebase stays the same, but the performance of data transfer is match better!

This setup is not ideal for most of the applications like where you don’t need always available server, or you want to go with available free services without ability to deploy custom Node.js server, but if you are building some in-house application and want to get max performance, better UI responsiveness, better UX for backend communications and better analytic event messages, you should at least check this out!

We’ve done all TreeScale client’s dashboards using this principle and results are remarkably better than standard REST API with request-response principle. BUT you should consider that scalability of real-time connections requires more skills, so be prepared for that, and hack around 💥

👏 So, clap for this! And write your story with this as a comment!

More by Tigran Bayburtsyan

Topics of interest

More Related Stories