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 |
|
findVendorByAppleId | Vendor |
|
findStoreByAppleId | Store |
|
The GraphQL
controller will provide all the details in a single API
call.
GraqlQL Query | GraphQL Response |
---|---|
|
|
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 |
---|---|---|
|
|
|
|
|
|
|
|
|
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
}
}