GraphQL query based on a specific value of a field - graphql

I want to be able to retrieve the latest release from GitHub for a specific repo using their GraphQL API. To do that, I need to get the latest release where isDraft and isPrerelease are false. I have managed to get the first part, but cant figure out how to do the "where" part of the query.
Here is the basic query I have gotten (https://developer.github.com/v4/explorer/):
{
repository(owner: "paolosalvatori", name: "ServiceBusExplorer") {
releases(first: 1, orderBy: {field: CREATED_AT, direction: DESC}) {
nodes {
name
tagName
resourcePath
isDraft
isPrerelease
}
}
}
}
Which returns:
{
"data": {
"repository": {
"releases": {
"nodes": [
{
"name": "3.0.4",
"tagName": "3.0.4",
"resourcePath": "/paolosalvatori/ServiceBusExplorer/releases/tag/3.0.4",
"isDraft": false,
"isPrerelease": false
}
]
}
}
}
}
I cant seem to find a way to do this. Part of the reason is that I am new to GraphQL (first time trying to do a query) and I am not sure how to frame my question.
Can one only "query" based on those types that support arguments (like repository and releases below)? Seems like there should be a way to specify a filter on the field values.
Repository: https://developer.github.com/v4/object/repository/
Releases: https://developer.github.com/v4/object/releaseconnection/
Node: https://developer.github.com/v4/object/release/

Can one only "query" based on those types that support arguments
Yes: GraphQL doesn't define a generic query language in the same way, say, SQL does. You can't sort or filter a field result in ways that aren't provided by the server and the application schema.
I want to be able to retrieve the latest [non-draft, non-prerelease] release from GitHub for a specific repo using their GraphQl API.
As you've already found, the releases field on the Repository type doesn't have an option to sort or filter on these fields. Instead, you can iterate through the releases one at a time with multiple GraphQL calls. These would individually look like
query NextRelease($owner: String!, $name: String!, $after: String) {
repository(owner: $owner, name: $name) {
releases(first: 1,
orderBy: {field: CREATED_AT, direction: DESC},
after: $after) {
pageInfo { lastCursor }
nodes { ... ReleaseData } # from the question
}
}
}
Run this in the same way you're running it now (I've split out the information identifying the repository into separate GraphQL variables). You can leave off the after variable for the first call. If (as in your example) it returns "isDraft": false, "isPrerelease": false, you're set. If not, you need to try again: take the value from the lastCursor in the response, and run the same query, passing that cursor value as the after variable value.

{
repository(owner: "paolosalvatori", name: "ServiceBusExplorer") {
releases(first: 1, orderBy: {field: CREATED_AT, direction: DESC}) {
nodes(isDraft :false , isPrerelease :false ) {
name
tagName
resourcePath
isDraft
isPrerelease
}
}
}
}
Alternatively please have look at GraphQL directives, as sometimes it's required to skip or include the fields on the basis of the values
#skip or #include.
The skip directive, when used on fields or fragments, allows us to exclude fields based on some condition.
The include directive, allows us to include fields based on some condition
GraphQL Directives

Related

What is the recommended schema for paginated GraphQL results

Let's say I have users list to be returned. What would be best schema strategy among following.
Users returned contains only the data of user as follows, separate query is used for pagination details. In this query the downside is we need to pass same filters to both users and usersCount query.
query {
users(skip: 0, limit: 100, filters: someFilter) {
name
},
usersCount(filters: someFilters)
}
Which return following
{
results: {
users: [
{ name: "Foo" },
{ name: "Bar" },
],
usersCount: 1000,
}
}
In this strategy we make pagination details as part of users query, we don't need to pass filters twice. I feel this query is not nice to read.
query {
users(skip: 0, limit: 100, filters: someFilter) {
items: {
name
},
count
}
}
Which returns the following result
{
results: {
users: {
items: [
{ name: "Foo" },
{ name: "Bar" },
],
count: 1000,
}
}
}
I am curious to know which strategy is the recommended way while designing paginated results?
I would recommend to follow the official recommendation on graphql spec,
You need to switch to cursor based pagination.
This type of pagination uses a record or a pointer to a record in the dataset to paginate results. The cursor will refer to a record in the database.
You can follow the example in the link.
GraphQL Cursor Connections Specification
Also checkout how GitHub does it here: https://docs.github.com/en/graphql/reference/interfaces#node

Sort GraphQL query results in the exact order of input array

