How to use the query for custom fields on GraphQL? - graphql

I have this schema on my graphcool:
type User #model {
id: ID! #isUnique
name: String!
email: String!
password: String!
}
Using playground, I can execute this properly:
query {
User(id: "1234") {
id
name
}
}
But this query:
query {
User(name: "Thomas") {
id
name
}
}
throws error:
Unknown argument 'name' on field 'User' of type 'Query'. (line 2,
column 8):
User(name: "Thomas").
Why? And how to fix this? From my pov, anything that's already on the model, can be queried immediately, right? Btw, I'm very newbie in graphQL, and there's almost no article talk about this error (every tutorial just like assume that this will immediately works), so please give some more elaborate answer if necessary.

GraphQL does not intrinsically allow arbitrary queries against objects.
Somewhere in your schema there will be an additional declaration like
type Query {
User(id: ID!): User
}
The names in the Query type are the top-level queries you can run, and the arguments listed in that query are the only arguments they accept. (There is a corresponding Mutation type for top-level mutations, which can change the underlying state, and use the mutation keyword in a query.)
If you control the server implementation, you could add a parameter or an additional top-level query
userByName(name: String!): User
but you'd also have to provide an implementation of this query or handle the additional parameter, which is a code change.

Related

Graphql types, should they represent db fields?

In Graphql, I have the following mutation:
activateUserAccount(token: String!): ActivateUserAccountResult!
the type is currently:
type ActivateUserAccountResult{
id: Int
activated_on: String
}
The problem with this is that the type ActivateUserAccountResult will most likely never get used by anything else. So I was wondering if i created a more generic UserAccount type as follows and returned that instead, can you think of why this might be bad?
activateUserAccount(token: String!): UserAccount !
type UserAccount {
id: Int
activated_on: String
email: String
... other db fields here
}
The client would obviously still only request id and activated_on and ignore the other fields. The only downside i can think of is that you cant enforce activated_on by adding an exclamation mark (activated_on!).
Which is better practise? Better re-usability or more concise?

How to use custom input types in AppSync GraphQL schema?

