Slack Clone with React | Semantic UI | GraphQL | PostgresSQL (PART 5)
AjeaS
Posted on September 24, 2020
Previously, we went over how GraphQL works. You can find that article here.
Today, we'll start to create our Graphql queries and mutations.
Open up the typesDefs.js
file. The first thing we need to do is map out what our data is going to be. We know that we need to have a User
object to represent a user in our project, so let's start there.
Types
Remove the previous code in our typeDefs.js
file and replace it with this =>
const { gql } = require("apollo-server");
module.exports = gql`
type User {
username: String!
email: String!
password: String!
}
`
Next, we need to create a Team
object for when a user needs to create a team
const { gql } = require("apollo-server");
module.exports = gql`
type Team {
owner: User!
members: [User!]!
channels: [Channel!]!
}
`
Were gonna need Channels
to join =>
const { gql } = require("apollo-server");
module.exports = gql`
type Channel {
id: Int!
name: String!
public: Boolean!
messages: [Message!]!
users: [User!]!
}
`
Lastly, we need to be able to send and receive Messages
=>
const { gql } = require("apollo-server");
module.exports = gql`
type Message {
id: Int!
text: String!
user: User!
channel: Channel!
}
`
Let's look at these types closely.
A Team who created it? (owner prop), who's in the team? (members prop), and what channels are associated with this team? (channels prop).
A User we need to know, which user is creating the teams and channels (email, username, password props).
A Channel which channel it is (id prop), what's the name of the channel? (name prop), will it be public or private? (public prop), what are the messages (message prop), and list the users in this channel (users prop).
A Message which message it is? (id prop), what does the message say? (text prop), which user sent this message (user prop), lastly which channel does this message belong to (channel prop)
In the end, your typeDefs.js
should look like this =>
Now, let's define our queries (GET endpoints) and mutations (POST, PUT, DELETE endpoints)
Still, inside typeDefs.js
file, let's add our queries
type Query {
getUsers: [User!]!
getMessages: [Message!]
getUser(id: Int!): User!
}
These are what I have so far. Mutations look like =>
type Mutation {
createUser(username: String!, email: String!, password: String!): User
createMessage(channel_id: Int!, text: String!): Boolean
createTeam(name: String!): Boolean
createChannel(teamId: Int!, name: String!, public: Boolean = false): Boolean
}
FYI we only need to pass the params we need to use. As you can see all of our mutations involve creating something(POST) for now.
We're just defining it now, let's actually return some real data with our resolvers starting with creating a user using the createUser
mutation.
Resolvers
Head over to the resolvers.js
file and create a createUser
mutation Remember, naming is important it needs to be the same name as the type query you defined
const bcrypt = require("bcrypt");
const { User } = require("../models");
module.exports = {
Mutation: {
createUser: async (_, args) => {
let { username, email, password } = args;
try {
// 1. Check if user exist in DB
const getUser = await User.findOne({ where: { email: email } });
if (!getUser) {
// 2. Hash user password
password = await bcrypt.hash(password, 12);
// 3. store user in DB
const user = await User.create({
username,
email,
password
});
return user;
} else {
throw Error("User already exist");
}
} catch (err) {
return err;
}
}
};
What we're doing is creating a user with the data passed to us through args (destructuring data). Were creating a user in the DB using the User model from Sequelize. I hope the rest of the comments help you get the gist of what's happening.
Let's test this endpoint in our playground. Have your server running and go to localhost:4000.
calling the createUser
mutation should look like this =>
You specify the type, either query or mutation. Then you pick the endpoint. It should return a user and create a user in the DB if successful =>
Now check the DB.
FYI ignore the first user, that was a test I did earlier. But as you can see JamesB was created with the password hashed, which is awesome.
let's create the rest of the mutations. Creating a Team, Channel, and Message.
const bcrypt = require("bcrypt");
const { Channel, Message, Team } = require("../models");
module.exports = {
Mutation: {
createChannel: async (_, args) => {
try {
await Channel.create(args);
return true;
} catch (err) {
console.log(err);
return false;
}
},
createMessage: async (_, args) => {
// const channel = Channel.findOne({where: {id: args.channel_ids}})
try {
await Message.create({
...args,
userId: 1
});
return true;
} catch (error) {
console.log(error);
return false;
}
},
createTeam: async (_, args) => {
try {
await Team.create({
...args,
owner: 1
});
return true;
} catch (error) {
console.log(error);
return false;
}
}
}
};
Now our queries =>
const bcrypt = require("bcrypt");
const { User } = require("../models");
module.exports = {
Query: {
getUsers: async () => {
try {
const users = await User.findAll();
return users;
} catch (err) {
console.log(err);
}
},
getUser: async (_, { id }) => {
try {
const user = await User.findOne({ where: { id } });
return user;
} catch (error) {
console.log(error);
}
}
},
}
Now that we have users to work with, let's get users using our getUsers
query.
it returns exactly what we said it needed to, great. In a nutshell, these are the endpoints that we will be calling from our frontend.
Overall, your typeDefs.js
file should now look like this =>
And resolvers.js
file
That's all for this one, hope that wasn't too mind-blowing. I have a few more articles to go before this series is caught up to where I'm currently at in this project. So until then, if you have any questions or if I missed anything let me know :)
Posted on September 24, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.