If I have a set of field that is common to multiple types in my GraphQL schema, is there a way to do something like this?
type Address {
line1: String
city: String
state: String
zip: String
}
fragment NameAndAddress on Person, Business {
name: String
address: Address
}
type Business {
...NameAndAddress
hours: String
}
type Customer {
...NameAndAddress
customerSince: Date
}
Fragments are only used on the client-side when making requests -- they can't be used inside your schema. GraphQL does not support type inheritance or any other mechanism that would reduce the redundancy of having to write out the same fields for different types.
If you're using apollo-server, the type definitions that make up your schema are just a string, so you can implement the functionality you're looking for through template literals:
const nameAndAddress = `
name: String
address: Address
`
const typeDefs = `
type Business {
${nameAndAddress}
hours: String
}
type Customer {
${nameAndAddress}
customerSince: Date
}
`
Alternatively, there are libraries out there, like graphql-s2s, that allow you to use type inheritance.
Don't know if it was not available at this question time, but I guess interfaces should fit your needs
Related
I'm facing a problem where I need to reference a resolved field on the parent from inside the __resolveType. Unfortunately the field I need to reference did not come as part of the original api response for the parent, but from another field resolver, which I would not have though mattered, but indeed it does, so it is undefined.
But I need these fields (in this example the; obj.barCount and obj.bazCount) to be able to make the following query, so I've hit a dead end. I need them to be available in the resolveType function so that I can use them to determine what type to resolve in case this field is defined.
Here's an example:
The graphql query I wish to be able to make:
{
somethings {
hello
... on HasBarCount {
barCount
}
... on HasBazCount {
bazCount
}
}
}
Schema:
type ExampleWithBarCount implements Something & HasBarCount & Node {
hello: String!
barCount: Int
}
type ExampleWithBazCount implements Something & HasBazCount & Node {
hello: String!
bazCount: Int
}
interface Something {
hello: String!
}
interface HasBarCount {
barCount: Int
}
interface HasBazCount {
bazCount: Int
}
Resolvers:
ExampleWithBarCount: {
barCount: (obj) => {
return myApi.getBars(obj.id).length || 0
}
}
ExampleWithBazCount {
bazCount: (obj) => {
return myApi.getBazs(obj.id).length || 0
}
}
Problem:
Something: {
__resolveType(obj) {
console.log(obj.barCount) // Problem: this is always undefined
console.log(obj.bazCount) // Problem: this is always undefined
if (obj.barCount) {
return 'ExampleWithBarCount';
}
if (obj.bazCount) {
return 'ExampleWithBazCount';
}
return null;
}
}
Any ideas of alternative solutions or what am I missing?
Here's a little more about the use case.
In the database we have a table "entity". This table is very simple and only really important columns are id, parent_id, name. type, and then you can of course attach some additional metadata to it.
Like with "entity", types are created dynamically from within the backend management system, and aftewards you can assign a type to your concrete entity.
The primary purpose of "entity" is to establish a hierarchy / tree of nested entities by parent_id and with different "types" (in the type column of entity). There will be some different meta data, but let's not focus on that.
Note: entity can be named anything, and the type can be anything.
In the API we then have an endpoint where we can get all entities with a specific type (sidenote: and in addition to the single type on an entitiy we also have an endpoint to get all entities by their taxonomy/term).
In the first implementation I modeled the schema by adding all the "known" types I had in my specification from the UX'er during development. The tree of entities could be like eg.
Company (or Organization, ..., Corporation... etc)
Branch (or Region, ..., etc)
Factory (or Building, facility, ..., etc)
Zone (or Room, ..., etc)
But this hierarchy is just one way it could be done. The naming of each might be totally different, and you might move some of them a level up or down or not have them at all, depending on the use case.
Only thing that is set in stone is that they share the same database table, will have the type column/field defined and they may or may not have children. The bottom layer in the hierarchy will not have children, but machines instead. The rest of just diffent metadata, which I think we should ignore for to not complicate this further.
As you can see the hierarchy needs to be very flexible and dynamic, so I realized it wasn't a great solution I had begun on.
At the lowest level "Zone" in this case, there will need to be a "machines" field, which should return a list of machines (they are in a "machines" table in the db, and not part of the hierarchy, but simply related with an "entity_id" on the "machines" table.
I had schema types and resolvers for all in the above hierarchy: Organization, Branch, Factory, Zone etc, but I was for the most part just repeating myself, so I thought I could turn to interfaces to try to generalize this more.
So instead of doing
{
companies{
name
branchCount
buildingCount
zoneCount
branches {
name
buildingCount
zoneCount
buildings {
name
zoneCount
zones {
name
machines {
name
}
}
}
}
}
}
And having to add schema/resolvers for all the different namings of the entities, I thought this would work:
{
entities(type: "companies") {
name
... on HasEntityCount {
branchCount: entityCount(type: "branch")
buildingCount: entityCount(type: "building")
zoneCount: entityCount(type: "zone")
}
... on HasSubEntities {
entities(type: "branch") {
name
... on HasEntityCount {
buildingCount: entityCount(type: "building")
zoneCount: entityCount(type: "zone")
}
... on HasMachineCount {
machineCount
}
... on HasSubEntities {
entities(type: "building") {
name
... on HasEntityCount {
zoneCount: entityCount(type: "zone")
}
... on HasMachineCount {
machineCount
}
... on HasSubEntities {
entities(type: "zone") {
name
... on HasMachines {
machines
}
}
}
}
}
}
}
}
}
With the interfaces being:
interface HasMachineCount {
machineCount: Int
}
interface HasEntityCount {
entitiyCount(type: String): Int
}
interface HasSubEntities {
entities(
type: String
): [Entity!]
}
interface HasMachines {
machines: [Machine!]
}
interface Entity {
id: ID!
name: String!
type: String!
}
The below works, but I really want to avoid a single type with lots of optional / null fields:
type Entity {
id: ID!
name: String!
type: String!
# Below is what I want to avoid, by using interfaces
# Imagine how this would grow
entityCount
machineCount
entities
machines
}
In my own logic I don't care what the entities are called, only what fields expected. I'd like to avoid a single Entity type with alot of nullable fields on it, so I thought interfaces or unions would be helpful for keeping things separated so I ended up with HasSubEntities, HasEntityCount, HasMachineCount and HasMachines since the bottom entity will not have entities below, and only the bottom entity will have machines. But in the real code there would be much more than the 2, and it could end up with a lot of optional fields, if not utilizing interfaces or unions in some way I think.
There's two separate problems here.
One, GraphQL resolves fields in a top down fashion. Parent fields are always resolved before any children fields. So it's never possible to access the value that a field resolved to from the parent field's resolver (or a "sibling" field's resolver). In the case of fields with an abstract type, this applies to type resolvers as well. A field type will be resolved before any children resolvers are called. The only way to get around this issue is to move the relevant logic from the child resolver to inside the parent resolver.
Two, assuming the somethings field has the type Something (or [Something], etc.), the query you're trying to run will never work because HasBarCount and HasBazCount are not subtypes of Something. When you tell GraphQL that a field has an abstract type (an interface or a union), you're saying that what's returned by the field could be one of several object types that will be narrowed down to exactly one object type at runtime. The possible types are either the types that make up the union, or types that implement the interface.
A union may only be made up of object types, not interfaces or other unions. Similarly, only an object type may implement an interface -- other interfaces or unions may not implement interfaces. Therefore, when using inline fragments with a field that returns an abstract type, the on condition for those inline fragments will always be an object type and must be one of the possible types for the abstract type in question.
Because this is pseudocode, it's not really clear what business rules or use case you're trying to model with this sort of schema. But I can say that there's generally no need to create an interface and have a type implement it unless you're planning on adding a field in your schema that will have that interface as its type.
Edit: At a high level, it sounds like you probably just want to do something like this:
type Query {
entities(type: String!): [Entity!]!
}
interface Entity {
type: String!
# other shared entity fields
}
type EntityWithChildren implements Entity {
type: String!
children: [Entity!]!
}
type EntityWithModels implements Entity {
type: String!
models: [Model!]!
}
The type resolver needs to check for whether we have models, so you'll want to make sure you fetch the related models when you fetch the entity (as opposed to fetching them inside the models resolver). Alternatively, you may be able to add some kind of column to your db that identifies an entity as the "lowest" in the hierarchy, in which case you can just use this property instead.
function resolveType (obj) {
return obj.models ? 'EntityWithModels' : 'EntityWithChildren'
}
Now your query looks like this:
entities {
type
... on EntityWithModels {
models { ... }
}
... on EntityWithChildren {
children {
... on EntityWithModels {
models { ... }
}
... on EntityWithChildren {
# etc.
}
}
}
}
The counts are a bit trickier because of the variability in the entity names and the variability in the depth of the hierarchy. I would suggest just letting the client figure out the counts once it gets the whole graph from the server. If you really want to add count fields, you'd have to have fields like childrenCount, grandchildrenCount, etc. Then the only way to populate those fields correctly would be to fetch the whole graph at the root.
I know from the doc I can alias a field. But how could I alias a type?
E.g. how could I alias a int or string type? Will type MyStringType = String work?
Motivation
I have a signin mutation that returns an auth token, and I would like to write something like:
type Mutation {
signin(email: String!, password: String!): AuthToken
}
type AuthToken = String
GraphQL does not support type aliases.
You can, however, implement a custom scalar with the same properties as an existing scalar but a different name. It's unclear from your question what language or libraries you're working with, but in GraphQL.js, you can just do something like:
const { GraphQLString, GraphQLScalarType } = require('graphql')
const AuthToken = new GraphQLScalarType({
name: 'AuthToken',
description: 'Your description here',
serialize: GraphQLString.serialize,
parseValue: GraphQLString.parseValue,
parseLiteral: GraphQLString.parseLiteral,
})
Here's how to add a custom scalar in Apollo Server. Keep in mind that doing this may actually make things harder for clients, especially ones written in strongly typed languages. If you don't need custom serialization or parsing behavior, I would stick to the built-in scalars.
If I want to use interface/implements without any arguments, how would I do that?
My current code looks like this:
interface User {
GUID: String
name: String
age: Int
}
type FacebookUser implements User
type GoogleUser implements User
The reason I'm doing it like this, is to handle FacebookUsers age getting and GoogleUsers age getting different in the Resolvers-function.
And if they aren't asking for age, I don't want to get them at all.
If I add this, it works:
interface User {
GUID: String
name: String
age: Int
}
type FacebookUser implements User {
bogus: String
}
type GoogleUser implements User {
bogus: String
}
But there is nothing that separates the users except their underlying system, so I don't want to add specific fields to them.
I'm using Apollo GraphQL Tools 3.0.2
Let's say i have a GraphQL Schema like:
type author {
id: Int
name: String
rating: Int
}
You can use this type definition to generate an executable schema that can be queried on.
But in case i don't have the above type definitions, but only have GraphQLSchema object obtained through the introspection query, how do i generate the above type definitions?
I checked out graphql/utilities which had a printSchema method, that accepts a GraphQLSchema object and prints a string. But i can't seem to filter the output for a particular node alone, since i don't want the entire schema. Parsing the string output is not desirable.
Pointing to right methods is appreciated.
Unlike printSchema it is not mentioned in the docs, but graphql-js exports printType method exactly for this! See in the sources. For example with graphql-iso-date.
const { printType } = require('graphql');
const { GraphQLDate } = require('graphql-iso-date');
console.log(printType(GraphQLDate));
It prints:
"""
A date string, such as 2007-12-03, compliant with the `full-date` format
outlined in section 5.6 of the RFC 3339 profile of the ISO 8601 standard for
representation of dates and times using the Gregorian calendar.
"""
scalar Date
Which can be simply put to makeExecutableSchema functions typeDefs parameter.
Why GraphQL implements keyword need to duplicate the fields, is that mandatory? Like the examples in the document:
enum Episode { NEWHOPE, EMPIRE, JEDI }
interface Character {
id: String
name: String
friends: [Character]
appearsIn: [Episode]
}
type Human implements Character {
id: String
name: String
friends: [Character]
appearsIn: [Episode]
homePlanet: String
}
type Droid implements Character {
id: String
name: String
friends: [Character]
appearsIn: [Episode]
primaryFunction: String
}
If yes, what is the underlying reasons?
Coz If I have to duplicate, if I change then i need to change everywhere...
Yes, this is mandatory according to the current spec:
The object type must include a field of the same name for every field defined in an interface.
It allows you to specify more precisely types of fields in the derived type. Fields can be changed to be not-null or to some specific subtype of an interface or a union. Taking your example, let's assume that humans are friends only with other humans and the same for droids. Then the following schema is valid.
interface Character {
id: String
name: String
friends: [Character]
appearsIn: [Episode]
}
type Human implements Character {
id: String
name: String
friends: [Human] # <- notice Human here
appearsIn: [Episode]
homePlanet: String
}
type Droid implements Character {
id: String
name: String
friends: [Droid!]! # <- specified Droid here + added not null
appearsIn: [Episode]
primaryFunction: String
}
The object field may include additional arguments not defined in the interface field (but any additional argument must not be required).
Check out the spec for more details: Object type validation
Yes it's mandatory. If it helps, think of it as analogous to Java classes and interfaces. The interfaces have the type signatures, but cannot have an implementation. It is in the classes where you write out all the implementation, and the type signatures get repeated. This gives you the ability to choose subtypes or covariant types in the types signatures, so they might not be exactly the same.
Now suppose you are creating a GraphQL schema with the JavaScript objects from graphql-js. The interfaces are simply field names and types. The Object Type definitions themselves have the "implementation," or the resolve, resolveType, and other properties that actually make it an executable schema.
Your example, however, uses the schema language instead, which has no "implementation" at all. So they pretty much are an exact repetition of one another. You don't necessarily need to spell it all out every time, you could use string interpolation to share parts of the interface.
const characterFields = `
id: String
name: String
friends: [Character]
appearsIn: [Episode]
`
const typeDefs = `
interface Character {
${characterFields}
}
type Human Implements Character {
${characterFields}
homePlanet: String
}
`
EDIT: Analogous to the Java comparison, the fields may not be exactly the same type. As RomanHotsiy pointed out, you can make the types non-nullable or use subtypes of polymorphic types.