I'm experimenting with AppSync + DynamoDB. I want to have the following types in my GraphQL Schema:
type User {
user_id: String!
}
type Box {
name: String!
user: User!
}
How can I create, in DynamoDB, a table storing items pointing to another table (In my case, I want the field user of the table BoxTable to be a reference to a user in the table UserTable?
How can I, in AppSync, define the above schema? When I set user: User!, I get the error Expected User! to be a GraphQL input type.?
As per my understanding of your question, these are my answers.
How can I create, in DynamoDB, a table storing items pointing to another table
DynamoDB is not a relational database and does not offer foreign keys or table joins. Therefore, to achieve what you have mentioned in your post, you would still require two calls to DynamoDB to get all the information for the Box i.e. first get the Box item from BoxTable and then get user from UserTable based on user_id. If your use case is such that you get user first, then you can get the Box using filter by user_id.
Now to the second part of your post,
How can I, in AppSync, define the above schema?
With DynamoDB unit resolvers, you can query against a single table (outside of DynamoDB Batch Operations but those are reserved for bulk use cases).
One way of doing this is by defining your schema that should look something like this;
type User {
user_id: String!
}
type Box {
name: String!
user: User!
}
input BoxInput {
name: String!
user: UserInput!
}
input UserInput {
user_id: String!
}
type Mutation {
createBox(input: BoxInput): Box
}
type Query {
getBox(input: BoxInput): Box
}
And this is how you can run query and mutation;
mutation createBox {
createBox(input: {
name: "abc"
user: { user_id: "1234-abcd-5678-efgh"}
}){
name
user { user_id }
}
}
query getBox {
getBox(input: {
name: "abc"
user: { user_id: "1234-abcd-5678-efgh"}
}){
name
user { user_id }
}
}
So, beware of the above query and mutation. These will show user as null unless you attach a separate resolver with you user type within your Box type. For example:
Query that returns Box --> Resolver
type Box {
name
user --> Attach resolver to get user_id from your UserTable
}
Other way is to utilize the pipeline resolvers in which you can create multiple functions, each of which can use the results of the previous function and query a database. These functions run in an order you specify. For example:
Function to get Box from BoxTable.
Function to get user from UserTable by using user_id from ctx.prev.result.
And finally consolidating above two result into one JSON object depending upon Box type in your schema.

Passing variables in GraphQL

I'm trying to run a GraphQL query in the AWS AppSync console:
query MyQuery {
getUserInfoById(id: "1234566789") {
account {
id // need this value for getAvailableCourses
}
}
getAvailableCourses(accountId: "", pageNumber: 0) {
data {
id
name
type
}
}
}
Basically I need the value account.id in getUserInfoById for getAvailableCourses. I'm obviously new to GraphQL. How would I go about this?
To the best of my knowledge, there can be two ways you can do this.
You can handle this in your frontend by getting user's id
from the session info and pass it to the other query.
You can also merge these two queries and make it one. You will also have to change the respective fields. Then attach a resolver with AvailableCourses and use $ctx.source.id in the resolver to get further details. Schema would look something like this
type Account {
id : ID!
availableCourses: AvailableCourses
..
}
type AvailableCourses {
name: String!
type: String!
..
}
type Query {
getUserInfoById(id: ID!): Account
}
Using the returned fields as inputs for a second query into your datasource is precisely what field resolvers are for. I can't say for sure since I don't know your schema or access patterns but it looks like you need to make available courses a sub field of the user.

Can one have different types for same field between Prisma GraphQL schema and datamodel?

I'm a newbie to Prisma/GraphQL. I'm writing a simple ToDo app and using Apollo Server 2 and Prisma GraphQL for the backend. I want to convert my createdAt field from the data model to something more usable on the front-end, like a UTC date string. My thought was to convert the stored value, which is a DateTime.
My datamodel.prisma has the following for the ToDo type
type ToDo {
id: ID! #id
added: DateTime! #createdAt
body: String!
title: String
user: User!
completed: Boolean! #default(value: false)
}
The added field is a DataTime. But in my schema.js I am listing that field as a String
type ToDo {
id: ID!
title: String,
added: String!
body: String!
user: User!
completed: Boolean!
}
and I convert it in my resolver
ToDo: {
added: async (parent, args) => {
const d = new Date(parent.added)
return d.toUTCString()
}
Is this OK to do? That is, have different types for the same field in the datamodel and the schema? It seems to work OK, but I didn't know if I was opening myself up to trouble down the road, following this technique in other circumstances.
If so, the one thing I was curious about is why accessing parent.added in the ToDo.added resolver doesn't start some kind of 'infinite loop' -- that is, that when you access the parent.added field it doesn't look to the resolver to resolve that field, which accesses the parent.added field, and so on. (I guess it's just clever enough not to do that?)
I've only got limited experience with Prisma, but I understand you can view it as an extra back-end GraphQL layer interfacing between your own GraphQL server and your data (i.e. the database).
Your first model (datamodel.prisma) uses enhanced Prisma syntax and directives to accurately describe your data, and is used by the Prisma layer, while the second model uses standard GraphQL syntax to implement the same object as a valid, standard GraphQL type, and is used by your own back-end.
In effect, if you looked into it, you'd see the DateTime type used by Prisma is actually a String, but is likely used by Prisma to validate date & time formats, etc., so there is no fundamental discrepancy between both models. But even if there was a discrepancy, that would be up to you as you could use resolvers to override the data you get from Prisma before returning it from your own back-end.
In short, what I'm trying to say here is that you're dealing with 2 different GraphQL layers: Prisma and your own. And while Prisma's role is to accurately represent your data as it exists in the database and to provide you with a wide collection of CRUD methods to work with that data, your own layer can (and should) be tailored to your specific needs.
As for your resolver question, parent in this context will hold the object returned by the parent resolver. Imagine you have a getTodo query at the root Query level returning a single item of type ToDo. Let's assume you resolve this to Prisma's default action to retrieve a single ToDo. According to your datamodel.prisma file, this query will resolve into an object that has an added property (which will exist in your DB as the createdAt field, as specified by the #createdAt Prisma directive). So parent.added will hold that value.
What your added resolver does is transform that original piece of data by turning it into an actual Date object and then formatting it into a UTC string, which conforms to your schema.js file where the added field is of type String!.

How to deal with nested input in GraphQL

When writing queries I can define a resolver on any field and that field’s value will be determined by its resolver, regardless of query depth.
However when writing a mutation I seem to only be able to define the resolvers at the root level. Adding a resolve method to fields in my args or input type does not seem to have any affect.
What’s the best way deal with nested input in mutations?
What do you mean by nested input in your mutations? GraphQL input types does not have resolvers. With resolvers you are just determining how to fetch results. If you would like to have nested input, e.g. for example I would like to create user also with company. I will then define CreateUserInput CreateCompanyInput type for example like this in SDL
input CreateCompanyInput {
name: String!
type: CompanyEnum!
}
input CreateUserInput {
username: String!
firstname: String!
lastname: String!
company: CreateCompanyInput!
}
type Mutation {
createUser(input: CreateUserInput!): User
}
This way I am basically nesting arguments and can implement more complex mutations. In addition I can reuse the CreateCompanyInput for createCompany mutation if I need mutation even for that. I will then have the whole CreateUserInput even with CreateCompanyInput in the createUser resolver as input argument. I can apply transactions as I will create two new records etc. Not sure if it is what you mean by nested input if you mean something else. Just let me know :)

Resources