Is there an option to query enum description instead of value - graphql

I query some type from graphql schema, and for a specific field, I want to get it with the enum description instead of the enum value.
type Gigi {
a: SomeEnum!
b: Int!
}
enum SomeEnum {
"Bla"
BLA
"Foo"
FOO
}
I want to get the 'Gigi.a' field, with the description of the enum.
For example:
{
a: "Bla"
b: 5
}
Does anyone know if this is possible?

Descriptions of types, fields and enum values exist for documentation purposes only and are therefore only accessibly through an introspection query:
query {
__type(name: "SomeEnum") {
enumValues {
name
description
}
}
}
Also note that there is nothing in the spec that requires enum values to be in all caps, so you can have an enum like:
enum SomeEnum {
Bla
Foo
}

Related

Is there a way to pass a parameter into a GraphQL query to specify the GraphQL type it should be run on?

I'm new to GraphQL and would like to be able to use a variable for a GraphQL name in a query.
I've attempted to use the standard $ syntax but with no luck.
Working query:
query Tryptych($section: SectionsEnum = home) {
enGB: entries(section: [$section], site: "enGB") {
... on Home {
tryptych {
...tryptychFields
}
}
}
}
What I'd like to be able to do:
query Tryptych($section: SectionsEnum = home, $interface: SomeType = Home) {
enGB: entries(section: [$section], site: "enGB") {
... on $interface {
tryptych {
...tryptychFields
}
}
}
}
Fragment for reference:
fragment tryptychFields on TryptychTryptych {
__typename
theme
tagline
firstImageTitle
firstImageContent
firstImageAsset {
url
}
firstImageLink
secondImageTitle
secondImageContent
secondImageAsset {
url
}
secondImageLink
thirdImageTitle
thirdImageContent
thirdImageAsset {
url
}
thirdImageLink
}
In the code snippet for what I'd like to achieve I get the error message:
Expected Name, found $
Thanks for the help.
A variable can only have one type, that type must be an input type (i.e. a scalar, enum or input object type), and it can only be used where an input type would be expected (i.e. a field or directive argument). In other words, the syntax you're suggesting is not supported.
If you have multiple types that may be returned by the same field, you may use any number of fragments to specify the selection set by type. The actual selection set will be determined at runtime when the type of the field is evaluated. For example, if the animal field returns a union of Cat, Dog and Bird types:
query {
animal {
... on Cat {
meows
}
... on Dog {
barks
}
... on Bird {
chirps
}
}
}
You may also use the #skip and #include directives to control which fields are selected:
query ($inAHouse: Boolean!, $withAMouse: Boolean!) {
greenEggs #skip(if: $inAHouse)
ham #include(if: $withAMouse)
}
And you may include multiple operations in a single document, and then specify an operationName with your request to tell the server which operation to run:
query OperationA {
foo
}
query OperationB {
bar
}

I dont want to redefine properties when defining a GraphQL type. Is there a way to get past it? [duplicate]

