How to disable cache in apollo-link or apollo-client?

I'm using&nbsp;<em>apollo-client</em>,&nbsp;<em>apollo-link</em>&nbsp;and&nbsp;<em>react-apollo</em>, I want to fully disable cache, but don't know how to do it.

I'm using apollo-clientapollo-link and react-apollo, I want to fully disable cache, but don't know how to do it.

I read the source of apollo-cache-inmemory, it has a config argument in its constructor, but I can't build a dummy storeFactory to make it works.

File Upload with Vue, Apollo Client and GraphQL

File Upload with Vue, Apollo Client and GraphQL

In this tutorial, I'll be showing you how to hand file uploads in GraphQL by building a fullstack app.

The tutorial will be divided into two main sections: building the GraphQL API and the frontend app. The GraphQL API will be built using Apollo Server and the frontend app will be built with Vue.js and Vue Apollo.

Table of Contents

  • Prerequisites
  • What we'll be building
  • Getting your Cloudinary keys
  • Building the GraphQL server
  • Building the frontend app
  • Fetching all photos
  • Uploading photo
  • Conclusion
Prerequisites

This tutorial assumes you conformatable with GraphQL and using it in a Vue app.

What we'll be building

For the purpose of this tutorial, we'll be building a simple photo album app, where users will be able to upload as well as view their photos. All the photos will be uploaded directly to Cloudinary. Below is a quick demo of the final app:

File Upload with Vue, Apollo Client and GraphQL

Getting your Cloudinary keys

Before we dive into code, let’s make sure we have our Cloudinary key in place. If you don’t already have an account with them, you can signup for free. Otherwise, login to your dashboard where you would see your account details along with your keys.

File Upload with Vue, Apollo Client and GraphQL

Building the GraphQL server

Now, let's start building the GraphQL server. First, let's create our project directory:

$ mkdir graphql-vue-photo-upload && cd graphql-vue-photo-upload
$ mkdir server && cd server
$ npm init -y

All the GraphQL API related code will be inside the server directory. Next, let’s install the necessary dependencies for our GraphQL server:

$ npm install graphql apollo-server cloudinary dotenv

Addition to installing Apollo Server and it dependence, we also install the Cloudinary Node.js library and a package to read environment variables.

Once that’s done installing, we can start building the GraphQL server. Create a new src directory and create a new index.js file inside it, then add the following code in it:

// server/src/index.js

const { ApolloServer } = require('apollo-server')
require('dotenv').config()
const typeDefs = require('./schema')
const resolvers = require('./resolvers')

const server = new ApolloServer({
  typeDefs,
  resolvers
})

server.listen().then(({ url }) => console.log(`Server ready at ${url}`))

Next, we need to create the schema and resolvers which our GraphQL server references. We’ll start with creating the schema. Create a schema.js inside src directory and paste the following code into it:

// server/src/schema.js

const { gql } = require('apollo-server')

const typeDefs = gql`type Photo {
    filename: String!
    path: String!
  }

  type Query {
    allPhotos: [Photo]
  }

  type Mutation {
    uploadPhoto(photo: Upload!): Photo!
  }`

module.exports = typeDefs

Here, we define a Photo type that comprises of two fields: the filename of the photo and the path to the actual photo. Then we define a single query allPhotos for fetching all uploaded photos. Lastly, we have a mutation for uploading a photo. The uploadPhoto mutation accepts a single argument, which is the photo that is to be uploaded. The argument is of the scalar type Upload, which is made available to us my Apollo Server, since it has built-in support of file upload. The mutation will return the uploaded photo.

The next, thing to do is create the resolvers. Still inside src directory, create a resolvers.js and add the code below in it:

// server/src/resolvers.js

const cloudinary = require('cloudinary').v2

cloudinary.config({
  cloud_name: process.env.CLOUD_NAME,
  api_key: process.env.API_KEY,
  api_secret: process.env.API_SECRET
})

const photos = []

const resolvers = {
  Query: {
    allPhotos () {
      return photos
    }
  },
  Mutation: {
    async uploadPhoto (parent, { photo }) {
      const { filename, createReadStream } = await photo

      try {
        const result = await new Promise((resolve, reject) => {
          createReadStream().pipe(
            cloudinary.uploader.upload_stream((error, result) => {
              if (error) {
                reject(error)
              }

              resolve(result)
            })
          )
        })

        const newPhoto = { filename, path: result.secure_url }

        photos.push(newPhoto)

        return newPhoto
      } catch (err) {
        console.log(err)
      }
    }
  }
}

module.exports = resolvers

First, we pull in the Cloudinary library and configured it will our credentials, which are getting from the environment variables. Then we create an empty array, which will hold our photos. Next, we define the resolver for the allPhotos query, which simply returns the photos array.

For the uploadPhoto mutation, Apollo Server would return the selected file as Promise, which contains a bunch of details about the file, such as: createReadStream, filename, mimetype and encoding. In this tutorial, we’ll only be making use of the first two, so we extract them from the object. Using createReadStream, we stream the file directly to Cloudinary. Since it is an asynchronous operation we wrap it in a Promise and awaits it. If the Promise was resolved, that is, the file was uploaded successfully to Cloudinary, we create a new object containing the uploaded file name and the Cloudinary path to the file. Then we push the new object to the photos array and finally return the new object.

Lastly, if there was an error uploading the file to Cloudinary, we simply console log the erorr.

Before we wrap up the GraphQL API, let’s quickly add our environment variables. Create a .env file directly in the server directory and add the code below in it:

// server/.env

CLOUD_NAME=YOUR_CLOUD_NAME
API_KEY=YOUR_API_KEY
API_SECRET=YOUR_API_SECRET

Remember to replace the placeholders with your actual account details.

Finally, let’s start the server:

$ node src/index.js

