So what is GraphQL exactly?
- A query language for your API.
- GraphQL gives the power to ask for exactly what we need and nothing more.
- Get as many as resources in a single request.
- Evolve your API's without versions.
- GraphQL makes it easy to build powerful tools like GraphiQL by leveraging your APIβs type system.
Concepts of GraphQL
- Queries & Mutations - GraphQL queries are so much easier to request data than a REST API.
- Schema & Types, Variables, Arguments - GraphQL has its own schema & type system which we are already familiar with (
String
,Int
,[]
etc.). - Resolver - is responsible for mapping the query to a function.
- Validation - By using the type system, it is easy to determine whether a GraphQL query is valid or not.
- Execution - After being validated, a GraphQL query is executed by a GraphQL server which returns a result that mirrors the shape of the requested query, typically as JSON.
- Introspection - It's often useful to ask a GraphQL schema for information about what queries it supports.
Steps to run the demo
1. Clone the repo
git clone https://github.com/gokulkrishh/introduction-to-graphql
2. Install dependencies
yarn or npm install
3. Run
yarn or npm start
Demo
- For local server open localhost:3000 in your browser.
- Demo using GraphQL API.
- Demo GraphQL Server.
Libraries I used for demo
- GraphQL.
- Express server.
- GraphQL HTTP Server Middleware.
- apollo-fetch for making fetch requests for demo.
1. Variables, Arguments & Types
Like any other programming language, GraphQL has variables
, arguments
. Let's see some examples.
Types
The most basic components of a GraphQL schema are object types, which just represent a kind of object you can fetch from your service, and what fields it has. If you are a web developer, you can relate this to flow or typescript.
Example:
type Person {
name: String!
}
String!
-name
property is a non-nullable string. Meaning you will always give a value to this property.- More types.
Arguments
We can pass arguments to any query.
Example:
query user {
getUser(id: 1) {
name
age
gender
picture
}
}
Variables
So far, we have been writing all of our arguments inside the query string. But in most applications, the arguments to fields will be dynamic.
Example:
{
"userId": 1
}
query:
query user($id: Int!) {
getUser(id: $id) {
name
age
gender
picture
about
}
}
2. Queries (GET API's)
1. What is better than a Hello World π€ͺ
query helloworld {
hello
}
Result:
{
"data": {
"hello": "Hello World"
}
}
2. To get all the users from dummy JSON.
query getAllUsers {
getUsers {
name
age
gender
picture
}
}
Resolver getUsers:
const getUsers = (args) => {
const { gender } = args;
if (gender) return users.filter((user) => user.gender === gender);
else return users;
};
Result:
{
"data": {
"users": [
{
"name": "Price Weber",
"age": 37,
"gender": "male",
"picture": "http://placehold.it/32x32"
},
{
"name": "Pennington Parsons",
"age": 22,
"gender": "male",
"picture": "http://placehold.it/32x32"
},
{
"name": "Yesenia Galloway",
"age": 36,
"gender": "female",
"picture": "http://placehold.it/32x32"
}
]
}
}
3. To get a single user based on an id.
query user {
getUser(id: 1) {
name
age
gender
picture
}
}
Resolver getUser:
export const getUser = (args) => {
const { id } = args;
const user = users.filter((user) => user.id === id);
if (user.length === 1) return user[0];
else return `User not found for the id ${id}`;
};
Result:
{
"data": {
"user": {
"name": "Price Weber",
"age": 37,
"gender": "male",
"picture": "http://placehold.it/32x32"
}
}
}
3. Mutations
Most discussions of GraphQL focus on data fetching, but any complete data platform needs a way to modify server-side data as well. It is analogous to performing HTTP verbs such as POST
, PATCH
, and DELETE
. Just like queries, mutation should have mutation
instead of query
with some id or something.
Examples: open localhost:3000/graphql to try the below.
Create a new user: (POST API π€ͺ)
variables:
{
"name": "JEDI",
"age": 25,
"gender": "male"
}
mutation:
mutation user($name: String!, $age: Int!, $gender: String) {
createUser(name: $name, age: $age, gender: $gender) {
name
age
gender
}
}
Resolver for createUser:
const createUser = (args) => {
const { name, age, gender } = args;
const user = users.filter((user) => user.name === name); // users from DB
if (user.length === 0) {
return user; // Save in DB and return
} else return `A user with that name already exists.`;
};
Result:
{
"data": {
"createUser": {
"name": "JEDI",
"age": 25,
"gender": "male"
}
}
}
Update existing user details: (PUT API π)
variables:
{
"id": 1,
"name": "JEDI π",
"age": 26
}
mutation:
mutation user($name: Int!, $name: String!) {
updateUser(name: $name, age: $age, gender: $gender) {
name
age
}
}
Resolver for updateUser:
const updateUser = (args) => {
const { id, name, age, gender } = args;
const user = users.filter((user) => user.id === id);
if (user.length === 1) {
return user; // Save the updates in DB and return
} else return `User doesn't exist for id ${id}.`;
};
Result:
{
"data": {
"updateUser": {
"name": "JEDI π",
"age": 25
}
}
}
Delate a user: (DELETE API π)
variables:
{
"id": 1
}
mutation:
mutation user($id: Int!) {
deleteUser(id: $id) {
id
name
age
gender
}
}
Resolver deleteUser:
const deleteUser = (args) => {
const { id } = args;
const user = users.filter((user) => user.id === id);
if (user.length === 1) {
return user; // Delete from DB and return user or return ok
} else return `User doesn't exist for id ${id}.`;
};
Result:
{
"data": {
"deleteUser": {
"id": 1,
"name": "Price Weber",
"age": 37,
"gender": "male"
}
}
}
4. Test Cases for GraphQL
If are wondering how to write test cases for GraphQL. Here is an example for you starWarsValidation-test.js.
5. Limitations of GraphQL
- Specific Response Structure may be required - In GraphQL the response matches the shape of the query, so if you need to respond in a very specific structure, you'll have to add a transformation layer to reshape the response.
- Handling File Upload - There is nothing about file upload in the GraphQL specification and mutations doesnβt accept files in the arguments.
- Cache at Network Level - Because of the common way GraphQL is used over HTTP (A POST in a single endpoint), cache at network level becomes hard. A way to solve it is to use Persisted Queries.
- Rate Limiting - Limiting the API call's to the particular query is the problem in GraphQL. Github recently introduced GraphQL with the different approach to solving this issue. Take a look here.
Thanks for reading. Share this post if you liked it.
Helpful links
- Best practices for GraphQL - Serving over HTTP, Pagination, Caching etc.
- Running an Express GraphQL Server
- GraphQL vs REST.
- Apollo-Fetch - Handle all POST fetch calls as normal fetch API (See demo folder for more).