Get list of types from operation variables - graphql

I have an operation and I want to get a list containing its variables and types.
Eg. given the operation
query getResource($id: ID!, $title: String) {
resource(id: $id, title: $title) {
id
title
content
}
}
I want to get something like this
[{type: "ID", required: true, name: "id"}, {type: "String", required: false, name: "title"}]
Is this easily attainable?
Edit: I guess what I want to do is to go from a VariableDefinitionNode to an object containing all the information I want, such as type, defaultValue, required, name.

You should be able to implement this if you change your schema (assuming you can generate this data on the server into this shape)
type Query {
resource ($id: ID!, $title: String): [Variable]
}
type Variable {
type: String!
required: Boolean!
name: String!
...
}
This is a very generalised syntax however, returning an array of values like that. If the structure of your resources is known, you would be better off defining that and removing the name parameter as it becomes redundant. (I'm thinking about lookups inside your client-side application)
type Query {
resource ($id: ID!, $title: String): Resource
}
type Resource {
id: Variable!
title: Variable
content: Variable
}
type Variable {
type: String!
required: Boolean!
...
}

Related

How to defined non-null elements inside an array in GraphQL Nexus?

I'm using GraphQL Nexus to implement my GraphQL schema.
The target GraphQL type I want to create is this:
input UserCreateInput {
email: String!
name: String
posts: [PostCreateInput!]!
}
However, I'm not sure how I can create the PostCreateInput array such that the elements of the posts are are required as well.
Right now this is what I have:
input UserCreateInput {
email: String!
name: String
posts: [PostCreateInput]!
}
Which is backed by this Nexus type definition:
const UserCreateInput = inputObjectType({
name: 'UserCreateInput',
definition(t) {
t.nonNull.string('email')
t.string('name')
t.nonNull.list.field('posts', {
type: 'PostCreateInput',
})
},
})
Is there a way how I can tell Nexus that each array element should be non-null?
In this case, adding a nonNull after the list should suffice. So something like the following:
const UserCreateInput = inputObjectType({
name: 'UserCreateInput',
definition(t) {
t.nonNull.string('email')
t.string('name')
t.nonNull.list.nonNull.field('posts', {
type: 'PostCreateInput',
})
},
})

Custom schema, interface, #fileByRelativePath and gatsby-image

I'm trying to get an interface working with the new #fileByRelativePath resolver extension, to keep compatible with v3.
I'm using Prismic for my content, and gatsby-source-prismic v2. I have two content types in Prismic, and created the interface to be able to more easily query and map over both for a home page index.
Here's the functioning (but with deprecated inferred resolvers) schema:
exports.createSchemaCustomization = ({ actions }) => {
const { createTypes } = actions
const typeDefs = `
interface indexPosts #nodeInterface {
id: ID!
uid: String!
data: Data!
type: String!
}
type Data {
title: Title!
date: Date!
featured: String!
featured_image: Featured_image!
body: Body!
}
type Title {
text: String!
}
type Featured_image {
localFile: File!
}
type Body {
html: String!
}
type PrismicGallery implements Node & indexPosts {
uid: String!
data: Data!
type: String!
}
type PrismicEssay implements Node & indexPosts {
uid: String!
data: Data!
type: String!
}
`
createTypes(typeDefs)
}
The problem comes after adding #fileByRelativePath to the Featured_image type definition. Doing so gives me an error during build:
"The "path" argument must be of type string. Received type undefined"
I'm unsure how to provide the necessary path argument, considering my images are third-party hosted. I'm trying to follow the brief guide at the end of this page and suspect the way to do it might be with a resolver or type builder and using 'source' to access the url field provided by both localFile and its parent, featured_image, but I can't figure it out!
I'm using gatsby-image and the childImageSharp convenience field to present the images, if that makes a difference at all!
I had exactly the same problem when I tried to use #fileByRelativePath. I managed to solve my problem by using #infer on the type that contained the File.
Try this:
type Featured_image #infer {
localFile: File!
}

How to combine multiple field in GraphQL Object

