How GraphQL Solves Under Fetching

An HTTP Rest API is the traditional way of fetching data from the server, for each endpoints, it has a fixed contract, which will be returned as the response of the API Call. in some scenarios, a single API response is enough, but for some clients, more data is required, hence another API call is required. To get all the data, the client has to make multiple API calls, which results in slower page response, this is also called as Under-fetching, now let's see How GraphQL Solves Under Fetching

GraphQL Solves Under Fetching by allowing an API consumer to combine multiple queries at a time and return everything in a single Response.

GraphQL provides API consumers an option to request what is required as the below API query

query FindAppleByAppleId {
    findAppleByAppleId(appleId: 1) {
        appleId
        appleName
        available
        price
    }
    findVendorByAppleId(appleId: 1) {
        vendorId
        appleId
        vendorName
        available
    }
    findStoreByAppleId(appleId: 1) {
        storeId
        appleId
        storeName
        storeAddress
    }
}

 Let's understand How GraphQL Solves Under Fetching with an Example

The below program provides details of Apple by taking appleId, it provides 3 REST API endpoints for each type of response, /findAppleByAppleId for Apple details, /findVendorByAppleId for Vendor and /findStoreByAppleId for Store details.

Rest API End Point Response Object API Response
findAppleByAppleId Apple
{
    "appleId": 1,
    "appleName": "FUJI",
    "available": false,
    "price": 1.0
}
findVendorByAppleId Vendor
{
    "vendorId": 1,
    "appleId": 1,
    "vendorName": "Daina Block",
    "available": false
}
findStoreByAppleId Store
{
    "storeId": 1,
    "appleId": 1,
    "storeName": "Dibbert, Walter and Schiller",
    "storeAddress": "Suite 372 3589 Boehm Freeway, Cruickshankville, ID 17581"
}

The GraphQL controller will provide all the details in a single API call.

GraqlQL Query GraphQL Response
query FindAppleByAppleId {
    findAppleByAppleId(appleId: 1) {
        appleId
        appleName
        available
        price
    }
    findVendorByAppleId(appleId: 1) {
        vendorId
        appleId
        vendorName
        available
    }
    findStoreByAppleId(appleId: 1) {
        storeId
        appleId
        storeName
        storeAddress
    }
}
{
    "data": {
        "findAppleByAppleId": {
            "appleId": "1",
            "appleName": "FUJI",
            "available": false,
            "price": 1
        },
        "findVendorByAppleId": {
            "vendorId": "1",
            "appleId": "1",
            "vendorName": "Daina Block",
            "available": false
        },
        "findStoreByAppleId": {
            "storeId": "1",
            "appleId": "1",
            "storeName": "Bednar, Bailey and Jaskolski",
            "storeAddress": "Suite 830 81561 Carlos Way, Angelynville, AL 91369-7772"
        }
    }
}

Let's see the Spring Boot GraphQL program for the same

package org.wesome.graphql.controllers;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.graphql.data.method.annotation.Argument;
import org.springframework.graphql.data.method.annotation.QueryMapping;
import org.springframework.stereotype.Controller;
import org.wesome.graphql.entity.Apple;
import org.wesome.graphql.entity.Store;
import org.wesome.graphql.entity.Vendor;
import org.wesome.graphql.service.AppleService;

import java.util.Optional;

@Controller
public record AppleGraphQLController(@Autowired AppleService appleService) {
    @QueryMapping("findAppleByAppleId")
    Optional<Apple> findAppleByAppleId(@Argument int appleId) {
        return appleService.findAppleByAppleId(appleId);
    }

    @QueryMapping("findVendorByAppleId")
    Optional<Vendor> findVendorByAppleId(@Argument int appleId) {
        return appleService.findVendorByAppleId(appleId);
    }

    @QueryMapping("findStoreByAppleId")
    Optional<Store> findStoreByAppleId(@Argument int appleId) {
        return appleService.findStoreByAppleId(appleId);
    }
}
package org.wesome.graphql.controllers;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.wesome.graphql.entity.Apple;
import org.wesome.graphql.entity.Store;
import org.wesome.graphql.entity.Vendor;
import org.wesome.graphql.service.AppleService;

import java.util.Optional;

@RestController
public record AppleRestController(@Autowired AppleService appleService) {

    @GetMapping(path = "/findAppleByAppleId/{appleId}", produces = "application/json")
    Optional<Apple> findAppleByAppleId(@PathVariable("appleId") int appleId) {
        return appleService.findAppleByAppleId(appleId);
    }