I need to get a number of items from a GraphQL enabled database (no control over its schema) and output them in the exact order called.
For example, if the database holds the items 1,2,3 in that respective order I need to get them as 3,1,2.
Query:
{items(filter: {id: {_in: ["3","1","2"] } } ) {data}}
Actual result:
{"data": {"items": [{"data": "data-from-1"},{"data": "data-from-2"},{"data": "data-from-3"}]}}
Expected result:
{"data": {"items": [{"data": "data-from-3"},{"data": "data-from-1"},{"data": "data-from-1"}]}}
So I guess that what I'm looking for is a 'meta' operator that relates to other operators rather than the actual query – something like:
sort:["_in"] or orderby:{operator:"_in"}
...but I didn't manage to find out if such a thing exists or not.
So is it possible in general or maybe in some flavour of GraphQL? Or is it my only choice to prebuild a query with aliases and do it like this:
{
_3: items(filter:{id: { _eq: "3" }}){data}
_1: items(filter:{id: { _eq: "1" }}){data}
_2: items(filter:{id: { _eq: "2" }}){data}
}
Which GraphQL client are you using?
If you're using Apollo, and you really don't have access to the schema/resolvers in the server, you can create a local field and resolve it on your own, and so you can manipulate as much as you want.
Reference
https://www.apollographql.com/docs/react/local-state/managing-state-with-field-policies/#defining
Basically, if you're querying a field like:
query {
someQuery(someFilter: {foo: "bar"}) {
items {
data
}
}
}
You can create a local field and write a typePolicy to it. Then you can query something like:
query {
someQuery(someFilter: {foo: "bar"}) {
items {
data
}
parsedItems #client
}
}
Then you can get data from ìtems and resolve parsedItems locally as you want.

Obtain GitHub issues in a specific milestone using GraphQL and IssueFilters

A similar question was previously asked but doesn't use IssueFilters which is what I want to use in this question.
Using GitHub's GraphQL Explorer, I am able to get issues of a repository using this query:
{
repository(owner: "neovim", name: "neovim") {
hasIssuesEnabled
issues(first: 20, orderBy: {field: CREATED_AT, direction: DESC}, filterBy: {milestone:"*"}) {
nodes {
... on Issue {
number
title
milestone {
number
id
url
title
}
}
}
}
}
}
The milestone is an issueFilter which, according to the documentation, allows you to:
List issues by given milestone argument. If a string representation of an integer is passed, it should refer to a milestone by its number field. Pass in null for issues with no milestone, and * for issues that are assigned to any milestone.
However, using a filter that is not a * but let's say filterBy: {milestone:"9"} (9 being a valid milestone number for the repository I am using), the query returns no nodes:
{
"data": {
"repository": {
"hasIssuesEnabled": true,
"issues": {
"nodes": []
}
}
}
}
I do not understand why this does not work from the documentation I read. Am I missing something regarding what means "it should refer to a milestone by its number field"?

Is it possible for vue-apollo to return different results from the Playground?

