Spring Boot GraphQL Pagination

GraphQL can return a list of results, but it's always advised all APIs returning a list of data should implement Pagination, The data set will grow and will take a lot longer to process by the API server slowing down the API response time.

Spring Boot GraphQL can leverage the Spring Data JPA provided by Spring Ecosystem and implement the Pagination, Learn more about Spring Data JPA.

package org.wesome.graphql.controllers;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
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.service.AppleService;

@Controller
public class AppleGraphQLController {
    @Autowired
    private AppleService appleService;

    @QueryMapping("findAllApple")
    Iterable<Apple> findAllApple(@Argument int page, @Argument int size) {
        PageRequest pageRequest = PageRequest.of(page, size);
        Page<Apple> allApple = appleService.findAllApple(pageRequest);
        System.out.println("allApple = " + allApple);
        return allApple;
    }
}
package org.wesome.graphql.controllers;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.wesome.graphql.entity.Apple;
import org.wesome.graphql.service.AppleService;

@RestController
public class AppleRestController {
    @Autowired
    private AppleService appleService;

    @GetMapping(path = "/findAllApple", produces = "application/json")
    Page<Apple> findAllApple(@RequestParam int page, @RequestParam int size) {
        PageRequest pageRequest = PageRequest.of(page, size);
        return appleService.findAllApple(pageRequest);
    }
}
package org.wesome.graphql.entity;

import jakarta.persistence.CascadeType;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.OneToOne;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Entity
@AllArgsConstructor
@NoArgsConstructor
public class Apple {
    @Id
    @GeneratedValue
    private int appleId;
    private String appleName;
    private String appleTaste;
    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "apple_id", referencedColumnName = "apple_id")
    private Vendor vendor;
}
package org.wesome.graphql.entity;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Entity
@AllArgsConstructor
@NoArgsConstructor
public class Vendor {
    @Id
    @GeneratedValue
    private int vendorId;
    private String vendorName;
}
package org.wesome.graphql.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import org.wesome.graphql.entity.Apple;

@Repository
public interface AppleRepository extends JpaRepository<Apple, Integer> {
}
package org.wesome.graphql.service;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.wesome.graphql.entity.Apple;

public interface AppleService {
    Page<Apple> findAllApple(PageRequest pageRequest);
}
package org.wesome.graphql.service;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import org.wesome.graphql.entity.Apple;
import org.wesome.graphql.repository.AppleRepository;

@Service
public class AppleServiceImpl implements AppleService {
    @Autowired
    private AppleRepository appleRepository;

    @Override
    public Page<Apple> findAllApple(PageRequest pageRequest) {
        return appleRepository.findAll(pageRequest);
    }
}
package org.wesome.graphql;

import com.github.javafaker.Faker;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.wesome.graphql.entity.Apple;
import org.wesome.graphql.entity.AppleTaste;
import org.wesome.graphql.entity.Vendor;
import org.wesome.graphql.repository.AppleRepository;

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

@SpringBootApplication
public class GraphqlProjectApplication implements CommandLineRunner {
    @Autowired
    private AppleRepository appleRepository;

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

    @Override
    public void run(String... args) {
        Faker faker = new Faker();
        List<Apple> apples = IntStream.rangeClosed(1, 10).mapToObj(value -> new Apple(value, faker.food().fruit(), AppleTaste.values()[faker.random().nextInt(0, 3)].name(), new Vendor(value, faker.name().fullName()))).collect(Collectors.toList());
        appleRepository.saveAll(apples);

    }
}
# Apple Object
type Apple{
    # appleId
    appleId:ID!
    # apple Name
    appleName:String
    # apple AppleTaste
    appleTaste:String
    vendor:Vendor
}
# Apple Vendor
type Vendor{
    # vendorId
    vendorId:ID!
    # vendor Name
    vendorName:String
}
# Apple Query
type Query{
    # query to get all apples
    findAllApple(page:Int!, size:Int!):[Apple]
}

\src\main\resources\application.properties

spring.jpa.generate-ddl=true
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
spring.datasource.url=jdbc:mysql://localhost:3306/appleDb?createDatabaseIfNotExist=true&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect
spring.datasource.username=root
spring.datasource.password=rootroot
spring.graphql.graphiql.enabled=true

pom.xml

<?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>
    <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.github.javafaker</groupId>
            <artifactId>javafaker</artifactId>
            <version>1.0.2</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </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>
    </dependencies>
    <name>spring boot graphql</name>
    <description>implementing graphql in spring boot</description>
    <properties>
        <java.version>17</java.version>
    </properties>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

Rest API

curl --location 'http://localhost:8080/findAllApple?page=0&size=10'

GraphiQL

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

query FindAllApple {
    findAllApple(page: 0, size: 10) {
        appleId
        appleName
        appleTaste
        vendor {
            vendorId
            vendorName
        }
    }
}

follow us on