The server should be running on [[http://localhost:4000](http://localhost:4000)](http://localhost:4000](http://localhost:4000))

Building the frontend app

As already mentioned, the frontend app will be built with Vue.js, so let’s create a new Vue.js app using the Vue CLI:

$ vue create client

When prompted, press enter to select the default preset. Start the app and leave it running while we build on it:

$ cd client
$ yarn serve

The app should be running on http://localhost:8080

Once the Vue app is created, let’s install the necessary dependencies:

$ npm install vue-apollo graphql-tag graphql apollo-cache-inmemory apollo-client apollo-upload-client

Out of these dependencies, the one that is new and that I’d like to point out is [apollo-upload-client]([https://github.com/jaydenseric/apollo-upload-client)](https://github.com/jaydenseric/apollo-upload-client)). It’s a package for Apollo Client that allows us to send GraphQL multipart requests. It will be used in place of apollo-link.

Next, let’s configure Vue Apollo and these dependencies. Update main.js as below:

// client/src/main.js

import { InMemoryCache } from 'apollo-cache-inmemory'
import { ApolloClient } from 'apollo-client'
import { createUploadLink } from 'apollo-upload-client'
import Vue from 'vue'
import VueApollo from 'vue-apollo'
import App from './App.vue'

Vue.config.productionTip = false

Vue.use(VueApollo)

const apolloClient = new ApolloClient({
  link: createUploadLink({ uri: 'http://localhost:4000' }),
  cache: new InMemoryCache()
})

const apolloProvider = new VueApollo({
  defaultClient: apolloClient
})

new Vue({
  apolloProvider,
  render: h => h(App)
}).$mount('#app')

You’ll notice here we are using createUploadLink from apollo-upload-client to create the ApolloClient link, passing to it our GraphQL API endpoint.

To give our app a bit of styling, we’ll be pulling in UIKit. Add the line below to head section of index.html:





Fetching all photos

We’ll start with the GraphQL query for fetching all our photo. Create a graphql directory inside the client/src directory and with in, create a AllPhotos.js file and paste the code below into it:

// client/src/graphql/AllPhotos.js

import gql from 'graphql-tag'

export default gql`query allPhotos {
    allPhotos {
      filename
      path
    }
  }`

For the sake of simplicity, we’ll be making use of just the App.vue component. So let’s update it as below:

// client/src/App.vue


  
    
      ## Photo Album


      
        
          
            
              ![](photo.path)
            
            {{ photo.filename }}

          
        
      
    
  



Within the apollo object, we add the ALL_PHOTOS query to fetch all photos and save it in a allPhotos. Once allPhotos is populated with data from our GraphQL API, we display the photos by looping through them.

If we view our app, we should get something similar to below:
File Upload with Vue, Apollo Client and GraphQL

Uploading photo

Of course, we need to have uploaded some photos before we can see them. Let’s implement that now. Still inside the graphql directory, create a UploadPhoto.js and paste the code below into it:

// client/src/graphql/UploadPhoto.js

import gql from 'graphql-tag'

export default gql`mutation uploadPhoto($photo: Upload!) {
    uploadPhoto(photo: $photo) {
      filename
      path
    }
  }`

Next, add the snippet below to the template section of App.vue, just below the Photo Album heading:

// client/src/App.vue


  

Here, we have a file input field that accepts only images. On change of the input field a uploadPhoto method is triggered.

In the script section, add:

// client/src/App.vue

import UPLOAD_PHOTO from "./graphql/UploadPhoto";

methods: {
  async uploadPhoto({ target }) {
    await this.$apollo.mutate({
      mutation: UPLOAD_PHOTO,
      variables: {
        photo: target.files[0]
      },
      update: (store, { data: { uploadPhoto } }) => {
        const data = store.readQuery({ query: ALL_PHOTOS });

        data.allPhotos.push(uploadPhoto);

        store.writeQuery({ query: ALL_PHOTOS, data });
      }
    });
  }
}

We get extract the target from the input event, then call the mutate method, passing to it the UPLOAD_PHOTO mutation as well as the required argument (through the variables object). We get the selected file from the files on the target object. Once the mutation is executed, we update the cache by adding the newly uploaded photo to the allPhotos array.
File Upload with Vue, Apollo Client and GraphQL

File Upload with Vue, Apollo Client and GraphQL

Conclusion

So in this tutorial, we have seen how to handle file uploads in GraphQL using Apollo Server on the server side and Vue and Vue Apollo on the client side. Though we used Cloudinary to store our photos, you can easily wrap it for any other cloud storage service or you can even save directly to your own local filesystem.

Build CRUD Mobile App using React Native, Elements, Navigation, Apollo Client and GraphQL

Build CRUD Mobile App using React Native, Elements, Navigation, Apollo Client and GraphQL

A comprehensive step by step tutorial on learn to build CRUD (Create, Read, Update, Delete) using React Native, Elements, Navigation, Apollo Client and GraphQL

A comprehensive step by step tutorial on learn to build CRUD (Create, Read, Update, Delete) using React Native, Elements, Navigation, Apollo Client and GraphQL

We will use existing Node.js, Express.js, MongoDB, and GraphQL that already created in React.js tutorial. So, we will focus on accessing GraphQL from the React Native Mobile Apps. Same exactly as React.js tutorial, we will use Apollo Client GraphQL to integrating with React Native.

The flow of CRUD Mobile Apps is very basic. It just the main page that shows the list of data, the details page, the edit, and the create data page. In this tutorial, we will use React Native and React Native Elements components including ScrollView, TextInput, Text, Button, View, etc. The list page contains a list of the book with the details button and a button to add new data on the right corner of the header. The details page contains data details text, the back button on the left corner of the header, an edit button and a delete button. The add-data page contains a React Native form, back button, and save button. The edit-data page contains the React Native form, back data, and update button. Simply as that, we will try to build this React Native Mobile Apps to Android and iOS devices.

The Example of React Native Navigation and Elements wrap together with Apollo Client GraphQL

The following tools, frameworks, and modules are required for this tutorial:

Before start to the main steps, make sure that you have installed Node.js and can run npm in the terminal or command line. To check the existing or installed Node.js environment open the terminal/command line then type this command.

node -v
v10.15.1
npm -v
6.9.0
yarn -v
1.10.1

React Native and Apollo GraphQL Tutorial: Install create-react-native-app and Create App

The create-react-native-app is a tool for creating a React Native App. To install it, type this command in your App projects folder.

sudo npm install -g create-react-native-app

Then create a React Native App using this create-react-native-app command.

create-react-native-app RnGraphql

If there question to install Expo, just type Y and choose a blank template because we will add the navigation later. That command will create a React Native app then install all required modules. The app or project folder will contain these folders and files.

|-- App.js
|-- app.json
|-- assets
| |-- icon.png
| `-- splash.png
|-- node_modules
`-- package.json

Next, go to the newly created React App folder.

cd RnGraphql

This React Native App is running via Expo app, before running on your Android or iOS device, make sure you have to install the Expo App to Android or Expo Client to iOS. Of course, that app is available in the App Store. So, we assume that you have installed the Expo App in your device then type this command to run the app.

npm start
or
yarn start

You will see this barcode and instruction in the terminal or command line.

To open the app in the Android device, open the Expo App first then tap on Scan QR Code button. Scan the barcode in the terminal or command line then you will see the React Native Android App like this after waiting for minutes the build process.

For iOS Device, press s from the keyboard to send React Native App URL to your Email or SMS. Enter your phone number or Email address (We use an email address) then press Enter. You will get this email to your mailbox.

Choose open in Expo URL then open in your browser, that will be redirected to Expo App. In Expo App welcome screen, shake your phone to open the React Native App. Now, you will see this screen in your iOS device.

Make sure you have the same network between your device and your development computer.

React Native and Apollo GraphQL Tutorial: Add Navigation Header and required Screen

As you see, the React Native app that you can only show plain text which unlike the standard Android or iOS Mobile Apps. To make a standard Android or iOS Mobile Apps, we need to add React Native header and navigation button with an icon to the React Native view. Type this command to install React Native Navigation (react-navigation).

yarn add react-navigation

Also, add the React Native Gesture Handler module by type this command.

yarn add react-native-gesture-handler

We have to link react-native to react-native-gesture-handler.

react-native link react-native-gesture-handler

Next, create a folder for components and components files in the root of the app folder by type these commands.

mkdir components
touch components/BooksScreen.js
touch components/BookDetailScreen.js
touch components/AddBookScreen.js
touch components/EditBookScreen.js

We will fill that files with simple React Native Button, Text, View, props, and navigation. Open and edit components/BooksScreen.js then add this React codes.

import React, { Component } from 'react';
import { Button, View, Text } from 'react-native';

class BooksScreen extends Component {
 static navigationOptions = {
  title: 'Books List',
 };
 render() {
  return (
   <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
    <Text>Books List</Text>
    <Button
     title="Go to Details"
     onPress={() => this.props.navigation.navigate('BookDetails')}
    />
    <Button
     title="Go to Add Book"
     onPress={() => this.props.navigation.navigate('AddBook')}
    />
    <Button
     title="Go to Edit Book"
     onPress={() => this.props.navigation.navigate('EditBook')}
    />
   </View>
  );
 }
}

export default BooksScreen;

Open and edit components/BookDetailScreen.js then add this React codes.

import React, { Component } from 'react';
import { Button, View, Text } from 'react-native';

class BookDetailScreen extends Component {
 static navigationOptions = {
  title: 'Book Details',
 };
 render() {
  return (
   <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
    <Text>Book Details</Text>
    <Button
     title="Go to Details... again"
     onPress={() => this.props.navigation.push('BookDetails')}
    />
    <Button
     title="Go to Home"
     onPress={() => this.props.navigation.navigate('Book')}
    />
    <Button
     title="Go back"
     onPress={() => this.props.navigation.goBack()}
    />
   </View>
  );
 }
}

export default BookDetailScreen;

Open and edit components/AddBookScreen.js then add this React codes.

import React, { Component } from 'react';
import { Button, View, Text } from 'react-native';

class AddBookScreen extends Component {
 static navigationOptions = {
  title: 'Add Book',
 };
 render() {
  return (
   <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
    <Text>Add Book</Text>
    <Button
     title="Go to Add Book... again"
     onPress={() => this.props.navigation.push('AddBook')}
    />
    <Button
     title="Go to Home"
     onPress={() => this.props.navigation.navigate('Book')}
    />
    <Button
     title="Go back"
     onPress={() => this.props.navigation.goBack()}
    />
   </View>
  );
 }
}

export default AddBookScreen;

Open and edit components/EditBookScreen.js then add this React codes.

import React, { Component } from 'react';
import { Button, View, Text } from 'react-native';

class EditBookScreen extends Component {
 static navigationOptions = {
  title: 'Edit Book',
 };
 render() {
  return (
   <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
    <Text>Add Book</Text>
    <Button
     title="Go to Edit Book... again"
     onPress={() => this.props.navigation.push('EditBook')}
    />
    <Button
     title="Go to Home"
     onPress={() => this.props.navigation.navigate('Book')}
    />
    <Button
     title="Go back"
     onPress={() => this.props.navigation.goBack()}
    />
   </View>
  );
 }
}

export default EditBookScreen;

Next, open and edit App.js then add replace all codes with this.

import { createStackNavigator, createAppContainer } from 'react-navigation';
import BooksScreen from './components/BooksScreen';
import BookDetailScreen from './components/BookDetailScreen';
import AddBookScreen from './components/AddBookScreen';
import EditBookScreen from './components/EditBookScreen';

const MainNavigator = createStackNavigator({
 Book: { screen: BooksScreen },
 BookDetails: { screen: BookDetailScreen },
 AddBook: { screen: AddBookScreen },
 EditBook: { screen: EditBookScreen },
});

const App = createAppContainer(MainNavigator);

export default App;

Next, run again the React Native app then refresh your Expo app. You will see the standard Android or iOS Mobile Apps in your device screen.

React Native and Apollo GraphQL Tutorial: Install and Configure Apollo Client GraphQL

Now, we have to install and configure all of the required modules and dependencies to the **React Apollo **and GraphQL. Type this command to install the modules at once.

yarn add react-apollo apollo-client apollo-cache-inmemory apollo-link-http graphql-tag graphql

Next, open and edit App.js then replace all imports with these imports.

import React from 'react';
import { AppRegistry } from 'react-native';
import { createStackNavigator, createAppContainer } from 'react-navigation';
import BooksScreen from './components/BooksScreen';
import BookDetailScreen from './components/BookDetailScreen';
import AddBookScreen from './components/AddBookScreen';
import EditBookScreen from './components/EditBookScreen';
import { ApolloClient } from 'apollo-client';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { HttpLink } from 'apollo-link-http';
import { ApolloProvider } from 'react-apollo';

Change the constant name of createAppContainer constant variable.

const MyRootComponent = createAppContainer(MainNavigator);

Add these lines of Apollo Client constant variables.

const cache = new InMemoryCache();
const client = new ApolloClient({
 cache,
 link: new HttpLink({
    uri: 'http://localhost:3000/graphql',
 }),
});

const App = () => (
 <ApolloProvider client={client}>
  <MyRootComponent />
 </ApolloProvider>
);

Register the Apollo Provider by adding this line before the export line.

AppRegistry.registerComponent('MyApp', () => App);

React Native and Apollo GraphQL Tutorial: Show List of Books

To show the list of the books in the Books components, open and edit components/BooksScreen.js then add/replace these imports.

import React, { Component } from 'react';
import { StyleSheet, FlatList, ActivityIndicator, View, Text } from 'react-native';
import { ListItem, Button } from 'react-native-elements';
import gql from 'graphql-tag';
import { Query } from 'react-apollo';

As you can see, there are view elements that use react-native-elements module. For that, install the react-native-elements module first by type this command.

yarn add react-native-elements

Declare a constant before the class name for the query.

const GET_BOOKS = gql`
 {
  books {
   _id
   title
   author
  }
 }
`;

Replace navigationOptions with this.

static navigationOptions = ({ navigation }) => {
  return {
    title: 'LIST OF BOOKS',
    headerRight: (
    <Button
      buttonStyle={{ padding: 0, backgroundColor: 'transparent' }}
      icon={{ name: 'add-circle', style: { marginRight: 0, fontSize: 28 } }}
      onPress={() => { navigation.push('AddBook') }}
    />
    ),
  };
};

Add the function to extract and mapping result from GraphQL and render list item from the FlatList that will be added to the render section.

keyExtractor = (item, index) => index.toString()

renderItem = ({ item }) => (
  <ListItem
    title={item.title}
    onPress={() => {
      this.props.navigation.navigate('BookDetails', {
        id: `${item._id}`,
      });
    }}
    chevron
    bottomDivider
  />
)

Replace all React Native view render with Apollo Client GraphQL Query with pollInterval to make the page always request GraphQL data.

render() {
  return (
    <Query pollInterval={500} query={GET_BOOKS}>
      {({ loading, error, data }) => {
        if (loading) return(
          <View style={styles.activity}>
            <ActivityIndicator size="large" color="#0000ff"/>
          </View>
        );
        if (error) return(
          <View style={styles.activity}>
            <Text>`Error! ${error.message}`</Text>
          </View>
        );
        return (
          <FlatList
            keyExtractor={this.keyExtractor}
            data={data.books}
            renderItem={this.renderItem}
          />
        );
      }}
    </Query>
  );
}

Add React Native styles constant variables before the export code.

const styles = StyleSheet.create({
 container: {
   flex: 1,
   paddingBottom: 22
 },
 item: {
   padding: 10,
   fontSize: 18,
   height: 44,
 },
 activity: {
   position: 'absolute',
   left: 0,
   right: 0,
   top: 0,
   bottom: 0,
   alignItems: 'center',
   justifyContent: 'center'
 }
})

React Native and Apollo GraphQL Tutorial: Show a Book Details

To show book details, we have to modify components/BookDetailScreen.js then replace all imports with these imports.

import React, { Component } from 'react';
import { ScrollView, StyleSheet, ActivityIndicator, View, Text } from 'react-native';
import { Card, Button } from 'react-native-elements';
import gql from 'graphql-tag';
import { Query, Mutation } from 'react-apollo';

Declare the constant variables of the GraphQL gql or Queries.

const GET_BOOK = gql`
  query book($bookId: String) {
    book(id: $bookId) {
      _id
      isbn
      title
      author
      description
      published_year
      publisher
      updated_date
    }
  }
`;

const DELETE_BOOK = gql`
 mutation removeBook($id: String!) {
  removeBook(id:$id) {
   _id
  }
 }
`;

Modify the render section of the class to display a book details by implementing Apollo GraphQL Query and React Native ScrollView, Card, View, Button, Text, ActivityIndicator, etc.

class BookDetailScreen extends Component {
 static navigationOptions = {
  title: 'Book Details',
 };
 render() {
  const { navigation } = this.props;
  return (
    <Query pollInterval={500} query={GET_BOOK} variables={{ bookId: navigation.getParam('id') }}>
      {({ loading, error, data }) => {
        if (loading) return(<View style={styles.activity}>
          <ActivityIndicator size="large" color="#0000ff" />
         </View>);
        if (error) return(<Text>`Error! ${error.message}`</Text>);
        return (
          <ScrollView>
            <Card style={styles.container}>
              <View style={styles.subContainer}>
                <View>
                  <Text style={{fontSize: 16, fontWeight: 'bold'}}>ISBN:</Text>
                  <Text style={{fontSize: 18, marginBottom: 10}}>{data.book.isbn}</Text>
                </View>
                <View>
                  <Text style={{fontSize: 16, fontWeight: 'bold'}}>Title: </Text>
                  <Text style={{fontSize: 18, marginBottom: 10}}>{data.book.title}</Text>
                </View>
                <View>
                  <Text style={{fontSize: 16, fontWeight: 'bold'}}>Author: </Text>
                  <Text style={{fontSize: 18, marginBottom: 10}}>{data.book.author}</Text>
                </View>
                <View>
                  <Text style={{fontSize: 16, fontWeight: 'bold'}}>Description: </Text>
                  <Text style={{fontSize: 18, marginBottom: 10}}>{data.book.description}</Text>
                </View>
                <View>
                  <Text style={{fontSize: 16, fontWeight: 'bold'}}>Published Year: </Text>
                  <Text style={{fontSize: 18, marginBottom: 10}}>{data.book.published_year}</Text>
                </View>
                <View>
                  <Text style={{fontSize: 16, fontWeight: 'bold'}}>Publisher: </Text>
                  <Text style={{fontSize: 18, marginBottom: 10}}>{data.book.publisher}</Text>
                </View>
                <View>
                  <Text style={{fontSize: 16, fontWeight: 'bold'}}>Updated Date: </Text>
                  <Text style={{fontSize: 18}}>{data.book.updated_date}</Text>
                </View>
              </View>
              <Mutation mutation={DELETE_BOOK} key={data.book._id} onCompleted={() => navigation.goBack()}>
                {(removeBook, { loading2, error2 }) => (
                  <View style={styles.subContainer}>
                    <Button
                    style={styles.detailButton}
                    large
                    backgroundColor={'#CCCCCC'}
                    leftIcon={{name: 'edit'}}
                    title='Edit'
                    onPress={() => {
                      navigation.navigate('EditBook', { id: `${data.book._id}`, });
                    }} />
                    <Button
                    style={styles.detailButton}
                    large
                    backgroundColor={'#999999'}
                    color={'#FFFFFF'}
                    leftIcon={{name: 'delete'}}
                    title='Delete'
                    onPress={() => {
                      removeBook({ variables: { id: data.book._id } })
                      .then(res => res)
                      .catch(err => <Text>{err}</Text>);
                    }} />
                    {loading2 && <View style={styles.activity}>
                      <ActivityIndicator size="large" color="#0000ff" />
                     </View>}
                    {error2 && <Text>`Error! ${error2.message}`</Text>}
                  </View>
                )}
              </Mutation>
            </Card>
          </ScrollView>
        );
      }}
    </Query>
  );
 }
}

Modify or add the React Native style to match the modified details page.

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 20
  },
  subContainer: {
    flex: 1,
    paddingBottom: 20,
    borderBottomWidth: 2,
    borderBottomColor: '#CCCCCC',
  },
  activity: {
    position: 'absolute',
    left: 0,
    right: 0,
    top: 0,
    bottom: 0,
    alignItems: 'center',
    justifyContent: 'center'
  },
  detailButton: {
    marginTop: 10
  }
})

React Native and Apollo GraphQL Tutorial: Add a New Book

To add a new book, we have an add button in the List of Book page that will push the Add Page. This Add Page will use Apollo GraphQL Mutation instead of React Form. The React Native TextInput change will use a function that triggers the OnChange event. Open and edit components/AddBookScreen.js then replace all imports with these imports.

import React, { Component } from 'react';
import { StyleSheet, ScrollView, ActivityIndicator, View, TextInput, Text } from 'react-native';
import { Button } from 'react-native-elements';
import gql from "graphql-tag";
import { Mutation } from "react-apollo";

Declare a constant variable of the Apollo GraphQL gql or Query before the class name.

const ADD_BOOK = gql`
  mutation AddBook(
    $isbn: String!,
    $title: String!,
    $author: String!,
    $description: String!,
    $publisher: String!,
    $published_year: Int!) {
    addBook(
      isbn: $isbn,
      title: $title,
      author: $author,
      description: $description,
      publisher: $publisher,
      published_year: $published_year) {
      _id
    }
  }
`;

Add an object of the state after the navigationOptions that consists of required fields that match the GraphQL data.

state = {
 isbn: '',
 title: '',
 author: '',
 description: '',
 published_year: '',
 publisher: '',
}

Add a function to update the React Native TextInput value and state object values.

updateTextInput = (text, field) => {
 const state = this.state
 state[field] = text;
 this.setState(state);
}

Modify the React Native render section to add Apollo GraphQL Mutation, React Native TextInput, ScrollView, View, ActivityIndicator, etc.

render() {
  const { isbn, title, author, description, published_year, publisher } = this.state;
  return (
   <Mutation mutation={ADD_BOOK} onCompleted={() => this.props.navigation.goBack()}>
     {(addBook, { loading, error }) => (
      <ScrollView style={styles.container}>
       <View style={styles.subContainer}>
        <TextInput
          style={styles.textInput}
          placeholder={'ISBN'}
          value={this.state.isbn}
          onChangeText={(text) => this.updateTextInput(text, 'isbn')}
        />
       </View>
       <View style={styles.subContainer}>
        <TextInput
          style={styles.textInput}
          placeholder={'Title'}
          value={this.state.title}
          onChangeText={(text) => this.updateTextInput(text, 'title')}
        />
       </View>
       <View style={styles.subContainer}>
        <TextInput
          style={styles.textInput}
          placeholder={'Author'}
          value={this.state.author}
          onChangeText={(text) => this.updateTextInput(text, 'author')}
        />
       </View>
       <View style={styles.subContainer}>
        <TextInput
          style={styles.textInput}
          placeholder={'Description'}
          multiline={true}
          numberOfLines={4}
          value={this.state.description}
          onChangeText={(text) => this.updateTextInput(text, 'description')}
        />
       </View>
       <View style={styles.subContainer}>
        <TextInput
          style={styles.textInput}
          placeholder={'Published Year'}
          value={this.state.published_year}
          keyboardType='numeric'
          onChangeText={(text) => this.updateTextInput(text, 'published_year')}
        />
       </View>
       <View style={styles.subContainer}>
        <TextInput
          style={styles.textInput}
          placeholder={'Publisher'}
          value={this.state.publisher}
          onChangeText={(text) => this.updateTextInput(text, 'publisher')}
        />
       </View>
       <View>
        <Button
         large
         leftIcon={{name: 'save'}}
         title='Save'
         onPress={() => {
          addBook({
           variables: {
            isbn: this.state.isbn,
            title: this.state.title,
            author: this.state.author,
            description: this.state.description,
            publisher: this.state.publisher,
            published_year: parseInt(this.state.published_year),
           }
          })
           .then(res => this.setState({ isbn: '', title: '', author: '', description: '', published_year: '', publisher }))
           .catch(err => <Text>{err}</Text>);
         }} />
       </View>
       {loading && <View style={styles.activity}>
         <ActivityIndicator size="large" color="#0000ff" />
        </View>}
       {error && <Text>`Error! ${error.message}`</Text>}
      </ScrollView>
     )}
    </Mutation>
  );
 }
}