I am trying my first Graphql Schema design. Is it possible that single field inside object type refer to a complex object?
enum KeyPrefix {
WS
WK
SI
ENT
}
input generalKey {
keyPrefix:KeyPrefix!
key:Int!
}
type Item
{
pk: generalKey!
data: String!
name: String!
}
It gives me error as below.
The type of Item.pk must be Output Type but got: generalKey!
input is a keyword reserved for describing input to a GraphQL query. Your schema should look like this:
enum KeyPrefix {
(your values)
}
type PrimaryKey {
prefix: KeyPrefix!
key: Int!
}
type Item {
pk: PrimaryKey!
data: String!
name: String!
}
When you define a Query in your GraphQL schema, you will want to use an input, like so:
input PrimaryKeyInput {
prefix: KeyPrefix!
key: Int!
}
type Query {
getItemByPrimaryKey(input: PrimaryKeyInput!): Item
}
This will allow a client to ask for an Item using the same fields you have on PrimaryKey.

GraphQL - variable not defined by operation

My GraphQL schema is defined as:
type Query {
getEntity(id: Int!): Entity
getEntityUsers(entityId: Int!, statusId: Int): [User]
}
type Entity {
id: Int!
name: String!
email: String!
logo: String
createdAt: DateTime!
updatedAt: DateTime!
users(statusId: Int): [User]
}
As you can see I have two ways of getting users for an Entity object. The one that is currently working for my query is the getEntityUsers root resolver method. This query looks like this:
query getEntityUsers($entityId: Int!, $statusId: Int) {
users: getEntityUsers(entityId: $entityId, statusId: $statusId) {
...
}
}
.. with the variables:
{
entityId: 1,
statusId: 2
}
Is there anyway to make the other way work by allowing me to pass in the statusId? Right now the query looks like this:
query getEntity($id: Int!) {
entity: getEntity(id: $id) {
...
users (statusId: 2) {
...
}
}
}
This obviously works with the variables:
{
id: 1
}
But, what if I wanted to use this second method and change the statusId? Is there anyway to pass in the statusId if it's not defined on the root resolver?
I have tried the query:
query getEntity($id: Int!) {
entity: getEntity(id: $id) {
...
users (statusId: $statusId) {
...
}
}
}
.. with the variables:
{
id: 1,
statusId: 2
}
But I just get the error: Variable "$statusId" is not defined by operation "getEntity". Is there anyway to do this?
Every operation (query or mutation) must explicitly define any variables you use inside that operation. So if you have a variable called $statusId, the type for this variable must be specified as part of your operation definition:
query getEntity($id: Int!, $statusId: Int) {
# your selection set here
}
Where those variables are used within your query (whether at the root level, or elsewhere) is irrelevant -- they must always be defined as part of your operation definition.

Graphql with nested mutations?

I am trying to figure out how to mutate a nested object with graphql mutations, if possible. For instance I have the following schema:
type Event {
id: String
name: String
description: String
place: Place
}
type Place {
id: String
name: String
location: Location
}
type Location {
city: String
country: String
zip: String
}
type Query {
events: [Event]
}
type Mutation {
updateEvent(id: String, name: String, description: String): Event
}
schema {
query: Query
mutation: Mutation
}
How can I add the place information inside my updateEvent mutation?
Generally speaking, you should avoid thinking of the arguments to your mutations as a direct mapping to object types in your schema. Whilst it's true that they will often be similar, you're better off approaching things under the assumption that they won't be.
Using your basic types as an example. Let's say I wanted to create a new event, but rather than knowing the location, I just have the longitude/latitude - it's actually the backend that calculates the real location object from this data, and I certainly don't know its ID (it doesn't have one yet!). I'd probably construct my mutation like this:
input Point {
longitude: Float!
latitude: Float!
}
input PlaceInput {
name
coordinates: Point!
}
type mutation {
createEvent(
name: String!
description: String
placeId: ID
newPlace: PlaceInput
): Event
updateEvent(
id: ID!
name: String!
description: String
placeId: ID
newPlace: PlaceInput
): Event
)
A mutation is basically just a function call, and it's best to think of it in those terms. If you wrote a function to create an Event, you likely wouldn't provide it an event and expect it to return an event, you'd provide the information necessary to create an Event.
If you want to add a whole object to the mutation you have to define a graphql element of the type input. Here is a link to a small cheatsheet.
In your case it could look like this:
type Location {
city: String
country: String
zip: String
}
type Place {
id: String
name: String
location: Location
}
type Event {
id: String
name: String
description: String
place: Place
}
input LocationInput {
city: String
country: String
zip: String
}
input PlaceInput {
id: ID!
name: String!
location: LocationInput!
}
type Query {
events: [Event]
}
type Mutation {
updateEvent(id: String, name: String, description: String, place: PlaceInput!): Event
}
schema {
query: Query
mutation: Mutation
}

Resources