Welcome folks today in this blog post we will be building a react.js apollo client
project where we will be building a graphql crud api server using express.js and mutations. All the full source code of the project is shown below.
Get Started
In order to get started you need to make the project folder and then inside that you need to create two folders for frontend
and backend
as shown below
mkdir reactapollo
cd reactapollo
mkdir frontend
mkdir backend
Making the GraphQL Backend API in Express
Now we will be building the graphql
backend api using express and apollo. For this first of all we will be building a node.js project by executing the below commands as shown below
npm init -y
This will create the package.json file and after that we will install the dependencies for this project
npm i express
npm i cors
npm i express-graphql
npm i graphql
npm i graphql-yoga
npm i mongoose
npm i apollo-server-express
Now Here we have installed the express package to make the web server and also we are installing the mongoose library for storing the data into database. And also we are installing the apollo server package.
And also inside your package.json file we need to add the type
parameter whose value will be module
as shown below
Now make the index.js
file and copy paste the following code
index.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
import express from "express"; import { ApolloServer } from "apollo-server-express"; import cors from "cors"; const app = express(); const port = 8080; app.use(express.json()); // body parser app.use(cors()); // app.listen(port, () => { console.log( `App is listening on http://localhost:${port}${server.graphqlPath}` ); }); |
In the very first step we are importing the express library and also including some important middlewares such as cors for avoiding the cors error. And also we are starting the express app at port 8080.
Connect to MongoDB Database
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
import express from "express"; import mongoose from "mongoose"; import cors from "cors"; const dbName = "library"; const uri = `mongodb://localhost:27017/${dbName}`; mongoose.Promise = global.Promise; mongoose.connect(uri, { useNewUrlParser: true }); const app = express(); const port = 8080; app.use(express.json()); // body parser app.use(cors()); // app.listen(port, () => { console.log( `App is listening on http://localhost:${port}${server.graphqlPath}` ); }); |
Here we are connecting to Mongodb database using the mongoose library. Here we are providing the database name and also we are providing the mongodb uri where we specify the port number of mongodb which is 27017 followed by db name.
Including the Apollo Client in Express
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
import express from "express"; import mongoose from "mongoose"; import { ApolloServer } from "apollo-server-express"; import cors from "cors"; import { typeDefs } from "./schema.js"; import { resolvers } from "./resolvers.js"; const dbName = "library"; const uri = `mongodb://localhost:27017/${dbName}`; mongoose.Promise = global.Promise; mongoose.connect(uri, { useNewUrlParser: true }); const app = express(); const port = 8080; const server = new ApolloServer({ typeDefs, resolvers }); server.applyMiddleware({ app }); app.use(express.json()); // body parser app.use(cors()); // 필수 app.listen(port, () => { console.log( `App is listening on http://localhost:${port}${server.graphqlPath}` ); }); |
Here as you can see we are applying the apollo client middleware to the express app. At the starting we are including the apollo library. And inside the constructor of Apollo Server we are passing two things which is typedef and resolvers. Now we need to create these two files in the next step.
Making Schema & Mutations of GraphQL API
Now we will be making the schema.js
file which will contain the schema of the graphql api. This schema will include which data you will be using for the graphql api
schema.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
import { gql } from "apollo-server-express"; export const typeDefs = gql` type User { _id: ID! name: String! age: Int! gender: String! } type Query { getUser(_id: ID!): User allUser: [User] } input UserInput { name: String! age: Int! gender: String! } type Mutation { createUser(input: UserInput): User updateUser(_id: ID!, input: UserInput): User deleteUser(_id: ID!): User } `; |
Here as you can see we are defining the schema and mutations for the apollo graphql api. And the mutations are createUser for creating a new user, updateUser which will update the exisiting user, deleteUser which will delete the user. We have a User object which will contain the name,age and gender fields.
Making Resolvers for GraphQL API
Now we will be making the resolvers for the schema of graphql api. Here we will defining all the functions which will modify the schema and data.
resolver.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
import User from "./models/user.js"; export const resolvers = { Query: { async getUser(root, { _id }) { return await User.findById(_id); }, // new async allUser() { return await User.find(); } }, Mutation: { async createUser(root, { input }) { return await User.create(input); }, async updateUser(root, { _id, input }) { return await User.findOneAndUpdate({ _id }, input, { new: true }); }, async deleteUser(root, { _id }) { return await User.findOneAndDelete({ _id }); } } }; |
As you can see we have defined all the four CRUD Methods which will actually create,delete,Update and read all the records from the mongodb database using mongoose. These mutations are very much helpful in graphql apollo api. And also we are importing the Models of mongodb database. So now we need to make a new model folder and inside it we need to make a User.js file and copy paste the following code
models/User.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// ./models/User.js import mongoose from 'mongoose'; const Schema = mongoose.Schema; const UserSchema = new Schema({ name: { type: String, required: true }, age: { type: Number }, gender: { type: String } }); export default mongoose.model('user', UserSchema); |
So in this block of code we are importing the mongoose library. And also we are defining the schema of mongodb database. Also we are defining the datatypes of each field. Lastly we are exporting the model and the schema.
Now if you open the localhost:8080/graphql
we will see the following interface to create mutations and also request some data from the graphql api.
As you can see we have a fully fledged graphql apollo server running on port 8080 and here we can perform any filtering and mutations of graphql api.
Making the React.js Frontend to Consume GraphQL API
Now we need to create a brand new react.js project inside the frontend folder to consume this graphql api and build a crud frontend. The commands are shown as follows
npx create-react-app apollographql
cd apollographql
Now we will be installing the dependencies which are required for this react.js project
npm i apollo-boost
npm i graphql
npm i graphql-tag
npm i react-apollo
As you can see we are installing the apollo client package for react.js and also graphql packages as well for making http requests to graphql backend api using apollo client.
Initializing Apollo Client in React.js
First of all modify the index.js
file of your react.js project and including the below code to initialize the Apollo Client in React.js
index.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; import { ApolloProvider } from 'react-apollo'; import ApolloClient from 'apollo-boost'; const client = new ApolloClient({ uri: 'http://localhost:8080/graphql' }) ReactDOM.render( <ApolloProvider client={client}> <App /> </ApolloProvider> , document.getElementById('root')); |
Now inside the App.js
file you need to copy paste the below code.
App.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
import React from "react"; import ReadUser from "./components/ReadUser"; import CreateUser from "./components/CreateUser"; import UpdateUser from "./components/UpdateUser"; import DeleteUser from "./components/DeleteUser"; const App = () => ( <div> <CreateUser /> <UpdateUser /> <DeleteUser /> <h2>GraphQL + MongoDB CRUD</h2> <ReadUser /> </div> ); export default App; |
Here we are including the four components required for the crud operations using the graphl api. So now we need to create this components in the next step.
Now guys you need to create a brand new components folder and inside it create the files.First of all we will be reading all the records from the database in ReadUser.js and the code is shown below
components/ReadUser.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
import React from "react"; import { Query } from "react-apollo"; import gql from "graphql-tag"; export const ALL_USER = gql` query { allUser { _id name age gender } } `; const ReadUser = () => ( <Query query={ALL_USER}> {({ loading, error, data }) => { if (loading) return <p>Loading...</p>; if (error) return <p>error</p>; return data.allUser.map(({ _id, name, age, gender }) => ( <div key={_id} onClick={e => { e.preventDefault(); window.location.pathname = _id; }} > id: {_id} / name: {name} / age: {age} / gender: {gender} </div> )); }} </Query> ); export default ReadUser; |
Here we are defining the actual mutation of readUser which basically uses the map operator to loop through all the records present inside database and showing it on browser. Here we are calling the Query for mutations inside graphql apollo client to make this request.And lastly we are printing out all the details of a certain user in browser. So now if you open the react.js project at port 3000 you will see all the users details printed
Now we need to create the new component which is called as createUser.js which is actually to insert a new record inside mongodb database and we will be using the mutation createUser as shown below
components/CreateUser.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
import React, { Component } from "react"; import { Mutation } from "react-apollo"; import { ALL_USER } from "./ReadUser"; import gql from "graphql-tag"; const CREATE_USER = gql` mutation createUser($name: String!, $age: Int!, $gender: String!) { createUser(input: { name: $name, age: $age, gender: $gender }) { _id name age gender } } `; export class CreateUser extends Component { constructor(props) { super(props); this.state = { name: "", age: 0, gender: "" }; } render() { return ( <Mutation mutation={CREATE_USER} update={(cache, { data }) => { // 캐시 업데이트를 해서 글 목록이 바로 보이게 하기 const { allUser } = cache.readQuery({ query: ALL_USER }); cache.writeQuery({ query: ALL_USER, data: { allUser: allUser.concat([data.createUser]) } }); }} > {(createUser, { data, loading, error }) => { if (loading) return <p>loading...</p>; if (error) return <p>error...</p>; return ( <div> <form onSubmit={async e => { e.preventDefault(); const { name, age, gender } = this.state; await createUser({ variables: { name, age, gender } }); this.setState({ name: "", age: 0, gender: "" }); }} > <h1> CreateUser </h1> <input onChange={e => this.setState({ name: e.target.value })} placeholder="name" type="text" value={this.state.name} /> <input onChange={e => this.setState({ age: parseInt(e.target.value) }) } placeholder="age" type="number" value={this.state.age} /> <input onChange={e => this.setState({ gender: e.target.value })} placeholder="gender" type="text" value={this.state.gender} /> <input disabled={ !this.state.name || !this.state.age || !this.state.gender } type="submit" value="Create" /> </form> </div> ); }} </Mutation> ); } } export default CreateUser; |
Here guys we have the simple form to create a new User and insert that data inside mongodb database. After the form submit button is clicked it is calling a createUser mutation to actually interact with the backend where mongoose is directly interacting with the mongodb database to store the actual record. If you open the app you will see this actual form
As you can see we have three fields name,age and gender and a create button which is disabled by default.
And Now we will create the updateUser.js Component to actually update the existing user as shown below
components/UpdateUser.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
import React, { Component } from "react"; import gql from "graphql-tag"; import { Mutation } from "react-apollo"; const UPDATE_USER = gql` mutation updateUser($_id: ID!, $name: String!, $age: Int!, $gender: String!) { updateUser(_id: $_id, input: { name: $name, age: $age, gender: $gender }) { _id name age gender } } `; class UpdateUser extends Component { constructor(props) { super(props); this.state = { _id: "", name: "", age: 0, gender: "" }; } componentDidMount() { const id = window.location.pathname.split("/")[1]; this.setState({ _id: id }); } render() { return ( <Mutation mutation={UPDATE_USER}> {(updateUser, { data, loading, error }) => { if (loading) return <p>loading...</p>; if (error) return <p>error...</p>; return ( <div> <form onSubmit={async e => { e.preventDefault(); const { _id, name, age, gender } = this.state; await updateUser({ variables: { _id, name, age, gender } }); this.setState({ _id: "", name: "", age: 0, gender: "" }); window.location.pathname = "/"; }} > <h1> UpdateUser </h1> <h3>_id : {this.state._id}</h3> <input onChange={e => this.setState({ name: e.target.value })} placeholder="name" type="text" value={this.state.name} /> <input onChange={e => this.setState({ age: parseInt(e.target.value) }) } placeholder="age" type="number" value={this.state.age} /> <input onChange={e => this.setState({ gender: e.target.value })} placeholder="gender" type="text" value={this.state.gender} /> <input type="submit" disabled={ !this.state.name || !this.state.age || !this.state.gender } value="Update" /> </form> </div> ); }} </Mutation> ); } } export default UpdateUser; |
Now as you can see we have another form this time it is for updating an existing record which is already present inside mongodb database. Here we are using the updateUser mutation when the form submits. This updateUser mutation takes all the fields values name,age and gender and based upon the changes updates the record. The update form looks like this as shown below
As you can see we are updating the records by using it’s unique id which is auto generated in mongodb database.
Now we will be making deleteUser component which actually deletes a record using unique id which is again generated by mongodb database. The deleteUser mutation is really simple here we will simply delete the record and no complex operations involved.
components/DeleteUser.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
import React, { Component } from "react"; import { Mutation } from "react-apollo"; import gql from "graphql-tag"; const DELETE_USER = gql` mutation deleteUser($_id: ID!) { deleteUser(_id: $_id) { _id name age gender } } `; class DeleteUser extends Component { constructor(props) { super(props); this.state = { _id: "" }; } componentDidMount() { const id = window.location.pathname.split("/")[1]; this.setState({ _id: id }); } render() { return ( <Mutation mutation={DELETE_USER}> {(deleteUser, { loading, error, data }) => { if (loading) return <p>Loading...</p>; if (error) return <p>error...</p>; return ( <div> <h1>DeleteUser</h1> _id: {this.state._id} <button onClick={async e => { e.preventDefault(); const { _id } = this.state; await deleteUser({ variables: { _id } }); window.location.pathname = "/"; }} > Delete User </button> </div> ); }} </Mutation> ); } } export default DeleteUser; |
As you can see guys here we have a simple button of delete with every record. If we click that button then that record will be deleted from the mongodb database. And we are deleting it using deleteUser mutation which takes the id of the record to be deleted. The delete User form looks like this
This wraps up the application guys. Hopefully you like the project. Thanks for taking the time and reading this post. Please share this post if you like it and bookmark this website for future programming and web development tutorials