I have a GraphQL query called myAccounts which returns an array of accounts. When I go to the Playground and call the query:
{
accounts {
email
}
}
I get this result:
"data": {
"accounts": [
{
"email": "zach#email-one.com",
},
{
"email": "zach#email-two.com",
}
]
}
However, when I am in my Component, vue-apollo returns two items in the array, but seems to overwrite the second item with the first. Here is the query (in MyAccounts.gql):
query myAccounts {
accounts: myAccounts {
email
}
}
and here is the Apollo query in the component:
import MY_ACCOUNTS_QUERY from '~/apollo/queries/MyAccounts'
...
apollo: {
accounts: {
query: MY_ACCOUNTS_QUERY,
result(data) {
console.log(JSON.stringify(data))
}
}
}
and here is what vue-apollo logs out through the result:
{
"data":{
"accounts":[
{
"email":"zach#email-one.com",
"__typename":"Account"
},
{
"email":"zach#email-one.com",
"__typename":"Account"
}
]
},
"loading":false,
"networkStatus":7,
"stale":false
}
Expected behavior
I would expect the data returned in the Playground to be identical to what vue-apollo is fetching.
Versions
vue: 2.6.10
vue-apollo: #nuxtjs/apollo: 4.0.0-rc18
Additional context
I thought the result hook would be the best way to debug, but any other suggestions gladly welcomed. I assumed that this was a bug in our code, but I cannot figure out what could be causing the repetition (and mismatch).
Apollo normalizes its cache based on the __typename and the id (or _id) field. You need to include an id or _id field in your selection set alongside email. Failing to do so results in both objects being assigned the same key. If you don't have an id field to request, you'll need to provide a custom dataIdFromObject function as shown here.
From Guillaume Chau (https://github.com/Akryum):
This is because the Apollo Client cache can't compute a different ID
for the two items, so you endup with Account:undefined (or similar)
for both. Open the Apollo devtools and look at the myAccounts key in
the cache.
Learn more:
https://www.apollographql.com/docs/react/caching/cache-configuration/

Do a full query simply in GraphQL [duplicate]

Assume you have a GraphQL type and it includes many fields.
How to query all the fields without writing down a long query that includes the names of all the fields?
For example, If I have these fields :
public function fields()
{
return [
'id' => [
'type' => Type::nonNull(Type::string()),
'description' => 'The id of the user'
],
'username' => [
'type' => Type::string(),
'description' => 'The email of user'
],
'count' => [
'type' => Type::int(),
'description' => 'login count for the user'
]
];
}
To query all the fields usually the query is something like this:
FetchUsers{users(id:"2"){id,username,count}}
But I want a way to have the same results without writing all the fields, something like this:
FetchUsers{users(id:"2"){*}}
//or
FetchUsers{users(id:"2")}
Is there a way to do this in GraphQL ??
I'm using Folkloreatelier/laravel-graphql library.
Unfortunately what you'd like to do is not possible. GraphQL requires you to be explicit about specifying which fields you would like returned from your query.
Yes, you can do this using introspection. Make a GraphQL query like (for type UserType)
{
__type(name:"UserType") {
fields {
name
description
}
}
}
and you'll get a response like (actual field names will depend on your actual schema/type definition)
{
"data": {
"__type": {
"fields": [
{
"name": "id",
"description": ""
},
{
"name": "username",
"description": "Required. 150 characters or fewer. Letters, digits, and #/./+/-/_ only."
},
{
"name": "firstName",
"description": ""
},
{
"name": "lastName",
"description": ""
},
{
"name": "email",
"description": ""
},
( etc. etc. ...)
]
}
}
}
You can then read this list of fields in your client and dynamically build a second GraphQL query to get the values of these fields.
This relies on you knowing the name of the type that you want to get the fields for -- if you don't know the type, you could get all the types and fields together using introspection like
{
__schema {
types {
name
fields {
name
description
}
}
}
}
NOTE: This is the over-the-wire GraphQL data -- you're on your own to figure out how to read and write with your actual client. Your GraphQL javascript library may already employ introspection in some capacity. For example, the apollo codegen command uses introspection to generate types.
2022 Update
Since this answer was originally written, it is now a recommended security practice to TURN OFF introspection in production. Reference: Why you should disable GraphQL introspection in production.
For an environment where introspection is off in production, you could use it in development as a way to assist in creating a static query that was used in production; you wouldn't actually be able to create a query dynamically in production.
I guess the only way to do this is by utilizing reusable fragments:
fragment UserFragment on Users {
id
username
count
}
FetchUsers {
users(id: "2") {
...UserFragment
}
}
I faced this same issue when I needed to load location data that I had serialized into the database from the google places API. Generally I would want the whole thing so it works with maps but I didn't want to have to specify all of the fields every time.
I was working in Ruby so I can't give you the PHP implementation but the principle should be the same.
I defined a custom scalar type called JSON which just returns a literal JSON object.
The ruby implementation was like so (using graphql-ruby)
module Graph
module Types
JsonType = GraphQL::ScalarType.define do
name "JSON"
coerce_input -> (x) { x }
coerce_result -> (x) { x }
end
end
end
Then I used it for our objects like so
field :location, Types::JsonType
I would use this very sparingly though, using it only where you know you always need the whole JSON object (as I did in my case). Otherwise it is defeating the object of GraphQL more generally speaking.
GraphQL query format was designed in order to allow:
Both query and result shape be exactly the same.
The server knows exactly the requested fields, thus the client downloads only essential data.
However, according to GraphQL documentation, you may create fragments in order to make selection sets more reusable:
# Only most used selection properties
fragment UserDetails on User {
id,
username
}
Then you could query all user details by:
FetchUsers {
users() {
...UserDetails
}
}
You can also add additional fields alongside your fragment:
FetchUserById($id: ID!) {
users(id: $id) {
...UserDetails
count
}
}
Package graphql-type-json supports custom-scalars type JSON.
Use it can show all the field of your json objects.
Here is the link of the example in ApolloGraphql Server.
https://www.apollographql.com/docs/apollo-server/schema/scalars-enums/#custom-scalars

Resources