    @GetMapping(path = "/findVendorByAppleId/{appleId}", produces = "application/json")
    Optional<Vendor> findVendorByAppleId(@PathVariable("appleId") int appleId) {
        return appleService.findVendorByAppleId(appleId);
    }

    @GetMapping(path = "/findStoreByAppleId/{appleId}", produces = "application/json")
    Optional<Store> findStoreByAppleId(@PathVariable("appleId") int appleId) {
        return appleService.findStoreByAppleId(appleId);
    }
}
package org.wesome.graphql.data;

public enum AppleType {
    MACINTOSH, FUJI, GALA, JONAGOLD
}
package org.wesome.graphql.entity;

public record Apple(int appleId, String appleName, Boolean available, Float price) {
}
package org.wesome.graphql.entity;

public record Store(int storeId, int appleId, String storeName, String storeAddress) {
}
package org.wesome.graphql.entity;

public record Vendor(int vendorId, int appleId, String vendorName, Boolean available) {
}
package org.wesome.graphql.service;

import org.wesome.graphql.entity.Apple;
import org.wesome.graphql.entity.Store;
import org.wesome.graphql.entity.Vendor;

import java.util.Optional;

public interface AppleService {
    Optional<Apple> findAppleByAppleId(int appleId);

    Optional<Vendor> findVendorByAppleId(int appleId);

    Optional<Store> findStoreByAppleId(int appleId);
}
package org.wesome.graphql.service;

import org.springframework.stereotype.Service;
import org.wesome.graphql.entity.Apple;
import org.wesome.graphql.entity.Store;
import org.wesome.graphql.entity.Vendor;

import java.util.Optional;

import static org.wesome.graphql.GraphqlProjectApplication.apples;
import static org.wesome.graphql.GraphqlProjectApplication.stores;
import static org.wesome.graphql.GraphqlProjectApplication.vendors;

@Service
public record AppleServiceImpl() implements AppleService {
    @Override
    public Optional<Apple> findAppleByAppleId(int appleId) {
        return apples.stream().filter(apple -> appleId == apple.appleId()).findFirst();
    }

    @Override
    public Optional<Vendor> findVendorByAppleId(int appleId) {
        return vendors.stream().filter(vendor -> appleId == vendor.appleId()).findFirst();
    }

    @Override
    public Optional<Store> findStoreByAppleId(int appleId) {
        return stores.stream().filter(store -> appleId == store.appleId()).findFirst();
    }
}
package org.wesome.graphql;

import com.github.javafaker.Faker;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.wesome.graphql.data.AppleType;
import org.wesome.graphql.entity.Apple;
import org.wesome.graphql.entity.Store;
import org.wesome.graphql.entity.Vendor;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;


@SpringBootApplication
public class GraphqlProjectApplication implements CommandLineRunner {
    public static List<Apple> apples;
    public static List<Vendor> vendors;
    public static List<Store> stores;

    public static void main(String[] args) {
        SpringApplication.run(GraphqlProjectApplication.class, args);
    }

    @Override
    public void run(String... args) {
        Faker faker = new Faker();
        apples = IntStream.rangeClosed(0, AppleType.values().length - 1).mapToObj(value -> new Apple(value, AppleType.values()[value].name(), faker.random().nextBoolean(), Float.valueOf(value))).collect(Collectors.toList());
        vendors = IntStream.rangeClosed(1, AppleType.values().length - 1).mapToObj(value -> new Vendor(value, value, faker.name().fullName(), faker.random().nextBoolean())).collect(Collectors.toList());
        stores = IntStream.rangeClosed(1, AppleType.values().length - 1).mapToObj(value -> new Store(value, value, faker.company().name(), faker.address().fullAddress())).collect(Collectors.toList());
    }
}

\src\main\resources\graphql\schema.graphqls

# Apple Object
type Apple{
    # primary key of apple
    appleId:ID!
    # apple Name
    appleName:String!
    # apple availability
    available: Boolean
    # apple Price
    price:Float
}
# Store Object
type Store{
    # primary key of store
    storeId:ID!
    # primary key of apple
    appleId:ID!
    # apple Store Name
    storeName:String!
    # apple store Address
    storeAddress:String!
}
# Store Object
type Vendor{
    # primary key of vendor
    vendorId:ID!
    # primary key of apple
    appleId:ID!
    # apple vendor Name
    vendorName:String!
    # vendor availability
    available: Boolean
}

