7. GraphQL - Node.js
GraphQL is a query language for APis and runtime to execute these queries. Unlike REST (multiple endpoints), GraphQL has a single endpoint where clients specify exactly what they want.
GraphQL vs REST
| REST | GraphQL |
It have multiple endpoints (/users, /posts, /comments) | It have single endpoint (/graphql) |
| It fetches extra data | fetch only that data that client ask for |
| Uses HTTP verbs (GET, POST, PUT, DELETE) | Uses queries, mutations, subscriptions |
| For real time data fetching it require websockets/extra setup | Bult in via subscription |
👉 REST = server decides response
👉 GraphQL = client decides response
Node.js (Apollo Server) provides GraphQL backend.
Queries (Read data)
Purpose: Fetch (read) data.
Similar to GET in REST.
Queries are read-only → they never modify the server’s state.
Mutations (Create , delete , update data)
Purpose: Modify data (create, update, delete).
Similar to POST, PUT, DELETE in REST.
Mutations change server state and usually return the modified data.
INSTALLATIONS
npm i @apollo/server graphql graphql-tag
Define Schemas in graphQL
Schemas is used to defines types and structure of API.
const { gql } = require('graphql-tag');
//type definations
const typeDefs = gql`
type User {
id: ID! # ! means required (non-nullable)
name: String!
email: String!
},
type Query{
users: [User] # get all the user
user(id: ID!):User # get particular(one) user by id
},
type Mutation{
createUser( # create user
name: String!
email:String!
): User # return our User
deleteUser(id: ID!):Boolean # we want to return boolean here
updateUser(
id: ID!
name: String # this time they are not mandotory
email: String
):User
}
`;
module.exports= typeDefs
After Schemas define - resolvers
// currently we dont have database so we assume this as a mock data
// We must create Mongoose model not used this mock data in real prjects
let users = [
{
id: "1",
name: "Ayush",
email: "ayush@example.com"
},
{
id: "2",
name: "Vansi",
email: "vansi@example.com"
},
];
// Now define resolvers here
const resolvers = {
Query:{
users:()=>users // return all users , users: asycn()=> await User.find({}) -> with mongoose model
user: (_ , {id})=> users.find(item => item.id === id) // find one user with provided id
},
Mutation:{
createUser:(_ , {name , email})=>{ //// createUser:asycn (_, args)=> {const newUser = await User.create(args) return newUser}
const newUser = {
id:String(users.length +1),
name,
email
}
users.push(newUser) // current we dont have db so push in mock data
return newUser
},
deleteUser:(_, {id})=> {
const index = users.findIndex(user => user.id === id);
if(index ===-1) return false
users.splice(index , 1)
return true
},
updateUser:(_, {id , ...updates})=>{
const index = users.findIndex(user => user.id === id);
if(index === -1) return null
const updatedUser = {
...users[index] , ...updates
}
users[index]=updatedUser
return updatedUser
}
}
}
module.exports = resolvers
Run server
// server.js
const {ApolloServer} = require('@apollo/server')
const {startStandaloneServer} = require('@apollo/server/standalone')
const typeDefs = require('./graphql/schema')
const resolvers = require('./graphql/resolvers')
async function startServer(){
// await dbConnect()
const server = new ApolloServer({
typeDefs , resolvers
});
const {url} = await startStandaloneServer(server , {
listen: {port:4000}
})
console.log(`Server running at: ${url}`)
}
startServer();
OnCLick on - http://localhost:4000 GraphQL redirect to apollo server UI where we test our graphQL Apis
// query to get all users testing
query{
users{
name
}
}
// Run above code we get only name field of all the users
{
"data": {
"users": [
{
"name": "Ayush"
},
{
"name": "Vansi"
}
]
}
}
// query to get partucular user by id
query{
user(id:"2"){
name
}
}
//output
{
"data": {
"user": {
"name": "Vansi"
}
}
}
Mutation testing results:
// pass input : createuser
mutation{
createUser(
name:"Simran",
email:"sim@gmail.com"
){
name #need only name
}
}
//output
{
"data": {
"createUser": {
"name": "Simran",
"email": "sim@gmail.com"
}
}
}
//pass input: delete User
mutation{
deleteUser(id: "4")
}
//output
{
"data": {
"deleteUser": true
}
}
//pass input: update user
mutation{
updateUser(id:"3" , name:"SimranUpdated"){
name
}
}
//output
{
"data": {
"updateUser": {
"name": "SimranUpdated"
}
}
}