Easy and readable React/GraphQL/Node Stack — Part 3

Zac Tolley
Apr 27, 2019 · 11 min read

Kicking things off

npx create-react-app client
"workspaces": [
"server",
"client"
]

Dependencies

yarn add apollo-boost formik graphql history react-apollo react-apollo-hooks react-router-dom typescript yupyarn add @types/jest @types/node @types/react @types/react-dom @types/react-router-dom
yarn add -W graphql graphql-tag @graphql-codegen/add @graphql-codegen/cli @graphql-codegen/typescript @graphql-codegen/typescript-operations @graphql-codegen/typescript-react-apollo

Development Server

# server/package.json"scripts": {
"dev": "nodemon --inspect -e js,json,graphql src/index.js",
"start": "node src/index"
}
# client/package.json"proxy": "http://localhost:4000"
yarn add -W concurrently
# package.json
"scripts": {
"server": "yarn --cwd server dev",
"client": "yarn --cwd client start",
"dev": "concurrently \"yarn server\" \"yarn client\""
}

Typescript

# tsconfig.json{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "preserve",
"strictNullChecks": false
},
"include": ["src"]
}
# client/tsconfig.json{
"extends": "../tsconfig",
"include":["src"]
}

Routing and Homepage

# client/src/index.tsximport React from 'react'
import ReactDOM from 'react-dom'
import { BrowserRouter as Router } from 'react-router-dom'
import App from './App'import './index.css'ReactDOM.render(
<Router>
<App />
</Router>,
document.getElementById('root')
)
# client/src/App.tsximport React from 'react'
import { Route, Switch } from 'react-router-dom'
import Home from './pages/Home'const App = () => (
<Switch>
<Route exact path="/" component={Home} />
</Switch>
)
export default App# client/src/pages/Home/index.tsximport React from 'react'const Home = () => <h1>Todos</h1>export default Home
Image for post
Image for post

Getting data

Writing the GraphQL query

# client/src/graphql/queries/TodoList.graphqlquery TodoList {
todos {
id
title
complete
project {
title
}
}
}
# codegen.ymloverwrite: true
schema: 'server/src/graphql/schema.graphql'
documents: 'client/src/**/*.graphql'
generates:
client/src/graphql/index.tsx:
config:
withHooks: true
plugins:
- add: '/* eslint-disable /*'
- 'typescript-common'
- 'typescript-client'
- 'typescript-react-apollo'
# package.json
"scripts": {
"server": "yarn --cwd server dev",
"client": "yarn --cwd client start",
"dev": "concurrently \"yarn server\" \"yarn client\" \"yarn codegen:watch\"",
"codegen": "gql-gen --config codegen.yml",
"codegen:watch": "gql-gen --config codegen.yml --watch"
}

GraphQL and hooks

import React, { Fragment } from 'react'
import { useTodoListQuery } from '../../graphql'
const Home = (): JSX.Element => {
const { data, error, loading } = useTodoListQuery()
if (loading) return <p>Loading...</p>
if (error) return <p>Error: {error.message}</p>
if (!data || !data.todos) return <p>No data</p>
const { todos } = data return (
<Fragment>
<h1>Todos</h1>
<ul>
{todos.map(({ title, id }) => (
<li key={id}>{title}</li>
))}
</ul>
</Fragment>
)}
export default Home
# client/src/index.tsx
import ApolloClient from 'apollo-boost'import React from 'react'
import { ApolloProvider } from 'react-apollo-hooks'
import ReactDOM from 'react-dom'
import { BrowserRouter as Router } from 'react-router-dom'
import App from './App'
import './index.css'
const client = new ApolloClient({
uri: '/graphql',
})
ReactDOM.render(
<Router>
<ApolloProvider client={client}>
<App />
</ApolloProvider>
</Router>,
document.getElementById('root')
)
Image for post
Image for post

Summary

Scropt

Building online services

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store