Also, modify the React Native style to match the required views.

const styles = StyleSheet.create({
 container: {
  flex: 1,
  padding: 20
 },
 subContainer: {
  flex: 1,
  marginBottom: 20,
  padding: 5,
  borderBottomWidth: 2,
  borderBottomColor: '#CCCCCC',
 },
 activity: {
  position: 'absolute',
  left: 0,
  right: 0,
  top: 0,
  bottom: 0,
  alignItems: 'center',
  justifyContent: 'center'
 },
 textInput: {
  fontSize: 18,
  margin: 5,
 },
})

React Native and Apollo GraphQL Tutorial: Edit a Book

The Edit Book Screen pushed from the Book Details Screen. Same as the previous Add Book Page, this page will contain the Apollo GraphQL Mutation and React Native TextInput with additional Apollo GraphQL Query. All React Native TextInput will fill with the data from GraphQL Query. Open and edit components/EditBookScreen.js then replace all imports with these imports.

import React, { Component } from 'react';
import { StyleSheet, ScrollView, ActivityIndicator, View, TextInput, Text } from 'react-native';
import { Button } from 'react-native-elements';
import gql from "graphql-tag";
import { Query, Mutation } from "react-apollo";

Declare the constant variables of the Apollo GraphQL Query or gql after the imports.

