In this section, we will walk through the process of building a full-stack application using GraphQL. This will involve setting up both the backend and frontend, connecting them, and ensuring they work seamlessly together.
Objectives
- Understand the architecture of a full-stack application using GraphQL.
- Set up a GraphQL server with a database.
- Create a frontend application that interacts with the GraphQL server.
- Implement CRUD operations in the full-stack application.
Prerequisites
- Basic understanding of GraphQL concepts.
- Familiarity with JavaScript/TypeScript.
- Basic knowledge of frontend frameworks (React, Vue, or Angular).
Step-by-Step Guide
- Setting Up the Backend
1.1 Initialize the Project
First, create a new directory for your project and initialize a Node.js project.
1.2 Install Dependencies
Install the necessary dependencies for setting up a GraphQL server.
1.3 Create the Server
Create a file named server.js
and set up a basic Express server with GraphQL.
const express = require('express'); const { graphqlHTTP } = require('express-graphql'); const { buildSchema } = require('graphql'); const mongoose = require('mongoose'); // Connect to MongoDB mongoose.connect('mongodb://localhost:27017/fullstack', { useNewUrlParser: true, useUnifiedTopology: true }); // Define a schema const schema = buildSchema(` type Query { hello: String } `); // Define a root resolver const root = { hello: () => 'Hello world!', }; // Create an Express app const app = express(); // Use graphqlHTTP middleware app.use('/graphql', graphqlHTTP({ schema: schema, rootValue: root, graphiql: true, })); // Start the server app.listen(4000, () => { console.log('Server is running on http://localhost:4000/graphql'); });
1.4 Define the Schema and Resolvers
Expand the schema to include more complex types and resolvers.
const schema = buildSchema(` type User { id: ID! name: String! email: String! } type Query { users: [User] user(id: ID!): User } type Mutation { createUser(name: String!, email: String!): User } `); const User = mongoose.model('User', new mongoose.Schema({ name: String, email: String, })); const root = { users: () => User.find(), user: ({ id }) => User.findById(id), createUser: ({ name, email }) => { const user = new User({ name, email }); return user.save(); }, };
- Setting Up the Frontend
2.1 Initialize the Frontend Project
Create a new directory for the frontend and initialize a React project.
2.2 Install Apollo Client
Install Apollo Client to interact with the GraphQL server.
2.3 Set Up Apollo Client
Create a file named ApolloClient.js
to configure Apollo Client.
import { ApolloClient, InMemoryCache, ApolloProvider } from '@apollo/client'; const client = new ApolloClient({ uri: 'http://localhost:4000/graphql', cache: new InMemoryCache() }); export default client;
2.4 Create React Components
Create components to fetch and display data from the GraphQL server.
// src/App.js import React from 'react'; import { ApolloProvider } from '@apollo/client'; import client from './ApolloClient'; import Users from './Users'; function App() { return ( <ApolloProvider client={client}> <div className="App"> <h1>Users</h1> <Users /> </div> </ApolloProvider> ); } export default App; // src/Users.js import React from 'react'; import { useQuery, gql } from '@apollo/client'; const GET_USERS = gql` query GetUsers { users { id name email } } `; function Users() { const { loading, error, data } = useQuery(GET_USERS); if (loading) return <p>Loading...</p>; if (error) return <p>Error :(</p>; return ( <ul> {data.users.map(user => ( <li key={user.id}> {user.name} - {user.email} </li> ))} </ul> ); } export default Users;
- Implementing CRUD Operations
3.1 Create User Component
Add a form to create new users.
// src/CreateUser.js import React, { useState } from 'react'; import { useMutation, gql } from '@apollo/client'; const CREATE_USER = gql` mutation CreateUser($name: String!, $email: String!) { createUser(name: $name, email: $email) { id name email } } `; function CreateUser() { const [name, setName] = useState(''); const [email, setEmail] = useState(''); const [createUser] = useMutation(CREATE_USER); const handleSubmit = (e) => { e.preventDefault(); createUser({ variables: { name, email } }); }; return ( <form onSubmit={handleSubmit}> <input type="text" placeholder="Name" value={name} onChange={(e) => setName(e.target.value)} /> <input type="email" placeholder="Email" value={email} onChange={(e) => setEmail(e.target.value)} /> <button type="submit">Create User</button> </form> ); } export default CreateUser;
3.2 Integrate CreateUser Component
Integrate the CreateUser
component into the main application.
// src/App.js import React from 'react'; import { ApolloProvider } from '@apollo/client'; import client from './ApolloClient'; import Users from './Users'; import CreateUser from './CreateUser'; function App() { return ( <ApolloProvider client={client}> <div className="App"> <h1>Users</h1> <CreateUser /> <Users /> </div> </ApolloProvider> ); } export default App;
- Running the Application
4.1 Start the Backend Server
Run the backend server.
4.2 Start the Frontend Application
Run the frontend application.
- Conclusion
In this section, we have built a full-stack application using GraphQL. We set up a GraphQL server with a MongoDB database, created a React frontend to interact with the server, and implemented basic CRUD operations. This foundational knowledge can be expanded to build more complex and feature-rich applications.
Exercises
-
Add Update and Delete Operations:
- Extend the GraphQL schema to include mutations for updating and deleting users.
- Implement the corresponding resolvers.
- Create React components to handle these operations.
-
Pagination:
- Implement pagination in the
Users
component to handle large datasets.
- Implement pagination in the
-
Authentication:
- Add authentication to the GraphQL server and protect certain queries and mutations.
Solutions
-
Update and Delete Operations:
Schema:
type Mutation { createUser(name: String!, email: String!): User updateUser(id: ID!, name: String, email: String): User deleteUser(id: ID!): User }
Resolvers:
const root = { users: () => User.find(), user: ({ id }) => User.findById(id), createUser: ({ name, email }) => { const user = new User({ name, email }); return user.save(); }, updateUser: ({ id, name, email }) => User.findByIdAndUpdate(id, { name, email }, { new: true }), deleteUser: ({ id }) => User.findByIdAndRemove(id), };
React Components:
- Create similar components for
UpdateUser
andDeleteUser
as shown forCreateUser
.
- Create similar components for
-
Pagination:
- Modify the
GET_USERS
query to accept pagination parameters and update theUsers
component to handle pagination.
- Modify the
-
Authentication:
- Use libraries like
jsonwebtoken
to add authentication middleware to the GraphQL server and protect specific queries and mutations.
- Use libraries like
By completing these exercises, you will gain a deeper understanding of building full-stack applications with GraphQL and be better prepared to handle real-world scenarios.