question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

graphql and spring data jpa. Select only requested fields

See original GitHub issue

Hi there. I know my question might look silly and already asked many times. But anyway I wonder is there any elegant way to map requested fields in graphql queries to fields in SQL queries using Spring Data layer (JPA) on the fly. Let’s consider a simple example. Imagine you have Persons and Products. In Spring Jpa Model you may present them like this below:

import org.springframework.data.repository.CrudRepository
import org.springframework.stereotype.Repository
import javax.persistence.*

@Entity
@Table(name = "persons")
class PersonEntity (
    val name: String,

    var email: String,

    var age: Int,

    var login: String,

    @OneToMany(mappedBy = "person", fetch = FetchType.LAZY,
        cascade = arrayOf(CascadeType.ALL)
    )
    val products: List<ProductEntity>
) {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Long? = null
}

@Entity
@Table(name = "products")
class ProductEntity(
    val price: Float,

    val description: String,

    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "person_id", nullable = false)
    val person: PersonEntity
) {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Long? = null
}

@Repository
interface PersonRepository : CrudRepository<PersonEntity, Long>

Then the same representation exists in graphql layer:

import com.expediagroup.graphql.spring.operations.Query
import org.springframework.stereotype.Component

data class Person (
    val id: Long,
    val name: String,
    val email: String,
    val age: Int,
    val login: String,
    val products: List<Product>
) {
    companion object {
        fun fromEntity(entity: PersonEntity): Person {
            return Person(
                entity.id!!,
                entity.name,
                entity.email,
                entity.age,
                entity.login,
                entity.products.map(Product.Companion::fromEntity).toList()
            )
        }
    }
}

data class Product (
    val id: Long,
    val price: Float,
    val description: String
) {
    companion object {
        fun fromEntity(entity: ProductEntity) = Product(entity.id!!, entity.price, entity.description)
    }
}

@Component
class PersonQuery(val personRepo: PersonRepository): Query {

    fun listAllPersons() = personRepo.findAll().map(Person.Companion::fromEntity).toList()
}

So, when Spring Boot App is launched next scheme is generated:

schema {
  query: Query
}

type Person {
  age: Int!
  email: String!
  id: Long!
  login: String!
  name: String!
  products: [Product!]!
}

type Product {
  description: String!
  id: Long!
  price: Float!
}

type Query {
  listAllPersons: [Person!]!
}

And an api user can request any number of fields from Person:

{
 listAllPersons{
    id,
    email,
    products {
      price
    }
  }
}

The code above selects all fields from db, including unnecessary ones. I know I can play around some custom data fetchers and loaders but it requires taking into account all the possible field combinations in queries, right? I am asking about some on the fly solution that transform graphql query to sql query using spring. Maybe Spring Data Projections is good friend here. Does anybody know how to achive that?

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:1
  • Comments:6 (2 by maintainers)

github_iconTop GitHub Comments

2reactions
rvit34commented, Feb 29, 2020

@smyrick If i need to add all possible inputs to scheme for all possible requests then I will lose the flexibility that gives me graphql, right? I don’t need graphql api then. I can use rest or rpc api instead.

@dariuszkuc Thanks. The example that I posted in the top is artificial and simplified. In the wild, this rarely happens. About defining GraphQL field as a function. It’s cool that the function won’t be called if corresponding field is not requested. But how about N+1 problem? According to the example with product and reviews from your wiki. If I requested N products the function getReviews will be called N times, right? So I need to do at least N+1 requests to db to fetch all data. Performance will be poor then.

Constructing database requests dynamically based on DataFetchingEnvironment is the way to go. But it requires to manually build db query for every graphql query and manually map graphql fields to db columns. Something like this guy presented here - https://github.com/graphql-java/graphql-java/issues/1180#issuecomment-439726096 It’s possible to do that with EntityManager and Criteria Builder Api but actually it’s not trivial and requires a lot of manual low level coding.

In fact I am asking about something which can do this stuff behind the scenes. Solution like Join-Monster in JS world https://join-monster.readthedocs.io/en/latest/#example-code It would be great if something like this appeared for Java or Kotlin.

0reactions
smyrickcommented, Apr 20, 2020

I am going to close the issue to keep our issues clear, but if anyone else has comments feel to continue the discussion here for others to find later.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Why does graphql java query all fields in entity when I only ...
I have a simple Jpa repository and a simple Jpa service that calls a "findAllTasks" method. It works great, but if I specify,...
Read more >
Spring Boot GraphQL Tutorial #15 - SelectionSet - YouTube
A very powerful feature of graphql spring boot is that you have access to the query's selection set. A selection set is the...
Read more >
An Advanced Guide to GraphQL with Spring Boot
This tutorial is showing how to integrate Spring Boot with GraphQL and JPA to implement more advanced queries like joins or filtering.
Read more >
Getting Started with GraphQL and Spring Boot - Baeldung
In this tutorial, we'll learn how to set up a GraphQL server using Spring Boot so that we can add it to existing...
Read more >
GraphQL performance tip: Select fields from requests all the ...
Solution. What if there was a way to figure out which fields were requested by the client and return only these fields from...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found