const GET_BOOK = gql`
  query book($bookId: String) {
    book(id: $bookId) {
      _id
      isbn
      title
      author
      description
      published_year
      publisher
      updated_date
    }
  }
`;

const UPDATE_BOOK = gql`
  mutation updateBook(
    $id: String!,
    $isbn: String!,
    $title: String!,
    $author: String!,
    $description: String!,
    $publisher: String!,
    $published_year: Int!) {
    updateBook(
    id: $id,
    isbn: $isbn,
    title: $title,
    author: $author,
    description: $description,
    publisher: $publisher,
    published_year: $published_year) {
      updated_date
    }
  }
`;

The above constant variables are Apollo GraphQL Query of the GET and the UPDATE Book data. Next, inside the main class, add an object of the state that contains required fields.

state = {
 isbn: '',
 title: '',
 author: '',
 description: '',
 published_year: '',
 publisher: '',
}

Add a function to update only the state instead of the state and the fields because the fields will fill with the data from GraphQL. In this case, you can't call setState twice to fill the state, so, we fill the state and the fields separately.

updateTextInput = (text, field) => {
 const state = this.state
 state[field] = text;
 this.setState(state);
}

Modify the React Native render section to Apollo GraphQL Query, Mutation, React Native ScrollView, View, TextInput, Button, etc.

