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
Userscomponent 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
UpdateUserandDeleteUseras shown forCreateUser.
- Create similar components for
-
Pagination:
- Modify the
GET_USERSquery to accept pagination parameters and update theUserscomponent to handle pagination.
- Modify the
-
Authentication:
- Use libraries like
jsonwebtokento 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.
