In this article, we will see how to build a chat application concept using react graphQL and Hasura. Building a Chat application with react graphql and Hasura.
Building Real time API using graphql subscriptions
TypeScript for React developers in 2020
Building a Piano with React Hooks
Complete Source code can be found here
Hasura is basically used to create a backend in less time. you don't need to setup a node server develop a graphql from scratch.
Hasura does that part for you. only thing you need to do is, create a postgres tables according to your application requirement.then, Hasura created all the GraphQL Queries,Mutations and Subscriptions for you.
Let's see how to setup hasura for our chat application. Setting up hasura is so easy on Heroku.
Once you deploy your app in heroku. it will take you to your heroku dashboard.
If you watch it carefully, Postgres database is already installed on your app.
Now it is time to create some postgres tables. click on Open App in your heroku dashboard. it will take you to your hasura console.
After that, we need to create tables for our application.
Once you create the tables, Hasura auto generates all the GraphQL Queries,Mutations and Subscriptions for you.
All you need to do is, use mutations and queries to insert and fetch the data from postgres database.
Our application backend work is done. So, easy!!! isn't it?.
Let's see how to setup react graphql for our application.
1npx create-react-app hasura-chat
Think about what are all components that we need for our chat application. Mainly we need a login page and chat elements. since it is a list we need to create a chat item as a component.
Everything is good so far. but wait, how are we going to handle the api call and data in react.
well, that is where we are going to use GraphQL here. i have to say, i tried two to three solutions for GraphQL. but, i was not satisfied until i found the current solution.
we are going to use Apollo react hooks for our API call. From my learning and experience, it is so far the easiest way to get started with GraphQL on react applications.
Okay. Enough talking. Let's get started with setting it up.
1npm install @apollo/react-hooks apollo-link-ws apollo-link-http apollo-link apollo-utilities apollo-cache-inmemory apollo-boost
Wow..that's a lot of packages. Let's break it down one by one.
After that, we need to set it up in our App.js. import all the packages in App.js.
1import ApolloClient from "apollo-client"2import { ApolloProvider } from "@apollo/react-hooks"3import { WebSocketLink } from "apollo-link-ws"4import { HttpLink } from "apollo-link-http"5import { split } from "apollo-link"6import { getMainDefinition } from "apollo-utilities"7import { InMemoryCache } from "apollo-cache-inmemory"
create http and websocket link with your hasura graphql endpoint
1const httpLink = new HttpLink({2 uri: "https://hasura-infiite-loader.herokuapp.com/v1alpha1/graphql", // use https for secure endpoint3})45// Create a WebSocket link:6const wsLink = new WebSocketLink({7 uri: "ws://hasura-infiite-loader.herokuapp.com/v1alpha1/graphql", // use wss for a secure endpoint8 options: {9 reconnect: true,10 },11})
Then, create a Apollo client for our application.
1// using the ability to split links, you can send data to each link2// depending on what kind of operation is being sent3const link = split(4 // split based on operation type5 ({ query }) => {6 const { kind, operation } = getMainDefinition(query)7 return kind === "OperationDefinition" && operation === "subscription"8 },9 wsLink,10 httpLink11)1213// Instantiate client14const client = new ApolloClient({15 link,16 cache: new InMemoryCache(),17})
finally, import it in our Apollo Provider
1function App() {2 return (3 <ApolloProvider client={client}>4 <ThemeProvider theme={customTheme}>5 <div className="App">6 <Routes />7 </div>8 </ThemeProvider>9 </ApolloProvider>10 )11}
Like we discussed in the beginning, let's create react components for our application.
create the components and routes for each components. Here, we have two routes, they are
Here, we use two important react hooks for graphql data fetch/insert. they are,
we use useMutation hooks in Login Component here. In login/index.js add the following code,
1import { useMutation } from "@apollo/react-hooks"2import { gql } from "apollo-boost"
we import useMutation and gql from react hooks and apollo boost to make a GraphQL request.
1const LOGIN_USER = gql`2 mutation InsertUsers($name: String!, $password: String!) {3 insert_users(objects: { name: $name, password: $password }) {4 returning {5 id6 name7 }8 }9 }10`
After that, we create a graphql mutation and store it in a constant LOGIN_USER
Then, inside our login component, define the useMutation react hook.
1const [insert_users, { data }] = useMutation(LOGIN_USER)
when use clicks the login button, call the insert_users function with required data.
1const onSubmit = () => {2 insert_users({ variables: { name: state.name, password: state.password } })3}
it will return the success response. store it in your component state and use it for the next step.
1import React, { useState, useEffect } from "react"2import { useForm } from "react-hook-form"3import {4 FormErrorMessage,5 FormLabel,6 FormControl,7 Input,8 Button,9 Box,10} from "@chakra-ui/core"1112import { useMutation } from "@apollo/react-hooks"13import { gql } from "apollo-boost"1415const LOGIN_USER = gql`16 mutation InsertUsers($name: String!, $password: String!) {17 insert_users(objects: { name: $name, password: $password }) {18 returning {19 id20 name21 }22 }23 }24`2526const Login = ({ history }) => {27 const [state, setState] = useState({28 name: "",29 password: "",30 })3132 const [insert_users, { data }] = useMutation(LOGIN_USER)3334 useEffect(() => {35 const user = data && data.insert_users.returning[0]36 if (user) {37 localStorage.setItem("user", JSON.stringify(user))38 history.push("/chat")39 }40 }, [data])41 const { handleSubmit, errors, register, formState } = useForm()4243 function validateName(value) {44 let error45 if (!value) {46 error = "Name is required"47 }48 return error || true49 }5051 function validatePassword(value) {52 let error53 if (value.length <= 4) {54 error = "Password should be 6 digit long"55 }5657 return error || true58 }5960 const onInputChange = e => {61 setState({ ...state, [e.target.name]: e.target.value })62 }6364 const onSubmit = () => {65 insert_users({ variables: { name: state.name, password: state.password } })6667 setState({ name: "", password: "" })68 }6970 return (71 <Box>72 <form onSubmit={handleSubmit(onSubmit)}>73 <FormControl isInvalid={errors.name}>74 <FormLabel htmlFor="name">Name</FormLabel>75 <Input76 name="name"77 placeholder="name"78 onChange={onInputChange}79 ref={register({ validate: validateName })}80 />81 <FormErrorMessage>82 {errors.name && errors.name.message}83 </FormErrorMessage>84 </FormControl>8586 <FormControl isInvalid={errors.password}>87 <FormLabel htmlFor="name">Password</FormLabel>88 <Input89 name="password"90 type="password"91 placeholder="password"92 onChange={onInputChange}93 ref={register({ validate: validatePassword })}94 />95 <FormErrorMessage>96 {errors.password && errors.password.message}97 </FormErrorMessage>98 </FormControl>99 <Button100 mt={4}101 variantColor="teal"102 isLoading={formState.isSubmitting}103 type="submit"104 >105 Submit106 </Button>107 </form>108 </Box>109 )110}111112export default Login
similary define the useSubscription inside the Chat/index.js to get the data through web sockets.
1import { useMutation, useSubscription } from "@apollo/react-hooks"2import { gql } from "apollo-boost"
we import useMutation, useSubscription and gql in the Chat component.
1const MESSAGES_SUBSCRIPTION = gql`2 subscription {3 messages {4 id5 text6 users {7 id8 name9 }10 }11 }12`1314const SUBMIT_MESSAGES = gql`15 mutation InsertMessages($text: String!, $userid: Int!) {16 insert_messages(objects: { text: $text, created_user: $userid }) {17 returning {18 text19 created_user20 users {21 name22 id23 }24 id25 }26 }27 }28`
Here, we have two graphql request. one is for Mutation and another one is for subscription.
After that, define the hooks in Chat component.
1const [insert_messages, { returnData }] = useMutation(SUBMIT_MESSAGES)23const { loading, error, data: { messages } = [] } = useSubscription(4 MESSAGES_SUBSCRIPTION5)
messages from subscription returns the updated data through websocket. so, use the data in react render for real time data.
1import React, { useState, useEffect } from "react"23import { Box, Flex, Input } from "@chakra-ui/core"45import ChatItem from "../ChatItem"67import { useMutation, useSubscription } from "@apollo/react-hooks"8import { gql } from "apollo-boost"910const MESSAGES_SUBSCRIPTION = gql`11 subscription {12 messages {13 id14 text15 users {16 id17 name18 }19 }20 }21`2223const SUBMIT_MESSAGES = gql`24 mutation InsertMessages($text: String!, $userid: Int!) {25 insert_messages(objects: { text: $text, created_user: $userid }) {26 returning {27 text28 created_user29 users {30 name31 id32 }33 id34 }35 }36 }37`3839const Chat = () => {40 const [state, setState] = useState({41 text: "",42 })4344 const [insert_messages, { returnData }] = useMutation(SUBMIT_MESSAGES)4546 const { loading, error, data: { messages } = [] } = useSubscription(47 MESSAGES_SUBSCRIPTION48 )4950 const onInputChage = e => {51 setState({ [e.target.name]: e.target.value })52 }5354 const onEnter = e => {55 if (e.key === "Enter") {56 let user = localStorage.getItem("user")57 user = JSON.parse(user)5859 insert_messages({ variables: { text: state.text, userid: user.id } })6061 setState({ text: "" })62 }63 }6465 return (66 <Box h="100vh" w="40%" margin="auto">67 <Flex direction="column" h="100%">68 <Box bg="blue" h="90%" w="100%" border="solid 1px" overflowY="scroll">69 {messages &&70 messages.map(message => {71 return <ChatItem item={message} />72 })}73 </Box>74 <Box bg="green" h="10%" w="100%">75 <Input76 placeholder="Enter a message"77 name="text"78 value={state.text}79 onChange={onInputChage}80 onKeyDown={onEnter}81 size="md"82 />83 </Box>84 </Flex>85 </Box>86 )87}8889export default Chat
ChatItem.js
1import React from "react"23import { Box, Flex, Avatar, Heading, Text } from "@chakra-ui/core"45const ChatItem = ({ item }) => {6 return (7 <Box h="60px">8 <Flex direction="row" alignItems="center" height="100%">9 <Avatar size="sm" padding="4px" marginLeft="10px" />10 <Flex direction="column" margin="5px">11 <Text fontSize="xl" margin="0">12 {item.users.name}13 </Text>14 <Text margin="0">{item.text}</Text>15 </Flex>16 </Flex>17 </Box>18 )19}2021export default ChatItem
So far, we have seen an application development using Hasura and Postgres on backend with React GraphQL and Apollo for front end.
In the upcoming article, we will see how to build a paginated api in Hasura and apollo react hooks and Infinite loader in react application. So stay tuned.
Until then, Happy Coding :-)
No spam, ever. Unsubscribe anytime.