In the previous post, we saw how to generate your backend, enable Google Authentication, and start a new React project with GraphQL query.
Now, let's get through the process of building a small data model and implement the corresponding GraphQL query and subscription in
React to create a real-time chat.
This tuto is for beginners. You don't need to be a senior developer or be an expert in React, SQL, and GraphQL. Agoston's backend will provide the power of using GraphQL without having to deal with its complexity.
1. Connect to the Postgres instance of your backend
Go to the Agoston.io backends view. Expand the View of the Postgres access of your backend:
You will see the information to connect to your dedicated Postgres instance:
You can use the Postgres client or IDE of your choice, such as DataGrip, VS Code, pgAdmin, etc. I personally use pgAdmin which works perfectly.
2. Create your data model in the Postgres instance
Agoston is database centric. You do everything in the database, and Agoston will automatically expose it to the GraphQL endpoint.
So, create a new Postgres server in pgAdmin:
Add the connection details that you get in the Agoston Postgres access popup:
Still, on pgAdmin, we expand the Postgres server we just created and open a new "Query tool":
And we create two tables that will handle our chat data:
CREATE TABLE IF NOT EXISTS agoston_public.chats
(
id serial,
name character varying(50) COLLATE pg_catalog."default" NOT NULL,
created_ts timestamp with time zone NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT chats_pkey PRIMARY KEY (id)
);
CREATE TABLE IF NOT EXISTS agoston_public.chat_messages
(
id serial,
chat_id integer NOT NULL,
user_id integer NOT NULL DEFAULT get_current_user_id(),
created_ts timestamp with time zone NOT NULL DEFAULT CURRENT_TIMESTAMP,
message character varying(250) COLLATE pg_catalog."default" NOT NULL,
CONSTRAINT chat_messages_pkey PRIMARY KEY (id),
CONSTRAINT chat_messages_chat_id FOREIGN KEY (chat_id)
REFERENCES agoston_public.chats (id),
CONSTRAINT chat_messages_user_id FOREIGN KEY (user_id)
REFERENCES agoston_identity.user_identities (id)
);
Finally, grant necessary permissions for this demo:
GRANT SELECT ON TABLE agoston_public.chats TO anonymous, authenticated;
GRANT SELECT ON TABLE agoston_public.chat_messages TO anonymous, authenticated;
3. Create a GraphQL query from the built-in GraphiQL IDE
Your Agoston backend comes with a GraphiQL IDE that helps you build your requests. Open it from the backends view:
A new tab opens with GraphiQL. In GraphiQL, there is an explorer of your GraphQL schema. That's very convenient for exploring your schema and building GraphQL requests. Open it here:
In the explorer, you'll see the chat tables we created before.
NOTE: Any change to the data model in Postgres will automatically be included in the GraphQL endpoint.
Now we can create the first query to retrieve the chat messages from our chat id 1:
query chatMessages($chatId: Int! = 1) {
chatMessages(condition: {chatId: $chatId}, orderBy: ID_DESC, first: 25) {
nodes {
id
createdTs
userId
message
}
}
}
4. Implement the GraphQL query in React
In our React application, I create not only the query but also the subscription that will listen for new messages coming in the chat messages table:
import { useQuery, useMutation, useSubscription } from 'urql';
import { gql } from '@urql/core';
// Here is a fragment that I will use to avoid
// duplicated attributes in the query and in
// the subscription. Because they return the
// same attributes
const ChatMessagesFragment = gql`
fragment MessageNode on ChatMessage {
id
createdTs
userId
message
}
`;
// Here is the Query executed during
// the module loading
const ChatMessages = gql`
query chatMessages($chatId: Int!) {
chatMessages(condition: {chatId: $chatId}, orderBy: ID_DESC, first: 25) {
nodes {
...MessageNode
}
}
}
${ChatMessagesFragment}
`;
// Here is the subscription that will
// load new messages
const NewChatMessages = gql`
subscription chatMessages($chatId: Int!, $topic: String!) {
listen(topic: $topic) {
query {
chatMessages(condition: {chatId: $chatId}, orderBy: ID_DESC, first: 25) {
nodes {
...MessageNode
}
}
}
}
}
${ChatMessagesFragment}
`;
5. Create the React component that will use the GraphQL query and subscription
function ChatBoxMessages() {
const chatId = 1;
// We do a first initial query to load our chat data
const [resultQuery] = useQuery({
query: ChatMessages,
variables: { chatId },
});
// We create a subscription using the urql hook to start listening
// to our trigger notification that will let us know when new messages are inserted
const [resultSubscription] = useSubscription({ query: NewChatMessages, variables: { chatId, topic: `chat_messages:chat_id:${chatId}` } });
if (resultQuery.fetching) return <p>Loading...</p>;
if (resultQuery.error) return <p>Oh no... {resultQuery.error.message}</p>;
var messages = []
if (!resultSubscription.data){
messages = resultQuery.data.chatMessages.nodes;
}else{
messages = resultSubscription.data.listen.query.chatMessages.nodes;
}
return (
<div className="messages">
{messages.map(message => (
<div key={message.id}>
<p className="message-line"> ⏱ {message.createdTs.substring(0,19).replace("T", " ")} <span className='user-id'>👤 {message.userId===0?'anonymous':`User ${message.userId}`}</span> <span className="message">🗣 {message.message}</span></p>
</div>
))}
</div>
);
};
const Chat = () => {
return (
<div className="chatbox">
<ChatBoxInput />
<ChatBoxMessages />
</div>
);
}
export default Chat
Complete example available here.
Conclusion
This post shows how simple and fast the process is to get from your data model to a real-time React component using GraphQL query and subscription.
Disclaimer
I'm the maker of Agoston.io, which is a backend as a service leveraging tried-and-tested open-source solutions. It allows beginner and junior developers to leverage the power of GraphQL without having to deal with its complexity.
Agoston includes a single GraphQL endpoint, self-hosting, unified authentication, subscription, dedicated Postgres, custom code, Stripe webhooks, job scheduling, and triggers.
Top comments (0)