How to combine multiple field in GraphQL Object - graphql

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.

Related

GraphQL query with multiple nested resolvers and mapping fields to arguments

From GraphQL Client's perspective, how do I perform a query with multiple nested resolvers where the fields from the parent are passed as arguments to the child resolver?
Here is a minimal example:
GraphQL Schema:
type Author {
id: ID!
name: String!
}
type Book {
id: ID!
title: String!
releaseDate: String!
}
type Query {
// Returns a list of Authors ordered by name, 'first' indicates how many entries to return
getAllAuthors(first: Int!): [Author]!
// Returns a list of Books ordered by releaseDate, 'first' indicates how many entries to return
getBooksByAuthorId(first: Int! authorId: ID!): [Book]!
}
Is it possible to write a query to get all authors and their last released book? Something around the lines:
query GetAuthorsWithLastBook($first: Int!) {
getAllAuthors(first: $first) {
authorId: id
name
lastBook: getBooksByAuthor(1, authorId) {
title
}
}
}
In the example above, I attempted to alias getAllAuthors.id as authorId and pass the alias down as argument to getBooksByAuthor(...) but that didn't work.
The key aspect of the problem is that I don't know the authorIds beforehand. I could fetch the authors first and build a query to fetch their last book but that will result in multiple queries and that is something I would like to avoid.
Update
A Java Kickstarter example is available here: https://www.graphql-java-kickstart.com/tools/schema-definition/
yes, on the graphql definition, you need to add lastBook in the Author
type Author {
id: ID!
name: String!
lastBook: [Book]
}
Next up u need to write the resolver for the lastBook
const resolvers = {
Query: {
Author {
lastBook: (parent, args) {
const userId = parent.id;
return getBooksByAuthor(userId, 1);
},
}
}
};

Is it possible to modify a graphql input model to reference an object using a composite key instead of id?

I have two models in my schema. 1) Tree - that uses ID as a primary key and 2) Attribute -that uses has a composite key. I would like to create a third table 3) TreeAttributes - that will reference 1st and 2nd objects, but when mutating I would like to reference the second object with a composite key.
I couldn't find anything like this in aws or Graphql documentations. I'm wondering if this is even possible?
Current schema looks something like this:
type Tree #model {
id: ID!
address: String!
description: String
attributes: [TreeAttributes] #connection (name: "treeAttributes")
}
type Attribute #model (subscriptions: null) #key(fields: ["name", "value"]) {
name: String!
value: [String]!
description: String
active: Boolean
trees: [TreeAttributes] #connection (name: "attributeTrees")
id: ID
}
type TreeAttributes #model(queries: null) {
id: ID!
tree: Tree! #connection (name: "treeAttributes")
attribute: Attribute! #connection (name: "attributeTrees")
}
Currently my input model looks like this:
input CreateTreeAttributesInput {
treeAttributesTreeId: ID!
treeAttributesAttributeId: ID!
}
but instead, i'd like it to look something like this:
input CreateTreeAttributesInput {
treeAttributesTreeId: ID!
treeAttributesAttribute: {
name: String!
value: String!}
}
Please refer to this document to construct many-to-many relationship between your models.
https://docs.amplify.aws/cli/graphql-transformer/directives#field-level-authorization
It is recommended to use new definition of the #connection, you can specify keyName and fields for it.

Indexing List Type field in a GraphQL type from within a Query

Say I have the following GraphQL Schema
query {
allAuthors: [Author]
}
type Author {
id: ID!
name: String!
books: [Book]
}
type Book {
id: ID!
name: String!
author: Author!
}
Now I can successfully run the following query to get all the authors and their associated books
query {
allAuthors {
name,
books {
name
}
}
}
However, if I only want to get the first three books for all authors, how would I go about doing something like that? Can we index the books field in the Author type from within a query? If so, how?
I tried something like this and it doesn't work
query {
allAuthors {
name,
books[3] {
name
}
}
}
GraphQL doesn't have syntax for this.
You can add a "limit" parameter to a field, and this is common enough:
type Query {
allAuthors(limit: Int, offset: Int): [Author!]!
}
type Author {
id: ID!
name: String!
books(limit: Int, offset: Int): [Book!]!
}
If you add parameters like this to the schema, then the query you want (for all authors, get the first three books) could look like
{
allAuthors {
name
books(limit: 3) {
name
}
}
}

Share structure between GraphQL schemas

I have a Apollo GraphQL server talking to an API returning responses with roughly the following structure:
{
"pagination": {
"page": 1,
// more stuff
},
sorting: {
// even more stuff
},
data: [ // Actual data ]
}
This structure is going to be shared across pretty much all responses from this API, that I'm using extensively. data is going to be an array most of the time, but can also be an object.
How can I write this in an efficient way, so that I don't have to repeat all these pagination and sorting fields on every data type in my schemas?
Thanks a lot!
I've sorted your problem by creating a lib called graphql-s2s. It enhances your schema by adding support for type inheritance, generic types and metadata. In your case, creating a generic type for your Paginated object could be a viable solution. Here is an example:
const { transpileSchema } = require('graphql-s2s')
const { makeExecutableSchema } = require('graphql-tools')
const schema = `
type Paged<T> {
data: [T]
cursor: ID
}
type Node {
id: ID!
creationDate: String
}
type Person inherits Node {
firstname: String!
middlename: String
lastname: String!
age: Int!
gender: String
}
type Teacher inherits Person {
title: String!
}
type Student inherits Person {
nickname: String!
questions: Paged<Question>
}
type Question inherits Node {
name: String!
text: String!
}
type Query {
students: Paged<Student>
teachers: Paged<Teacher>
}
`
const executableSchema = makeExecutableSchema({
typeDefs: [transpileSchema(schema)],
resolvers: resolver
})
I've written more details about this here (in Part II).
When you define your schema, you will end up abstracting out pagination, sorting, etc. as separate types. So the schema will look something like:
type Bar {
pagination: Pagination
sorting: SortingOptions
data: BarData # I'm an object
}
type Foo {
pagination: Pagination
sorting: SortingOptions
data: [FooData] # I'm an array
}
# more types similar to above
type Pagination {
page: Int
# more fields
}
type SortingOptions {
# more fields
}
type BarData {
# more fields
}
So you won't have to list each field within Pagination multiple times regardless. Each type that uses Pagination, however, will still need to specify it as a field -- there's no escaping that requirement.
Alternatively, you could set up a single Type to use for all your objects. In this case, the data field would be an Interface (Data), with FooData, BarData, etc. each implementing it. In your resolver for Data, you would define a __resolveType function to determine which kind of Data to return. You can pass in a typename variable with your query and then use that variable in the __resolveType function to return the correct type.
You can see a good example of Interface in action in the Apollo docs.
The downside to this latter approach is that you have to return either a single Data object or an Array of them -- you can't mix and match -- so you would probably have to change the structure of the returned object to make it work.

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