Is it possible to use inheritance with GraphQL input types?
Something like that (this, of course, doesn't work with input types):
interface UserInputInterface {
firstName: String
lastName: String
}
input UserInput implements UserInputInterface {
password: String!
}
input UserChangesInput implements UserInputInterface {
id: ID!
password: String
}
No, the spec does not allow input types to implement interfaces. And GraphQL type system in general does not define any form of inheritance (the extends keyword adds fields to an existing type, and isn't for inheritance). The spec is intentionally constrained to stay simple. This means that you're stuck repeating fields across input types.
That said, depending on the way you construct your schema, you could build some kind of type transformer that appends the common fields programmatically based on some meta-data, e.g. a directive.
Better yet, you might be able to solve your problem via composition (always keep composition over inheritance in mind).
E.g.
input Name {
firstName: String
lastName: String
}
input UserInput {
name: Name
password: String!
}
input UserChangesInput {
name: Name
id: ID!
password: String
}
The client now has to send an object a level deeper, but that doesn't sound like much of a price for avoiding big repeating chunks. It might actually be good for the client as well, as they can now have common logic for building names, regardless of the query/mutation using them.
In this example, where it's only 2 simple fields, this approach is an overkill, but in general - I'd say it's the way to go.
Starting with the June2018 stable version of the GraphQL spec, an Input Object type can extend another Input Object type:
Input object type extensions are used to represent an input object type which has been extended from some original input object type.
This isn't inheritance per se; you can only extend the base type, not create new types based on it:
extend input MyInput {
NewField: String
}
Note there is no name for the new type; the existing MyInput type is extended.
The JavaScript reference implementation has implemented Input Object extensions in GraphQL.js v14 (June 2018), though it's unclear how to actually pass the extended input fields to a query without getting an error.
For actual type inheritance, see the graphql-s2s library.
It's doable using a custom directive.
Code Summary
const typeDefs = gql`
directive #inherits(type: String!) on OBJECT
type Car {
manufacturer: String
color: String
}
type Tesla #inherits(type: "Car") {
manufacturer: String
papa: String
model: String
}
type Query {
tesla: Tesla
}
`;
const resolvers = {
Query: {
tesla: () => ({ model: 'S' }),
},
Car: {
manufacturer: () => 'Ford',
color: () => 'Orange',
},
Tesla: {
manufacturer: () => 'Tesla, Inc',
papa: () => 'Elon',
},
};
class InheritsDirective extends SchemaDirectiveVisitor {
visitObject(type) {
const fields = type.getFields();
const baseType = this.schema.getTypeMap()[this.args.type];
Object.entries(baseType.getFields()).forEach(([name, field]) => {
if (fields[name] === undefined) {
fields[name] = { ...field };
}
});
}
}
const schemaDirectives = {
inherits: InheritsDirective,
};
Query:
query {
tesla {
manufacturer
papa
color
model
}
}
Output:
{
"data": {
"tesla": {
"manufacturer": "Tesla, Inc",
"papa": "Elon",
"color": "Orange",
"model": "S",
}
}
}
Working example at https://github.com/jeanbmar/graphql-inherits.
If you came here looking for an explanation for the "implements", keyword, here it is:
An object type must be a super‐set of all interfaces it implements. The object type must include a field of the same name for every field defined in an interface.
(Excerpt taken from the June 2018 GraphQL spec.)
Here's an example
interface Foo {
id: ID!
foo: Int!
}
type Bar implements Foo #entity {
id: ID!;
foo: Int!;
bar: Int!;
}
So the Bar type doesn't inherit from the Foo interface, but it implements it. The former must include all the fields that are listed in the latter.
I think that this is a nice way to annotate types that should be like other types.

Use GraphQL to retrieve an object that contains an array of objects with different schemas

I am trying to write a query to retrieve an object with the property linkedCards that contains an array of objects with different schemas.
I have 3 different schemas (built in Contentful):
CardA example:
{
id: 42,
productName: 'Laptop',
price: 999
}
CardB example:
{
id: 999,
title: 'Buy our refurbished Laptops today!'
}
CardC example:
{
id: 100,
linkedCards: [
{
id: 42,
productName: 'Laptop',
price: 999
},
{
id: 999,
title: 'Buy our refurbished Laptops today!'
}
]
}
Query:
allCardC() {
nodes {
linkedCards {
id
title
}
}
}
When I try to run the following GraphQL query I get
"Cannot query field "title" on type "CardACardBUnion". Did you mean to use an inline fragment on "CardA" or "CardB"?"
Is there a built-in way to do this or can I use the ids of CardA & CardB somehow? Perhaps have one query to get the ids of the cards in linkedCards and another query to get said cards?
As the error indicates, you need to use an inline fragment when querying a field that resolves to a union:
allCardC {
nodes {
linkedCards {
... on CardA {
id
productName
price
}
... on CardB {
id
title
}
}
}
}
Fragments can be defined inline within a selection set. This is done to conditionally include fields based on their runtime type.
Unlike interfaces or regular object types, unions do not specify any particular fields, only the types that make up the union. That means a selection set for a field that returns a union must always use fragments to conditionally specify the fields depending on the actual type that the field resolves to.
It's like saying, "if this is the actual type of the returned object, request these fields".
You may find it useful to use a GraphQL interface to specify the fields that every card type has in common.
interface Card {
id: ID!
}
# type CardA implements Card { ... }
type CardB implements Card {
id: ID!
title: String!
}
type CardC implements Card {
id: ID!
linkedCards: [Card!]!
}
As #DanielRearden's answer suggests you still need to use (inline) fragments to select fields that are specific to one of the card types, but now that you know every card has an id field, you can select that directly.
allCardC {
nodes {
linkedCards {
id
... on CardB { title }
}
}
}

Apollo graphql queries with type condition on nested fields

I am working on the following types, where the "content" of a "Comment" is a union type:
type TextContent {
text: String
}
type RichContent {
participants: [String]
startTime: String
}
union Content = TextContent | RichContent
type Comment {
id: ID
sender: String
content: Content
}
type Review {
id: ID
title: String
lastComment: Comment
}
In my Apollo query, I was trying to use conditional fragments on the 2 Content types:
query listOfReviews {
reviews {
...reviewFields
}
}
fragment reviewFields on Review {
id
title
lastComment {
content {
... on TextContent {
text
}
... on RichContent {
participants
startTime
}
}
}
}
I received a runtime error where Apollo seems trying to access "participants" field of "undefined", where the actual content object is:
{
__typename: "TextContent:,
text: "abc"
}
It looks the two types of the union Content are merged together.
My question is: is it allowed to use type conditions on nested fields in Apollo queries? Or type conditions have to be used on the top level types returned by the queries? If it's allowed, how should I fix my types/queries?
Thanks a lot!
#const86 helped point out that this is due to this bug: https://github.com/apollographql/apollo-link-state/pull/258.

Can a GraphQL input type inherit from another type or interface?

Is it possible to use inheritance with GraphQL input types?
Something like that (this, of course, doesn't work with input types):
interface UserInputInterface {
firstName: String
lastName: String
}
input UserInput implements UserInputInterface {
password: String!
}
input UserChangesInput implements UserInputInterface {
id: ID!
password: String
}
No, the spec does not allow input types to implement interfaces. And GraphQL type system in general does not define any form of inheritance (the extends keyword adds fields to an existing type, and isn't for inheritance). The spec is intentionally constrained to stay simple. This means that you're stuck repeating fields across input types.
That said, depending on the way you construct your schema, you could build some kind of type transformer that appends the common fields programmatically based on some meta-data, e.g. a directive. Here's one such implementation.
Better yet, you might be able to solve your problem via composition (always keep composition over inheritance in mind).
E.g.
input Name {
firstName: String
lastName: String
}
input UserInput {
name: Name
password: String!
}
input UserChangesInput {
name: Name
id: ID!
password: String
}
The client now has to send an object a level deeper, but that doesn't sound like much of a price for avoiding big repeating chunks. It might actually be good for the client as well, as they can now have common logic for building names, regardless of the query/mutation using them.
In this example, where it's only 2 simple fields, this approach is an overkill, but in general - I'd say it's the way to go.
Starting with the June2018 stable version of the GraphQL spec, an Input Object type can extend another Input Object type:
Input object type extensions are used to represent an input object type which has been extended from some original input object type.
This isn't inheritance per se; you can only extend the base type, not create new types based on it:
extend input MyInput {
NewField: String
}
Note there is no name for the new type; the existing MyInput type is extended.
The JavaScript reference implementation has implemented Input Object extensions in GraphQL.js v14 (June 2018), though it's unclear how to actually pass the extended input fields to a query without getting an error.
For actual type inheritance, see the graphql-s2s library.
It's doable using a custom directive.
Code Summary
const typeDefs = gql`
directive #inherits(type: String!) on OBJECT
type Car {
manufacturer: String
color: String
}
type Tesla #inherits(type: "Car") {
manufacturer: String
papa: String
model: String
}
type Query {
tesla: Tesla
}
`;
const resolvers = {
Query: {
tesla: () => ({ model: 'S' }),
},
Car: {
manufacturer: () => 'Ford',
color: () => 'Orange',
},
Tesla: {
manufacturer: () => 'Tesla, Inc',
papa: () => 'Elon',
},
};
class InheritsDirective extends SchemaDirectiveVisitor {
visitObject(type) {
const fields = type.getFields();
const baseType = this.schema.getTypeMap()[this.args.type];
Object.entries(baseType.getFields()).forEach(([name, field]) => {
if (fields[name] === undefined) {
fields[name] = { ...field };
}
});
}
}
const schemaDirectives = {
inherits: InheritsDirective,
};
Query:
query {
tesla {
manufacturer
papa
color
model
}
}
Output:
{
"data": {
"tesla": {
"manufacturer": "Tesla, Inc",
"papa": "Elon",
"color": "Orange",
"model": "S",
}
}
}
Working example at https://github.com/jeanbmar/graphql-inherits.
If you came here looking for an explanation for the "implements", keyword, here it is:
An object type must be a super‐set of all interfaces it implements. The object type must include a field of the same name for every field defined in an interface.
(Excerpt taken from the June 2018 GraphQL spec.)
Here's an example
interface Foo {
id: ID!
foo: Int!
}
type Bar implements Foo #entity {
id: ID!;
foo: Int!;
bar: Int!;
}
So the Bar type doesn't inherit from the Foo interface, but it implements it. The former must include all the fields that are listed in the latter.
I think that this is a nice way to annotate types that should be like other types.

Resources