# Apple Query
type Query{
    # query to get all Apples
    findAppleByAppleId(appleId:Int):Apple
    # query to get all Vendor
    findVendorByAppleId(appleId:Int):Vendor
    # query to get all Store
    findStoreByAppleId(appleId:Int):Store
}

\src\main\resources\application.properties

spring.graphql.graphiql.enabled=true
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.1.5</version>
        <relativePath/>
    </parent>
    <groupId>org.wesome</groupId>
    <artifactId>spring-boot-graphql</artifactId>
    <version>0.0.1-snapshot</version>
    <name>spring-boot-graphql</name>
    <description>implementing graphql in spring boot</description>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-graphql</artifactId>
        </dependency>
        <dependency>
            <groupId>com.graphql-java</groupId>
            <artifactId>graphql-java-extended-scalars</artifactId>
            <version>21.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webflux</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.graphql</groupId>
            <artifactId>spring-graphql-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.github.javafaker</groupId>
            <artifactId>javafaker</artifactId>
            <version>1.0.2</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

how to access Rest API

cURL

cURL command is very useful for checking the API endpoint. The rest endpoint can be accessed via curl

API Rest End Point Curl
/findAppleByAppleId/{appleId}
http://localhost:8080/findAppleByAppleId/1
curl --location --request GET "http://localhost:8080/findAppleByAppleId/1"
/findVendorByAppleId/{appleId}
http://localhost:8080/findVendorByAppleId/1
curl --location --request GET "http://localhost:8080/findVendorByAppleId/1"
/findStoreByAppleId/{appleId}
http://localhost:8080/findStoreByAppleId/1
curl --location --request GET "http://localhost:8080/findStoreByAppleId/1"

how to access GraphQL API

GraphQL provides multiple ways to access the API, let's see them 

cURL

The GraphQL can be accessed via the below cURL command.

curl --location --request POST "http://localhost:8080/graphql" \
--header "Content-Type: application/json" \
--data "{\"query\":\"query FindAppleByAppleId {\r\n    findAppleByAppleId(appleId: 1) {\r\n        appleId\r\n        appleName\r\n        available\r\n        price\r\n    }\r\n    findVendorByAppleId(appleId: 1) {\r\n        vendorId\r\n        appleId\r\n        vendorName\r\n        available\r\n    }\r\n    findStoreByAppleId(appleId: 1) {\r\n        storeId\r\n        appleId\r\n        storeName\r\n        storeAddress\r\n    }\r\n}\r\n\",\"variables\":{}}"

GraphiQL

GraphQL provides an inbuild UserInterface GraphiQL, which can be accessed via http://localhost:8080/graphiql, in the query section add the below query

query FindAppleByAppleId {
    findAppleByAppleId(appleId: 1) {
        appleId
        appleName
        available
        price
    }
    findVendorByAppleId(appleId: 1) {
        vendorId
        appleId
        vendorName
        available
    }
    findStoreByAppleId(appleId: 1) {
        storeId
        appleId
        storeName
        storeAddress
    }
}

Postman Post Method

GraphQL can be accessed via Postman as well, open Postman-> file-> import and paste the below curl

curl --location --request POST "http://localhost:8080/graphql" \
--header "Content-Type: application/json" \
--data "{\"query\":\"query FindAppleByAppleId {\r\n    findAppleByAppleId(appleId: 1) {\r\n        appleId\r\n        appleName\r\n        available\r\n        price\r\n    }\r\n    findVendorByAppleId(appleId: 1) {\r\n        vendorId\r\n        appleId\r\n        vendorName\r\n        available\r\n    }\r\n    findStoreByAppleId(appleId: 1) {\r\n        storeId\r\n        appleId\r\n        storeName\r\n        storeAddress\r\n    }\r\n}\r\n\",\"variables\":{}}"

Postman GraphQL Request Type

Postman provides a GraphQL request type UI, Open Postman-> file-> new->GraphQL and add the URL  https://localhost:8080/graphql in the query section add the below query

query FindAppleByAppleId {
    findAppleByAppleId(appleId: 1) {
        appleId
        appleName
        available
        price
    }
    findVendorByAppleId(appleId: 1) {
        vendorId
        appleId
        vendorName
        available
    }
    findStoreByAppleId(appleId: 1) {
        storeId
        appleId
        storeName
        storeAddress
    }
}

follow us on