What is apollo server
Apollo Server is a community-driven, open-source GraphQL server that simplifies the process of building and serving GraphQL APIs in JavaScript environments. It's often used with Node.js, but it can also be used with other JavaScript runtime environments. Apollo Server is part of the larger Apollo platform, which includes tools for both server and client development.
Installation
google “apollo GraphQL” and open the website. Navigate to developers → docs or simply open this link https://www.apollographql.com/docs/
and scroll down to this section and read few things along the way if you want to.
than click on the highlighted part and which will take you to the following page. this has information on how to install the server
or you can visit this page directly Link
Setting up the Server
create a folder called server navigate inside the folder and run the following commands:
use this command to install apollo server and GraphQL
yarn add @apollo/server graphql
npm install @apollo/server graphql
add body parser, cors, axios, @type/axios as well to your project
yarn add body-parser cors axios @type/axios
npm install body-parser cors axios @types/axios
body-parser
Role:
body-parser
is a middleware used in Express.js (a popular Node.js web framework) for parsing incoming request bodies.Context with GraphQL: In a GraphQL server,
body-parser
is often used to parse incoming HTTP POST requests containing GraphQL queries and mutations in the request body. It's responsible for extracting the JSON payload from the request body so that the GraphQL server can process the query.
axios
Role:
axios
is a popular JavaScript library for making HTTP requests, both in the browser and on the server-side. It provides a convenient way to send HTTP requests and handle responses.Context with GraphQL: In the context of GraphQL,
axios
can be used in client-side applications to send GraphQL queries and mutations to a GraphQL server over HTTP. It can also be used to handle responses and manage GraphQL API interactions.
CORS (Cross-Origin Resource Sharing)
Role:
CORS
is a security feature implemented by web browsers that controls and manages cross-origin requests. It defines how a web server can allow or restrict access to its resources by web pages from other domains.Context with Web Development: In the context of web development,
CORS
plays a crucial role in enhancing security by preventing unauthorized access to resources from different origins (domains). It permits or restricts cross-origin HTTP requests made by client-side scripts in web applications.
Usage
create a filed called index.js
and import the following modules
const express = require('express')
const {expressMiddleware} = require('@apollo/server/express4')
const {ApolloServer} = require('@apollo/server')
const bodyParser = require('body-parser')
const cors = require('cors')
const { default: axios } = require('axios')
Next we’ll create an async function named startServer which will have all the code to start and configure our server
async function startServer(){
const app = express()
const server = new ApolloServer({})
const port = 8000
// using middle ware
app.use(bodyParser.json())
app.use(cors())
await server.start()
app.use("/graphql", expressMiddleware(server))
app.listen(port,()=> console.log("server started on " + port))
}
startServer()
in the above code we initialize express and apollo server. Set body parser and cors as middleware and start the server. Finally we tell express if any request comes on /graphql
we serve it by using apollo server and we start listening on desired port. what this will do is whenever we enter localhost:8000 on our browser we’ll get the apollo server UI and we can make use of its functionalities.
But if you run this code now it WON’T WORK and you’ll get an error. which says
Error: Query root type must be provided.
there are two more important concepts used when dealing with GraphQL.
type definitions
resolvers
In GraphQL, both type definitions (often referred to as "typeDefs") and resolvers are key components that define the structure and behavior of a GraphQL API. They are used to specify the schema and how data is fetched and manipulated. Let's dive into each of these components:
Type Definitions (typeDefs):
Type definitions, or "typeDefs" define the structure and shape of the GraphQL schema. They specify the types of data that can be queried and mutated in your GraphQL API. Type definitions are typically written in the GraphQL Schema Definition Language (SDL), which is a human-readable way to define types, queries, mutations, and relationships.
Here's a basic example of type definitions for a simple GraphQL schema:
type Query {
hello: String
getUser(id: ID!): User
}
type User {
id: ID
username: String
email: String
}
In this example:
We define a
Query
type with two fields:hello
andgetUser
.The
User
type has three fields:id
,username
, andemail
.
Type definitions provide the structure for the available data in your GraphQL API. They serve as the contract between the client and server, outlining what data can be requested. we’ll see later how can we request data from the apollo server UI.
Resolvers
Resolvers are JavaScript functions that define how to retrieve or manipulate the data for the fields defined in the type definitions. Each field in the type definitions corresponds to a resolver function. Resolvers are organized by the types they resolve, and they return the actual data when queried.
Here's an example of resolvers for the type definitions mentioned earlier:
const resolvers = {
Query: {
hello: () => 'Hello, world!',
getUser: (parent, args) => {
// In a real application, you might fetch the user data from a database or another source
const { id } = args;
return fetchUserDataById(id);
},
},
User: {
id: (user) => user.id,
username: (user) => user.username,
email: (user) => user.email,
},
};
In this example:
The
hello
field in theQuery
type is resolved by a function that returns the string'Hello, world!'
.The
getUser
field in theQuery
type is resolved with a function that takes anid
argument and fetches user data by that ID from a data source (e.g., a database).
Resolvers are crucial for defining how data is fetched, transformed, or computed. They connect your GraphQL schema to the actual data sources and services.
Together, type definitions and resolvers form the backbone of a GraphQL API. The type definitions define the available data and operations, while the resolvers determine how that data is fetched and presented to the client. This clear separation of concerns and self-documenting schema makes GraphQL powerful and flexible for building APIs.
Now that you have good understanding of how type Definitions and Resolvers work, we’ll fix the error which we were getting earlier by entering these two things i our code (index.js).
const express = require('express')
const {expressMiddleware} = require('@apollo/server/express4')
const {ApolloServer} = require('@apollo/server')
const bodyParser = require('body-parser')
const cors = require('cors')
const { default: axios } = require('axios')
async function startServer(){
const app = express()
const server = new ApolloServer({
typeDefs:`
type Todo {
id: ID!
title: String!
completed: Boolean
userId: ID!
user: User
}
type User {
id: ID!
name: String!
username: String!
email: String!
phone: String!
website: String!
}
type Query {
getTodos: [Todo]
getAllUsers: [User]
getUser(id:ID): User
}
` ,
resolvers: {
// todo is the parent which graph QL passes to the function by default
Todo:{
user: async (todo) => (await axios.get(`https://jsonplaceholder.typicode.com/users/${todo.userId}`)).data
},
Query: {
getTodos: async () => (await axios.get('https://jsonplaceholder.typicode.com/todos')).data,
getAllUsers: async () => (await axios.get('https://jsonplaceholder.typicode.com/users')).data,
getUser: async (parent, {id}) => (await axios.get(`https://jsonplaceholder.typicode.com/users/${id}`)).data,
}
}
})
const port = 8000
// using middle ware
app.use(bodyParser.json())
app.use(cors())
await server.start()
app.use("/graphql", expressMiddleware(server))
app.listen(port,()=> console.log("server started on " + port))
}
startServer()
As you can see in the above code type definitions and resolvers go inside an object which is passed to Apollo Server instance. we area also using axios here to fetch data from an free API. This is the complete code you need to run apollo server using GraphQL.
Requesting data
We just saw how we can configure the server using type definitions and resolvers to serve the data but we haven’t seen yet how the you can request data. GraphQL has an API using which we can send GraphQL queries and request for data. In this post we'll see how to send request to Apollo server using Apollo UI for testing the API.
GraphQL query
In GraphQL, a query is a request made by a client to retrieve specific data from a GraphQL API. Queries are one of the fundamental building blocks of GraphQL and serve as a way for clients to specify exactly what data they need, and in what shape, from the API. Here are the key characteristics of GraphQL queries:
Structured Data Retrieval: In GraphQL, a query is a structured request for data. It allows clients to specify the fields and relationships they want to retrieve, making it highly efficient for data retrieval.
Declarative Syntax: GraphQL queries are written in a declarative syntax, where clients declare what they want, and the server responds with exactly that data, and nothing more.
Hierarchical Structure: Queries have a hierarchical structure, mirroring the shape of the data that's returned. Clients can request nested data structures to retrieve related data efficiently.
Strongly Typed: GraphQL is a strongly typed query language. It requires the client to specify the types and fields they want to query, and it enforces a strict schema that defines what is available for retrieval.
Single Request: Unlike traditional REST APIs that might require multiple requests to different endpoints to assemble the required data, GraphQL typically requires a single request to retrieve all the requested data.
No Over-fetching or Under-fetching: With GraphQL queries, clients can avoid over-fetching (retrieving more data than needed) and under-fetching (making multiple requests for related data) issues, optimizing data transfer.
Here's an example of a simple GraphQL query:
query Query{
user(id: 123) {
name
email
posts {
title
body
}
}
}
Breaking down the query:
query
is the keyword that signals the beginning of a query operation.user(id: 123)
is a field that represents a user query with anid
argument.name
andemail
are fields on theuser
object, specifying the data the client wants to retrieve about the user.posts
is a field that represents a query for the user's posts.title
andbody
are fields on theposts
object, specifying the data the client wants to retrieve about each post.
The response to this query will be structured in the same hierarchical way as the query, providing the requested user data and their posts with the specified fields.
GraphQL queries allow clients to be very precise in requesting the data they need, reducing over-fetching and minimizing the number of API requests. This flexibility and efficiency are some of the key advantages of GraphQL over traditional REST APIs.
So now we understand how GraphQL works at the server side and client side. we know how a GraphQL query can be structured and sent to the server. So now we’ll see some examples of how this works using apollo UI
Examples
simple query
here you can see the apollo UI which lets us test our API. You can see in the operations tab we have entered our query and to our right is the response tab. lets look at the query now.
query Query{
getTodos {
id
title
}
}
on line one first we have entered the keyword query followed by ‘Query’ this is just a name which i choose you can replace it with anything else. This is the main block inside which we can specify our queries
on line 2 we type getTodos. this cannot be named as we like because we have specified this type in our TypeDefs and also we have specified a resolver for this. Whatever key/type we put inside the main block should be present inside the Query root on the server side.
TypeDefs at server
typeDefs:` type Todo { id: ID! title: String! completed: Boolean userId: ID! user: User } type User { id: ID! name: String! username: String! email: String! phone: String! website: String! } type Query { getTodos: [Todo] getAllUsers: [User] getUser(id:ID): User } ` ,
Here we can see that we have mentioned getTodos in the Query root and its suppose to return a list of Todos. Now look at the type for Todos and we can see that it can return
id
,title
,completed
,userId
,user
So let us look back at the query. in the query we can see that inside getTodos we are specifying the id and title. So we are telling GraphQL for every todo we want only Id and title nothing else and if you look at the response you can see that we have gotten exactly what we have asked for
This shows how GraphQL only sends what we have asked for.
Multiple queries
Here we have also added getUser type and as you can observer we are getting list of Todos as well as response for getUser.
You may also observe that there is id passed to get user that is because the get user resolver is defined in such a way that it takes an argument. you can look into index.js file
So we can see that in single request we can fetch data from two different places
Given arguments through variables
- sometimes you may want to pass the value to a type through a variable and not hard code the value directly into the query.
you can use the below syntax to pass the value as a variable
query Query($getUserId: ID) {
getUser(id: $getUserId) {
id
email
}
}
Nested Queries
sometimes we may want to nest queries and access the value of the nested object(type).
here we are fetching the all the Todos using getTodos and accessing the user type for each todo which which will be returned by getTodos. please check the resolver definition inside the code of index.js to check how its done.
This is how we can make nested queries and get all the required info in single request to the server
So this is it!! These are all the things you need to get started with GraphQL and understand how it works. I know its a long and detailed post and it took me a while to finish it. Soon I'll be posting a new blog explaining how to use GraphQL in Front end to so you can make queries programmatically. Hope you liked this post and it added some value to your coding journey. See you in the next one!