React, Python and GraphQL: intro and setup

July 03, 2018

This is the first post in a series dedicated to building modern web applications with React and Python, using a GraphQL API.

As an example, we’re going to create a simple web-based chat application (think of it as a Slack clone).

Outline

  1. Day #1: React project setup using Neutrinojs
  2. Day #2: GraphQL server: Flask, Graphene, PostgreSQL via SQLAlchemy
  3. Day #3: GraphQL client via Apollo Client
  4. Day #4: GraphQL subscriptions via websocket
  5. Day #5: File uploads in GraphQL, using multipart requests

Software stack

Getting started: project setup

Let’s start by creating our project folder and git repository:

mkdir yawc
cd yawc
git init

Aso create a .gitignore file containing the following:

*~
*.pyc
node_modules

We’re ready to create our first commit:

git add .gitignore
git commit -m 'Initial'

Let’s proceed to create a new Neutrino project.

The project is going to be in a sub-folder of the main repository, so we can have both client and server code in the same repository, for convenience:

npx @neutrinojs/create-project ./web
...
? 🤔  First up, what would you like to create? A web or Node.js application
? 🤔  Next, what kind of application would you like to create? React
? 🤔  Would you like to add a test runner to your project? Jest
? 🤔  Would you like to add linting to your project? StandardJS rules
...

Project configuration

It’s now time to start customizing our configuration. Open .neutrinorc.js and make the following changes to the use exported variable.

Change default host / port

module.exports = {
    use: [

        '@neutrinojs/standardjs',

        ['@neutrinojs/react', {
            devServer: {
                host: '127.0.0.1',
                port: 8000,
                https: false,
            },
            html: {
                title: 'yawc'
            }
        }],

        // ...
    ]
};

Use absolute paths, to allow navigation to nested pages

This is required in order to support navigation to deep-nested pages, eg /foo/bar/baz:

// Use absolute paths or it will break on nested pages...
(neutrino)=> {
    neutrino.config.output.publicPath("/");
},

Add support for SCSS modules

First, let’s install the required dependencies:

yarn add --dev @neutrinojs/style-loader autoprefixer node-sass postcss-loader sass-loader

And add the relevant configuration in .neutrinorc.js:

['@neutrinojs/style-loader', {
    test: /\.global\.(css|sass|scss)$/,
    modulesTest: /(?<!\.global)\.(css|sass|scss)$/,
    modules: true,
    css: {
        localIdentName: '[local]--[hash:base64:8]',
    },
    loaders: [
        {
            loader: 'sass-loader',
            useId: 'sass',
            options: {
                includePaths: ['node_modules', 'src'],
                localIdentName: '[local]--[hash:base64:8]',
            }
        },
        {
            loader: 'postcss-loader',
            options: {
                plugins: [
                    require('autoprefixer')({
                        browsers: [
                            '>1%',
                            'last 4 versions',
                            'Firefox ESR',
                            'not ie < 9', // React doesn't support IE8 anyway
                        ],
                        flexbox: 'no-2009',
                    }),
                ]
            }
        }
    ]
}]

Allow resolving modules from ./src

This allows to use absolute imports for our application code.

import Foo from 'components/Foo';  // Good
import Foo from '../../../components/Foo';  // Bad

Add this to .neutrinorc.js:

(neutrino) => {
    neutrino.config.resolve
            .modules
            .add(neutrino.options.source);
}

Configure eslint

We’re going to use eslint configuration from Create React App.

Install dependencies:

yarn add --dev @neutrinojs/eslint eslint-config-react-app@^2.1.0 eslint-plugin-flowtype@^2.49.3 eslint-plugin-jsx-a11y@^5.1.1 eslint-plugin-react@^7.1.0 eslint-plugin-import@^2.6.0

Add this to .neutrinorc.js:

['@neutrinojs/eslint', {
    eslint: {
        plugins: ['import', 'flowtype', 'jsx-a11y', 'react'],
        rules: {
            semi: ['error', 'always'],
        },
        baseConfig: {extends: ['eslint-config-react-app']},
    }
}]

Install bootstrap

We’re going to use bootstrap as CSS framework for our application.

Install dependencies:

yarn add bootstrap

Then proceed to create src/index.global.scss (note the .global.scss extendsion, needed to skip it from being loaded as a module):

// Feel free to customize any variables up here

@import "~bootstrap/scss/bootstrap";

Then make sure it is imported from src/index.js:

import './index.global.scss';

Final tweaks to default files

I usually rename *.jsx files to *.js for personal preference.

You can also rename App.css to App.scss to benefit from the full power of SCSS.

Make sure you adjust your App.js accordingly!

import styles from './App.scss'

Also, remember to update your JSX code to use the imported class names, instead of string literals:

    // Old
    <div className="App"> ... </div>

    // New
    <div className={styles.App}> ... </div>

Create application UI

Now you can proceed creating the rest of the UI components as needed.

You can see the end result on GitHub at: https://github.com/rshk/yawc/tree/master/web

Next: creating the back-end server

In Day #2, we’ll discuss how to create our back-end service.


Samuele Santi

Written by Samuele Santi, who is using technology to make the world a better place. You can follow him on Twitter @_rshk