9 Mart 2021 Salı

GraphQL Query

Giriş
Açıklaması şöyle
In REST API, we fetch data from specific endpoints. Each endpoint has a particular structure. In other words, the client needs to adhere to the API structure. Basically, the request URL determines the query parameters in a REST API.

However, GraphQL has a considerably different approach. Instead of multiple endpoints, a GraphQL server typically exposes only one endpoint. The structure of the response is not fixed. Instead, it is quite flexible. Basically, the client specifies what data is required and the server responds with the required data.
Root veya Hiyerarşik Sorgular
Nesneler başka nesneleri de içerebilir. Elimizde şöyle bir schema olsun
type Author {
  name: String!
  country: Int!
  books: [Book!]!
}

type Book {
  title: String!
  publishYear: Int!
  author: Author!
}
Sadece kök nesneyi istiyorsak sorgu olarak şöyle yaparız. Burada kitapların sadece title alanını istiyoruz
{
  allBooks {
    title
  }
}
Cevap olarak şunu alırız
{
  "allBooks": [
    { "title": "Eye of the World" },
    { "title": "The Way of Kings" },
    { "title": "The Mistborn" }
  ]
}
Eğer kök nesnenin diğer alanlarını da istiyorsak sorgu olarak şöyle yaparız. Burada kitapların title  ve publishYear alanlarını istiyoruz
{
  allBooks {
    title
    publishYear
  }
}
Hiyerarşik sorgu için şöyle yaparız. Burada yazarların name alanını ve yazdıkları kitapların title alanını istiyoruz. 
{
  allAuthors {
    name
    books {
      title
    }
  }
}


Bazı Örnekler

Örnek - parametresiz
Şöyle yaparız
{
  "query":"{findAllBooks { id title } }"
}
Örnek - parametreli
name + email çekmek için şöyle yaparız.
{
  "query": "query($id: String!){ retrieveUser (id: $id) {name email} }",
  "parameters": {
    "id": 1
  }
}
Sadece email çekmek için şöyle yaparız.
{
  "query": "query($id: String!){ retrieveUser (id: $id) {email} }",
  "parameters": {
    "id": 1
  }
}
Örnek - parametreli
Şöyle yaparız
query order($id: ID!){
  order(id: $id) {
    quantity
    price
    orderDate {
      day,
      month,
      year
    }
    isConfirmed
  }
}
Örnek - parametreli
Şöyle yaparız
void getPostById(WebClient client, String id) {
  client.post("/graphql")
        .sendJson(Map.of(
"query","query post($id:String!){ postById(postId:$id){ id title content author{ name } comments{ content createdAt} createdAt}}",
"variables", Map.of("id", id)
        ))
        .onSuccess(
          data -> log.info("data of postByID: {}", data.bodyAsString())
        )
        .onFailure(e -> log.error("error: {}", e));
}
Örnek - sabit parametre
Şöyle yaparız
query {
    recentPosts(count: 10, offset: 0) {
        id
        title
        category
        author {
            id
            name
            thumbnail
        }
    }
}
Örnek - last kelimesi
Şöyle yaparız
  
{
  "data": {
    "User": {
      "name": "Fernando Doglio",
      "posts": [{
        "title": "Post #1",
        "post_date": "2021-06-28"
      }
      ...
      ],
      "followers": [
        { "name": "Follower #1" },
        { "name": "Follower #2" }
      ]
    }
  }
}
Çıktı olarak şunu alırız
{
  "data": {
    "User": {
      "name": "Fernando Doglio",
      "posts": [{
        "title": "Post #1",
        "post_date": "2021-06-28"
      }
      ...
      ],
      "followers": [
        { "name": "Follower #1" },
        { "name": "Follower #2" }
      ]
    }
  }
}
Örnek - DataLoader
Şöyle yaparız. Bir tane viewer nesnesi döner.
query CompanyData {
  viewer { # DataLoader called usersById
    company(id: "1") { # DataLoader called companiesById
      name

      transports { # DataLoader called transportsByCompanyId
        number

        carrier { # Dataloader called companiesById
          name
        }
      }
    }
  }
}
Açıklaması şöyle
Assuming, for example, that the user has access to 5 transports. Without DataLoaders, we would be looking at 8 database queries:
1 call to fetch the user (viewer)
1 call to fetch company with ID 1
1 call to fetch the 5 transports
5 calls to fetch the carriers for each of the transports

In this example, the benefit comes from the companiesById DataLoader, as it groups the 5 calls for getting the carriers of the transports into a single database query, therefore, saving us from making 4 database queries.

You can imagine scaling this example to something more plausible, such as the user having access to 500 transports, with DataLoaders, the database query count is still 4, without DataLoaders, it would now be 503.

This approach helps us solve the aforementioned N+1 problem, whilst maintaining privacy by sandboxing each request to a separate set of DataLoaders — the cached results are not shared between requests, meaning any permissions logic is never shared.

The DataLoaders also avoid errors in database queries by avoiding duplication of database queries, as well as speeding up the developer experience. More often than not, there already exists a DataLoader for the entity that the code is working with.



Hiç yorum yok:

Yorum Gönder