render() {
  const { navigation } = this.props;
  const { isbn, title, author, description, published_year, publisher } = this.state;
  return (
   <Query query={GET_BOOK} variables={{ bookId: navigation.getParam('id') }}>
    {({ loading, error, data }) => {
     if (loading) return(<View style={styles.activity}>
       <ActivityIndicator size="large" color="#0000ff" />
      </View>);
     if (error) return(<Text>`Error! ${error.message}`</Text>);
     return (
      <Mutation mutation={UPDATE_BOOK} key={data.book._id} onCompleted={() => navigation.goBack()}>
       {(updateBook, { loading2, error2 }) => (
        <ScrollView style={styles.container}>
         <View style={styles.subContainer}>
          <TextInput
            style={styles.textInput}
            placeholder={'ISBN'}
            defaultValue={data.book.isbn}
            onChangeText={(text) => this.updateTextInput(text, 'isbn')}
          />
         </View>
         <View style={styles.subContainer}>
          <TextInput
            style={styles.textInput}
            placeholder={'Title'}
            defaultValue={data.book.title}
            onChangeText={(text) => this.updateTextInput(text, 'title')}
          />
         </View>
         <View style={styles.subContainer}>
          <TextInput
            style={styles.textInput}
            placeholder={'Author'}
            defaultValue={data.book.author}
            onChangeText={(text) => this.updateTextInput(text, 'author')}
          />
         </View>
         <View style={styles.subContainer}>
          <TextInput
            style={styles.textInput}
            placeholder={'Description'}
            multiline={true}
            numberOfLines={4}
            defaultValue={data.book.description}
            onChangeText={(text) => this.updateTextInput(text, 'description')}
          />
         </View>
         <View style={styles.subContainer}>
          <TextInput
            style={styles.textInput}
            placeholder={'Published Year'}
            defaultValue={data.book.published_year.toString()}
            keyboardType='numeric'
            onChangeText={(text) => this.updateTextInput(text, 'published_year')}
          />
         </View>
         <View style={styles.subContainer}>
          <TextInput
            style={styles.textInput}
            placeholder={'Publisher'}
            defaultValue={data.book.publisher}
            onChangeText={(text) => this.updateTextInput(text, 'publisher')}
          />
         </View>
         <View>
          <Button
           large
           leftIcon={{name: 'save'}}
           title='Save'
           onPress={() => {
            if (this.state.isbn === '')
             this.state.isbn = data.book.isbn;
            if (this.state.title === '')
             this.state.title = data.book.title;
            if (this.state.author === '')
             this.state.author = data.book.author;
            if (this.state.description === '')
             this.state.description = data.book.description;
            if (this.state.publisher === '')
             this.state.publisher = data.book.publisher;
            if (this.state.published_year === '')
             this.state.published_year = data.book.published_year;
            updateBook({
             variables: {
              id: data.book._id,
              isbn: this.state.isbn,
              title: this.state.title,
              author: this.state.author,
              description: this.state.description,
              publisher: this.state.publisher,
              published_year: parseInt(this.state.published_year),
             }
            })
             .then(res => res)
             .catch(err => <Text>{err}</Text>);
           }} />
         </View>
         {loading2 && <View style={styles.activity}>
           <ActivityIndicator size="large" color="#0000ff" />
          </View>}
         {error2 && <Text>`Error! ${error2.message}`</Text>}
        </ScrollView>
       )}
      </Mutation>
     );
    }}
   </Query>
  );
 }
}

Modify the React Native style to match the view requirements.

const styles = StyleSheet.create({
 container: {
  flex: 1,
  padding: 20
 },
 subContainer: {
  flex: 1,
  marginBottom: 20,
  padding: 5,
  borderBottomWidth: 2,
  borderBottomColor: '#CCCCCC',
 },
 activity: {
  position: 'absolute',
  left: 0,
  right: 0,
  top: 0,
  bottom: 0,
  alignItems: 'center',
  justifyContent: 'center'
 },
 textInput: {
  fontSize: 18,
  margin: 5,
 },
})

React Native and Apollo GraphQL Tutorial: Run and Test the Mobile Apps

Before running the React Native apps, we have to download and run the Node.js, Express.js, MongoDB GraphQL server. After download and install the required Node modules, run first the MongoDB server in the different terminal tab.

mongod

Run the Express.js GraphQL server in another terminal tab.

nodemon

In the current React Native app terminal tab, run the React Native app.

yarn start

Open again the Expo app on your iOS or Android device then refresh the current running React Native application. And here they are the full React Native Mobile Apps running with Apollo GraphQL data.

That it's, the React Native and Apollo GraphQL Tutorial: Build Mobile Apps with React Native Elements and Navigation. You can find the full source code from our GitHub.

How to Build a Webstore Using Modern Stack (Nest.js, GraphQL, Apollo) Part 1

How to Build a Webstore Using Modern Stack (Nest.js, GraphQL, Apollo) Part 1

The following article will show you how to build a web store using modern stacks with (Nest.js, GraphQL, Apollo)

Originally published by Bogdan Nechyporenko at dzone.com

The goal of my article is to share my experience of how I configured Nest.js, GraphQL, and Apollo together. I have extensive experience with software, such as React, Ramda, Gulp, and Webpack; I've touched on others, such as TypeScript and GraphQL, a bit — I know the context and the idea behind them, but I do not have much experience using them. And others, such as Apollo and Material UI, I have almost no experience with.

The goal of this project was two-fold. I wanted to familiarize myself with software that I hadn't previously worked extensively with in order to create an open source, highly-customizable WebStore (related to UI, services, databases, integration layers, localization, etc.); as a result, there shouldn’t be any boundary to make some modules work with a completely different ecosystem.

My plan is to have periodical articles describing new features, how they were built, and what kind of architecture was used. I’ll use branching, and you can always come to that state to check out the project and be confident that the content of articles matches the source code in GitHub. For this article, you need to switch to the 0.1.x-maintenance branch. 

As an initial design, I took an amazing material dashboard from Creative Tim's website.

Technology wise I used:

  • Typescript for the frontend and backend.
  • Nest.js for the backend.
  • GraphQL for API communication between the frontend and backend — type-graphql as a facilitator).
  • MySQL for initial database storage.
  • Project uses ORM — typeorm
  • React for the frontend
  • Apollo for the GraphQL client.  
  • Design is based on Material UI components.
  • Gulp as a build system tool.
  • Webpack helps to create a frontend bundle.
  • Jest for unit testing.
  • Cypress for integration testing.
  • Lerna to build a mono repository.
  • Circle CI for continuous integration.
The Project

Circle CI

To check the project on each commit, I decided to use Circle CI. 

Requirements

  1. A create a .circleci folder in the root of the project.
  2. create config.yml file in .circleci folder.

In config.yml, we use Docker base images on which we can install extra modules. In CircleCI, you can specify several Docker images, and it will behave in the same way as Docker compose; that’s really handy. Here is the chosen Node Docker image, so it’s possible to work with npm, as we have a frontend and MySQL with preinstalled databases.

