Relay and GraphQL

Relay and GraphQL

Start implementing GraphQL and Relay.

Now we have everything in order, we can (re)start implementing GraphQLand Relay.

I highly advise you to watch the 4 first chapters from How To Graph QL - Basics and then some reading at Relay Documentation to understand some concepts of GraphQL and Relay.

Setting Up GraphQL

First we shall define our schema.graphql. This file is written in Schema Definition Language (SDL) and contains what GraphQL will look for.

It'll usually have 3 root types: QueryMutation and Subscription. If we set a CRUD (Create, Read, Update, Delete) style API, we'll have

  • Query: Reads
  • Mutation: Creates, Update, Delete
  • Subscription: Subscribes to these CRUD events

Besides root types, it'll also have some "object" types that will define your objects in the database.

In our case below, we're setting our schema.graphql with the Product type with a required (!) id and a title.

We're also setting a Query called "product" that needs an id and returns a Product type.

We can also set a "products" query that returns a list of Products

// packages/server/data/schema.graphql
// and a copy in packages/app/data/schema.graphql
type Product {
  id: ID!
  title: String
}

type Query { product(id: ID!): Product products: [Product] }

Now we have to write this schema as javascript so Koa (via koa-graphql) can use it as instructions (contract) to find data in our database.

You'll notice how some code is converted:

! as GraphQLNonNull

ID as GraphQLID

String as GraphQLString

an so on

// packages/server/graphql/productType.js
const graphql = require('graphql');
const globalIdField = require('graphql-relay').globalIdField;

const {GraphQLObjectType, GraphQLString} = graphql;

const ProductType = new GraphQLObjectType({ name: 'Product', fields: () => ({ id: globalIdField('products'), title: {type: GraphQLString}, }), });

module.exports = ProductType; // packages/server/graphql/schema.js const { GraphQLSchema, GraphQLObjectType, GraphQLID, GraphQLList, GraphQLNonNull, } = require('graphql'); const fromGlobalId = require('graphql-relay').fromGlobalId; const productGraphQLType = require('./productType'); const Product = require('../models/Product');

const Query = new GraphQLObjectType({ name: 'Query', fields: { product: { type: productGraphQLType, args: {id: {type: GraphQLNonNull(GraphQLID)}}, resolve(parent, args) { return Product.findById(fromGlobalId(args.id).id); }, }, products: { type: GraphQLList(productGraphQLType), resolve() { return Product.find().lean(); }, }, }, });

module.exports = new GraphQLSchema({ query: Query, });

You'll notice our Resolve Functions. They are functions that connects the schema to the database. Remember that the Product class imported from '../models/Product is created with Mongoose and that's how it accesses our MongoDB instance.

React Native

To require the data from React, we'll use babel-plugin-relay/macro to "translate" graphql into our request.

We'll also use a High Order Component called <QueryRenderer> to render our actual <App> with the data from Relay.

Query Renderer component will use the following props:

  • A configuration file Environment
  • The query
  • Variables used in the query
  • A render function that returns 3 cases: error, success and loading
// packages/app/src/App.js
import React, {Fragment} from 'react';
import {Text} from 'react-native';
import graphql from 'babel-plugin-relay/macro';
import {QueryRenderer} from 'react-relay';

import Environment from './relay/Environment';

const App = ({query}) => { const {products} = query;

return ( <Fragment> <Text>Hello World! Product: {products[0].title}</Text> </Fragment> ); };

const AppQR = () => { return ( <QueryRenderer environment={Environment} query={graphql query AppQuery { products { id title } } } variables={{}} render={({error, props}) => { console.log('qr: ', error, props); if (error) { return <Text>{error.toString()}</Text>; }

    if (props) {
      return &lt;App query={props} /&gt;;
    }

    return &lt;Text&gt;loading&lt;/Text&gt;;
  }}
/&gt;

); };

export default AppQR;

However to make babel-plugin-relay work, you'll need to create this scriptto generate a schema.json file that'll be read by a relay-compiler

// packages/server/scripts/updateSchema.js
#!/usr/bin/env babel-node --optional es7.asyncFunctions

const fs = require('fs'); const path = require('path'); const schema = require('../graphql/schema'); const graphql = require('graphql').graphql; const introspectionQuery = require('graphql/utilities').introspectionQuery; const printSchema = require('graphql/utilities').printSchema;

// Save JSON of full schema introspection for Babel Relay Plugin to use (async () => { const result = await graphql(schema, introspectionQuery); if (result.errors) { console.error( 'ERROR introspecting schema: ', JSON.stringify(result.errors, null, 2), ); } else { fs.writeFileSync( path.join(__dirname, '../data/schema.json'), JSON.stringify(result, null, 2), );

process.exit(0);

} })();

// Save user readable type system shorthand of schema fs.writeFileSync( path.join(__dirname, '../data/schema.graphql'), printSchema(schema), );

You'll need to change babel.config.js file as follows

// packages/app/babel.config.js
module.exports = {
  presets: ['module:metro-react-native-babel-preset'],
  plugins: ['macros'], // add this
};

And you'll also need to run this updateSchema.js everytime you change your schema by using yarn update-schema

// packages/server/package.json
...
  "scripts": {
    "start": "nodemon server.js",
    "update-schema": "babel-node --extensions ".es6,.js,.es,.jsx,.mjs,.ts" ./scripts/updateSchema.js",
    "test": "jest"
  },