jobs:
  build:
    docker:
      - image: circleci/node:12
      - image: circleci/mysql:5.7
        environment:
          MYSQL_ROOT_PASSWORD: zebra
          MYSQL_DATABASE: zebra
          MYSQL_USER: zebra
          MYSQL_PASSWORD: zebra

When the docker image is downloaded, you can configure your own custom steps and the job names you’d like to run.

Gulp

Gulp is used to create a seamless flow of running consequent commands; some of them can be run in parallel. Others can be run sequentially. 

To run the backend server, you need to execute the command: gulp nest-server

To run the frontend server, you need to execute the command: gulp dev-server  

If you’ll have a look at the e2e.js file, you’ll see which commands are running first before starting running e2e tests on Cypress. 

gulp nest-server  runs npm run start  and waits until the server will be up and running by pinging the URL, http://localhost:3333/ping, every five seconds. If after fifth attempt the URL is still not accessible, the process will be killed. 

gulp dev-server  is running a webpack client, which builds frontend source code to a bundle, runs a dev server based on Express.js with configured proxy to the backend server, and makes the frontend page accessible on http://localhost:6517.

Lerna

To be able to run npm install  or yarn in different folders, I used Lerna.

You can find the config file for Yerna in the root of the project, in the file named lerna.json  

{
  "npmClient": "yarn”, // changes default NPM client to Yarn
  "version": "independent”, // allows us to publish each package with independent version
  "parallel": true, // allows us to build packages in parallel
  "stream": true, // output from child processes immediately with the originating package name
  "packages": [
    "src/**” // glob to match directories with package.json, which afterwards will be treated as              // packages
  ],
  "useWorkspaces": true, // enables integration with Yarn Workspaces
  "workspaces": ["src/*”] // specifying the sources of workspaces
}

Webpack

To create a bundle for the frontend we use Webpack. Configuration is located in gulp/tasks/webpack.js . There are two configurations — one for development and another for the development environment. The biggest difference is that for production, the code is minimized and the webpack dev server client is not running. It’s used only in development to be able to get hot-reloading features. 

Nest.js

As a backend server, I used Nest.js. It contains functionality, which boosts the performance of development. The Nest server is started with command defined in package.json residing in the server folder:  "ts-node -r tsconfig-paths/register src/main.ts" 

Let's have a look at the main.ts file:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
    const app = await NestFactory.create(AppModule);
    await app.listen(3333);
}
bootstrap();

We are creating the main module of application (line 6) and waiting for when the application will be started by listening to the defined port. This is an entry point to the backend of the application. 

import {Module} from '@nestjs/common';
import {GraphQLModule} from '@nestjs/graphql';
import {ProductModule} from './modules/products/product-module';
@Module({
    imports: [
        ProductModule,
        GraphQLModule.forRoot({
            installSubscriptionHandlers: true,
            autoSchemaFile: 'schema.gql',
        })
    ]
})
export class AppModule {
}

On that level, we define a module that will work with products and the name of the genereated database schema for the entire application. Each specific part of the database should be placed in its own module; then, you'll make your application granular enough to be split into logical parts. After a time, you can replace/remove outdated parts with less hassle. As at this point of time, current implementation contains only one module — ProductModule

import {Module} from '@nestjs/common';
import {DatabaseModule} from '../database/database.module';
import {productProviders} from './product-providers';
import {ProductService} from './product-service';
import {ProductResolver} from './product-resolver';
@Module({
    imports: [DatabaseModule],
    providers: [
        ...productProviders, // creates a connection to a product repository
        ProductService, // creates a transformation to DB entities and GraphQL objects
        ProductResolver // creates a layer to communicate with a database
    ],
})
export class ProductModule {
}

Typeorm

To not map every object's field manually to a database table I decided to use an ORM solution. It supports a number of databases, so that's quite handy. Initial implementation is done for MySQL, but with a little of effort, it can be changed to another database. In the next stage, I'd like to demonstrate how to connect to other databases.

import {Entity, PrimaryGeneratedColumn, Column} from 'typeorm';
@Entity()
export class ProductEntity {
    @PrimaryGeneratedColumn()
    id: number;
    @Column()
    name: string;
    @Column()
    description: string;
    @Column('float')
    price: number;
}

As you can see from this example, it's a quite simple and straightforward way of annotating entity fields to map them on a database table. 

To define database implementation and all required configuration, you'll need to create a ormconfig.json file in the root of the module. 

{
  "type": "mysql",
  "host": "localhost",
  "port": 3306,
  "username": "zebra",
  "password": "zebra",
  "database": "zebra",
  "synchronize": true,
  "logging": false,
  "entities": [
    "src/modules/**/entity/*-entity.ts"
  ],
  "migrations": [
    "src/modules/**/migration/*.ts"
  ],
  "subscribers": [
    "src/modules/**/subscriber/*.ts"
  ],
  "cli": {
    "entitiesDir": "src/modules/**/entity",
    "migrationsDir": "src/modules/**/migration",
    "subscribersDir": "src/modules/**/subscriber"
  }
}

Apart from all credentials for the database, you need to define what folders have to be scanned for entities/mutations/etc. That's made with performance in mind, especially when the project becomes monstrous size.

GraphQL

GraphQL gives us another way of communicating with the frontend and backend. Additionally, when you have a complicated graph of objects, GraphQL allows you to specify exactly which objects and which fields you are interested in. In the REST approach, you'll get the full object or full graph of objects.

As GraphQL is based on strong typing, you have to explicitly declare input/output types. Based on objects (product.ts, productInput,ts) annotated by the type-graphql library and defined mutations in product-resolver.ts during compilation time, the GraphQL schema is generated (schema.sql):

# -----------------------------------------------
# !!! THIS FILE WAS GENERATED BY TYPE-GRAPHQL !!!
# !!!   DO NOT MODIFY THIS FILE BY YOURSELF   !!!
# -----------------------------------------------
type Mutation {
  addProduct(product: ProductInput!): Product!
  removeProduct(name: String!): Product!
}
type Product {
  id: ID!
  name: String!
  description: String
  price: Float!
}
input ProductInput {
  name: String!
  description: String
  price: Float!
}
type Query {
  product(name: String!): Product
  """Get all the products from around the world """
  products: [Product!]!
}

In this project, I decided to use source of truth entities and generate the schema out of that, but you can also configure it so that you can define the schema manually. It might be useful in some cases when you don't want to rely on an auto-generation tool.

Frontend

For the frontend, I decided to create a single section, as it's quite hard to separate each topic separately. As I have experience with React and I really love it, I'm using it in this project. Previously, I hadn't worked with TypeScript, as at my current project at work I really don't see any problem with using pure Javascript, especially with appeared ES6; replacing a working solution without a significant reason is never a good idea. 

Let's have a look at the entry point of the frontend side: 

import historyService from './history';
import React from 'react';
import ReactDOM from 'react-dom';
import {AppContainer} from 'react-hot-loader';
import {Provider} from 'react-redux';
import {ApolloClient} from 'apollo-client';
import {InMemoryCache} from 'apollo-cache-inmemory';
import {HttpLink} from 'apollo-link-http';
import {ApolloProvider} from 'react-apollo';
import {Redirect, Route, Switch} from 'react-router';
import {ConnectedRouter} from 'connected-react-router';
import {store} from './plumbing';
import Admin from './components/Admin';
const cache = new InMemoryCache();
const link = new HttpLink({
    uri: 'http://localhost:3333/graphql'
});
const client = new ApolloClient({
    cache,
    link
});
ReactDOM.render(
    <AppContainer>
        // applying Appolo Client
        <ApolloProvider client={client}>
            // applying Redux store
            <Provider store={store}>  
               // applying React Redux routing  
                <ConnectedRouter history={historyService.history} key={Math.random()}> 
                    <div className="app-root">
                        // defining the application routes
                        <Switch>
                            <Route path="/admin" component={Admin}/>
                            <Redirect from="/" to="/admin/table"/>
                        </Switch>
                    </div>
                </ConnectedRouter>
            </Provider>
        </ApolloProvider>
    </AppContainer>,
    document.getElementById('root')
);

As we are using GraphQL, we have to somehow read that format on the frontend and map it to React elements. To solve that issue, we're using Appollo Client; in order to use it, you have to know how to connect it to an exposed GraqhGL schema (line 18-20). The connection, at this point, is not secured. That's why knowing the link address is enough. In the next stage, I'll walk through a few ways to make the connection secure. 

Another thing that you have to configure is where to save the cache (line 16). Currently, I'm using the simplest solution — to keep the cache in memory. 

The render point for React application is defined on lines 27-42. document.getElementById('root')  attaches that component to defined the element with id="root" in the index.html file.

Currently, Redux is used only for routing. As the project evolves, Redux will keep the internal state of components inside, so you can easily control component from another component or define the state. That's way more flexible and stable approach compared to keeping the internal state inside of the component itself, as it is done now in Admin.tsx.

class Dashboard extends React.Component<Props, State> {
    static propTypes: { classes: Validator<NonNullable<object>> };
    constructor(props) {
        super(props);
        this.state = {mobileOpen: false}; // that part better to keep in Redux store
    }
  ....

Let's have a look at how React components communicate with GraphQL, starting from the React component in  ProductItems.tsx.

export default compose(
    withApollo,
    // @ts-ignore
    withStyles(styles),
    GET_ALL_PRODUCTS,
    REMOVE_PRODUCT
)(ProductItems);

Here is an important line: withApollo. We create a connection with the Apollo Client. And then we inject two functions to properties defined in GETALLPRODUCTS and REMOVE_PRODUCTIf you'll open both of them, you'll find that there is a name defined in each of these variables. For example, GET_ALL_PRODUCTS, you find the defined allProducts name, and that's why we desctuct it from properties in ProductsItem:

function ProductItems(props) {
    const {allProducts, classes, removeProduct} = props;
    return (

How the query itself is defined:

import {gql} from 'apollo-boost';
import {graphql} from 'react-apollo';
export const GET_ALL_PRODUCTS = gql`
  {
      products {
        name,
        description,
        price
      }
  }
`;
export default graphql(GET_ALL_PRODUCTS, {name: 'allProducts'});

So in essense, you provide a GraphQL query, which options contain at least a name for UI connection.

Data modification works the same way, just with extra parameters. Let's have a look at add-product-mutation.ts 

import {gql} from 'apollo-boost';
import {graphql} from 'react-apollo';
import {GET_ALL_PRODUCTS} from '../queries/get-all-products';
const ADD_PRODUCT = gql`
  mutation AddProduct($product: ProductInput!) {
   addProduct(product: $product) {
    name,
    description,
    price
   }
  }
`;
export default graphql(ADD_PRODUCT, {
    name: 'addProduct',
    options: () => ({
        refetchQueries: [{
            query: GET_ALL_PRODUCTS,
        }]
    }),
});

In parameters, alongside a name, an optionsfield is defined. You can do it even without it, and it still will work. Watch what refetchQueriresdoes here that solves the caching problem. Once you add a new product, you need to update the table, and for that, you need to notify the Apollo Client which queries have to be refetched. They don't do it automatically in order to not degrade the performance of the application, so you, as a developer, have to decide where and when the data has to be refetched. 

Tests

Test suites are missing so far though. CircleCI and Cypress are fully configured and working. As the article became quite long, I chose to split that information to the next article and describe how you can test even that simple application. 

Please leave your feedback in the comments section; I'd really appreciate hearing your opinion about the article and the code. 

Originally published by Bogdan Nechyporenko at dzone.com

=============================

Thanks for reading

If you liked this post, share it with all of your programming buddies!

Follow me on Facebook | Twitter

Learn More

☞ NestJS Zero to Hero - Modern TypeScript Back-end Development

☞ The Complete Node.js Developer Course (3rd Edition)

☞ Complete Next.js with React & Node - Beautiful Portfolio App

☞ Angular & NodeJS - The MEAN Stack Guide

☞ NodeJS - The Complete Guide (incl. MVC, REST APIs, GraphQL)


Beginner’s Guide to GraphQL with Angular and Apollo

Beginner’s Guide to GraphQL with Angular and Apollo

In this article, we are going to build a small application using Angular and Apollo client

Originally published by Swathi Prasad at https://techshard.com

GraphQL has been gaining popularity and it has made its way to the forefront of API development. There are several GraphQL clients available today for front-end frameworks. Apollo client is one of the popular clients for Angular applications.

Creating Angular Application

Let’s create a new Angular project using Angular CLI. Run the following command in a terminal or command prompt.

ng new angular-apollo –style=scss –routing=true

I have created project with Angular 8 for this tutorial.

Once the project is created, navigate to the project directory angular-apollo in the command prompt or terminal. Add apollo-angular dependency using the command below.

ng add apollo-angular

This command will add following list of dependencies to the project.

"apollo-angular": "^1.6.0",
   "apollo-angular-link-http": "^1.6.0",
   "apollo-cache-inmemory": "^1.3.2",
   "apollo-client": "^2.6.0",
   "apollo-codegen": "^0.20.2",
   "apollo-link": "^1.2.11",
   "graphql": "^14.3.1",
   "graphql-tag": "^2.10.0"

This command would also create a new file graphql.module.ts. This file can be found under /src/app/. In this file, you will find the below line.

const uri = ''; // <-- add the URL of the GraphQL server here

Add this URL http://localhost:8080/graphql to the above line. This is the GraphQL server endpoint which I created in my previous article.

Accessing GraphQL Mutation endpoints in Angular Apollo

The GraphQL server contains the endpoints for creating and reading data for a Vehicle entity. The process that involves write operations is called Mutation. These operations may involve create, update and delete.

The vehicle data model on the server looks like:

type Vehicle {
       id: ID!,
       type: String,
       modelCode: String,
       brandName: String,
       launchDate: String
}

 The mutation endpoint on the server is as below.

type Mutation {
       createVehicle(type: String!, modelCode: String!, brandName: String, launchDate: String):Vehicle
}

Now, we will access this mutation endpoint using Apollo client.

this.apollo.mutate({
     mutation: gql`mutation {
       createVehicle(type: "car", modelCode: "XYZ0192", brandName: "XYZ", launchDate: "2016-08-16") 
        {
         id
       }
     }`
   }).subscribe(data => {
     //successfully created vehicle entity.
   });

I have added the above code in app.component.ts as below. This is the just a basic example to show how the call is made using Apollo client. Note that, we could use services provided by Apollo client for reusability and maintainability.

import { Component, OnInit } from '@angular/core';
import { Apollo } from 'apollo-angular';
import gql from 'graphql-tag';

@Component({
 selector: 'app-root',
 templateUrl: './app.component.html',
 styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {

 data: any;

 constructor(private apollo: Apollo) { }

 ngOnInit() {
   this.apollo.mutate({
     mutation: gqlmutation { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;createVehicle(type: "car", modelCode: "XYZ0192", brandName: "XYZ", launchDate: "2016-08-16") &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;id &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
   }).subscribe(data => {
     //successfully created vehicle entity.
   });
 }
}

In this call, we can fetch all the fields from Vehicle object or only a few fields that we need. Here, we are only asking for id field. The server gives back only the id field in the data object.

Querying Data using Apollo Client

As the heading suggests, we will fetch the data using GraphQL query. The query on the server is as follows:

type Query {
       vehicles(count: Int):[Vehicle]
       vehicle(id: ID):Vehicle
}

Using Apollo client, we will access the above the endpoint.

this.apollo.query({
     query: gqlquery { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;vehicles(count: 1) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;modelCode, &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;brandName &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
   }).subscribe(({ data, loading }) => {
     this.data = data;
   });

Here, we are interested in only modelCode and brandName fields. The server returns the data with only these fields.

Let’s add a little bit of HTML code to display the data.

 <div class="card" *ngFor="let vehicle of data.vehicles">
   <label class="content">Model: {{ vehicle.modelCode }}</label>
   <label class="content">Brand: {{ vehicle.brandName }}</label>
 </div>
Running the App

Run the application using ng serve. The final app looks like below.


Wrapping Up

We have just scratched the surface of Apollo client. It offers much more features. Check out their documentation for further information.

I hope you enjoyed this article. Let me know if you have any comments or suggestions in the comment section below.

The example for this tutorial can be found on GitHub repository.

Thanks for reading

If you liked this post, share it with all of your programming buddies!

Follow us on Facebook | Twitter

Further reading

Angular 8 (formerly Angular 2) - The Complete Guide

Angular & NodeJS - The MEAN Stack Guide

The Complete Node.js Developer Course (3rd Edition)

Best 50 Angular Interview Questions for Frontend Developers in 2019

React vs Angular: An In-depth Comparison

React vs Angular vs Vue.js by Example

A Beginner’s Guide to GraphQL

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

Developing and Securing GraphQL APIs with Laravel




Fully functional WhatsApp Clone using Angular, GraphQL, Apollo, TypeScript and PostgreSQL

Fully functional WhatsApp Clone using Angular, GraphQL, Apollo, TypeScript and PostgreSQL

An open-source full-stack example app made with Angular 7.2, TypeScript, GraphQL Subscriptions, GraphQL Code Generator, GraphQL Modules, PostgreSQL and TypeORM.

An open-source full-stack example app made with Angular 7.2, TypeScript, GraphQL Subscriptions, GraphQL Code Generator, GraphQL Modules, PostgreSQL and TypeORM.

You might have seen it around already — an open-source WhatsApp Clone tutorial; a project which was originally started in 2015 by Urigo based on Angular-Meteor and Ionic, and have been throughout different incarnations ever since.

You may have also noticed that we recently published a new React version of the Whatsapp Clone tutorial.

This time around, I’m happy to announce that a new version of the WhatsApp Clone is here, and it’s based on Angular 7.2, Angular CLI 7.3.2, Material-UI, TypeScript, Apollo, GraphQL-Subscriptions, GraphQL Code Generator, GraphQL Modules, PostgreSQL and TypeORM, full with step by step guides to teach you every step of the way.

Click me to go to the tutorial page

We’re back in business!

What is it good for?

This app was built with all the latest and hottest technologies out there. The purpose is simple — it should be a guideline for building a proper app, thus we thought very carefully about the design patterns and architecture used in it, plus, we made sure to cover all communication methods with a GraphQL-back-end in different variations (query, mutation, subscription). This way whenever you’re looking to start a new app, maintain an existing one or upgrade your dev-stack, the WhatsApp-clone can be a great source to start with! It’s full stack and has a complete flow.

Why did we choose this dev-stack?

Angular, GraphQL, Apollo, PostgreSQL and TypeScript for obvious reasons — they are backed by a strong ecosystem that grows rapidly. These technologies can be used in endless variations, and there’s no one way which is the most right of doing so, but we chose a way that makes the most sense for us and that we truly believe in when it comes to building apps. We’ve connected it all with TypeORM, GraphQL-Code-Generator, GraphQL-Modules for the following reasons:

  • The GraphQL back-end was implemented using GraphQL-Modules where logic was splitted into feature based modules. GraphQL-Modules is a library which provides you with the ability to manage and maintain your GraphQL schema in a scalable and reusable way. Not once nor twice I have seen people who struggle with that and get tangled upon their own creation, and with GraphQL-Modules where you have a very defined structure, this problem can be easily solved. You can read more in this series of 7 blog posts about it.
  • Every GraphQL/TypeScript definition was automatically generated with GraphQL-Code-Generator using a single command call. There’s no need to maintain the same thing twice if it already exists in one way or another. This way you don’t have to write TypeScript type definitions for your all your server responses, you get ready-to-use — fully typed Angular services, GraphQL resolvers and GraphQL types.
  • The new version of Angular 7.2 was used with the Angular Material UI and Angular CLI 7.3.2 (and we’ll keep updating the tutorial with the latest versions)
  • We used TypeORM to correctly split the logic of the entities in the database and define the relationships between them. ORMs are controversial these days, but they can help a lot in some cases and we thought a good example could be valuable to the community.

What to expect?

  • The GraphQL back-end was implemented using GraphQL-Modules where logic was splitted into feature based modules. GraphQL-Modules is a library which provides you with the ability to manage and maintain your GraphQL schema in a scalable and reusable way. Not once nor twice I have seen people who struggle with that and get tangled upon their own creation, and with GraphQL-Modules where you have a very defined structure, this problem can be easily solved. You can read more in this series of 7 blog posts about it.
  • Every GraphQL/TypeScript definition was automatically generated with GraphQL-Code-Generator using a single command call. There’s no need to maintain the same thing twice if it already exists in one way or another. This way you don’t have to write TypeScript type definitions for your all your server responses, you get ready-to-use — fully typed Angular services, GraphQL resolvers and GraphQL types.
  • The new version of Angular 7.2 was used with the Angular Material UI and Angular CLI 7.3.2 (and we’ll keep updating the tutorial with the latest versions)
  • We used TypeORM to correctly split the logic of the entities in the database and define the relationships between them. ORMs are controversial these days, but they can help a lot in some cases and we thought a good example could be valuable to the community.

The tutorial goes through every aspect of building the app, starting from the very basics. We will start building a very simple server with a fake db, then we will introduce Authentication, Subscriptions, a real database backed by PostgreSQL and TypeORM plus advanced tooling like GraphQL Code Generator and GraphQL Modules.

This can be extremely useful for those who have little to no background in one of the technologies in our dev-stack.

What’s next?

Right now we implemented a simple REST-based Passport authentication, but we already have PRs[1][2] for Accounts-JS based authentication which will use the GraphQL endpoint instead of traditional REST ones. An additional chapter about Pagination is also expected, as well as a “Performance” chapter tackling the N+1 problem with GraphQL. Our backend has been designed to handle way more features than the ones currently implemented, so be ready because features like the Whatsapp blue ticks are going to land on our clone very soon.

Keep up to date

This tutorial was written using Tortilla — the Tutorial framework.

This means that we will keep upgrading the tutorial with the latest versions of Angular and the other libraries, and instead of doing the same tutorial all over again, you will get a git-diff of how to upgrade the existing tutorial! We care about your time as a developer.

Influence

We want to hear your opinions!

Should we choose another library and technology over another? Could we write the code better/cleaner? Should we add a specific feature to the app? We want to hear it all!

Please tell us now so we could integrate your feedback on the tutorial itself!

We keep evolving the stack and as the tutorial is based on git commits, we can create the same clone with different tech-stacks and compare them on this real app using code diffs.

Soon we will also release yet another version of the Whatsapp Clone, using Ionic, Stencil and Web Components.

If you are good at creating screencast and videos, we would love your help in creating videos for some of the chapters.

Everything is completely free and open source, and we want your help and (not financial) contribution!

Best place would be to open an issue or create a PR on the repositories:

  • The GraphQL back-end was implemented using GraphQL-Modules where logic was splitted into feature based modules. GraphQL-Modules is a library which provides you with the ability to manage and maintain your GraphQL schema in a scalable and reusable way. Not once nor twice I have seen people who struggle with that and get tangled upon their own creation, and with GraphQL-Modules where you have a very defined structure, this problem can be easily solved. You can read more in this series of 7 blog posts about it.
  • Every GraphQL/TypeScript definition was automatically generated with GraphQL-Code-Generator using a single command call. There’s no need to maintain the same thing twice if it already exists in one way or another. This way you don’t have to write TypeScript type definitions for your all your server responses, you get ready-to-use — fully typed Angular services, GraphQL resolvers and GraphQL types.
  • The new version of Angular 7.2 was used with the Angular Material UI and Angular CLI 7.3.2 (and we’ll keep updating the tutorial with the latest versions)
  • We used TypeORM to correctly split the logic of the entities in the database and define the relationships between them. ORMs are controversial these days, but they can help a lot in some cases and we thought a good example could be valuable to the community.

Again, all types of feedback is welcome, write freely!

Thanks for reading ❤