...
// package.json
...
"scripts: {
   ...
   "update-schema": "yarn --cwd packages/server update-schema",
   ...
   },
...

Relay

The Enviroment configuration shall be done as the following:

// packages/app/src/relay/Environment.js
import {Environment, Network, RecordSource, Store} from 'relay-runtime';

import fetchQuery from './fetchQuery';

const network = Network.create(fetchQuery);

const source = new RecordSource(); const store = new Store(source);

const env = new Environment({ network, store, });

export default env; // packages/app/src/relay/fetchQuery.js import {Variables, UploadableMap} from 'react-relay'; import {RequestNode} from 'relay-runtime';

export const GRAPHQL_URL = 'http://localhost:3000/graphql';

// Define a function that fetches the results of a request (query/mutation/etc) // and returns its results as a Promise: const fetchQuery = async (request, variables) => { const body = JSON.stringify({ name: request.name, // used by graphql mock on tests query: request.text, // GraphQL text from input variables, }); const headers = { Accept: 'application/json', 'Content-type': 'application/json', };

const response = await fetch(GRAPHQL_URL, { method: 'POST', headers, body, });

return await response.json(); };

export default fetchQuery;

You'll also have to configure relay-compiler by adding and running yarn relay

"scripts": {
  "relay": "relay-compiler --src ./src --schema ./schema.graphql"
}

KoaJS

Finally, to serve our GraphQL server into a single endpoint, we'll use koa-mount and koa-graphql using our schema.js

// packages/server/server.js
const Koa = require('koa');
const mount = require('koa-mount');
const graphqlHTTP = require('koa-graphql');
const schema = require('./graphql/schema');

const databaseUrl = "mongodb://127.0.0.1:27017/test"; mongoose.connect(databaseUrl, { useNewUrlParser: true }); mongoose.connection.once("open", () => { console.log(Connected to database: ${databaseUrl}); });

const app = new Koa();

app.use( mount( '/graphql', graphqlHTTP({ schema: schema, graphiql: true, }), ), );

app.listen(3000, () => console.log("Server is running on http://localhost:3000/") );

Running

You'll need to install all dependencies first.

  • Inside app package:

yarn add react-relay

yarn add --dev graphql graphql-compiler relay-compiler relay-runtime babel-plugin-relay

  • Inside server package:

yarn add graphql koa-mount koa-graphql graphql-relay graphql-compiler

yarn add --dev @babel/core @babel/node

And run our set scripts:

yarn relay

yarn update-schema

Then you might run some yarn commands that were set in last post.

yarn start:server (don't forget to sudo service mongod start)

yarn start:app

yarn android

If you get Network error with server and mongodb running correctly, you'll need to redirect some ports with adb reverse tcp:<portnumber> tcp: <portnumber>

You may want to add the following script in packages/app/scripts/redirectPorts.sh and "redirect": "sh ./packages/app/scripts/redirectPorts.sh" in the root package.json to make things easier with a yarn redirect

adb reverse tcp:8081 tcp:8081
adb reverse tcp:3000 tcp:3000
adb reverse tcp:5002 tcp:5002

adb -d reverse tcp:8081 tcp:8081 adb -d reverse tcp:3000 tcp:3000 adb -d reverse tcp:5002 tcp:5002

adb -e reverse tcp:8081 tcp:8081 adb -e reverse tcp:3000 tcp:3000 adb -e reverse tcp:5002 tcp:5002

That's it. You should be seeing "Stampler" in your view.

Thanks for reading. If you liked this post, share it with all of your programming buddies!

Further reading

☞ A Beginner’s Guide to GraphQL

☞ GraphQL with React: The Complete Developers Guide

☞ How to create a simple CRUD App using GraphQL and Node.js

☞ Node, Express, PostgreSQL, Vue 2 and GraphQL CRUD Web App

☞ Developing and Securing GraphQL APIs with Laravel

☞ Building the New facebook.com with React, GraphQL and Relay

graphql react-native node-js web-development

Bootstrap 5 Complete Course with Examples

Bootstrap 5 Tutorial - Bootstrap 5 Crash Course for Beginners

Nest.JS Tutorial for Beginners

Hello Vue 3: A First Look at Vue 3 and the Composition API

Building a simple Applications with Vue 3

Deno Crash Course: Explore Deno and Create a full REST API with Deno

How to Build a Real-time Chat App with Deno and WebSockets

Convert HTML to Markdown Online

HTML entity encoder decoder Online

How native is React Native? | React Native vs Native App Development

Article covers: How native is react native?, React Native vs (Ionic, Cordova), Similarities and difference between React Native and Native App Development.

Hire Dedicated React Native Developer

Have you ever thought of having your own app that runs smoothly over multiple platforms? React Native is an open-source cross-platform mobile application framework which is a great option to create mobile apps for both Android and iOS. **[Hire...

Hire Dedicated React Native Developer in India | React Native Development

Hire dedicated React Native developers for your next project. As the top react native development company we offer you the best service as per your business needs.

Hire Node.JS Developers | Skenix Infotech

We are providing robust Node.JS Development Services with expert Node.js Developers. Get affordable Node.JS Web Development services from Skenix Infotech.

Hire React Js Developer from Expert React JS Development Company

Are you looking to [hire React JS developer](https://www.mobiwebtech.com/react-js-development-company/ "hire React JS developer") from a prestigious and reliable React JS development company? Visit at Mobiweb Technologies here we have a big team...