Nested mutations don't seem to work in Lighthouse 3.7 - laravel

I'm trying to setup a schema in Lighthouse 3.7/Laravel 5.8
What I want to achieve is this:
A User should be able to create Clists.
There is a One to Many relation between User and Clist.
And I'm trying to implement nested mutations as described here.
I have already implemented the "query" part and it works fine.
But when I test a createClist mutation in GraphQL Playground, I get this error:
"debugMessage": "Array to string conversion",
"message": "Internal server error",
"extensions": {
"category": "internal"
},
...
And I can't figure out what I'm doing wrong.
Here is my code:
type Mutation {
createClist(input: CreateClistInput! #spread): Clist #create
}
input CreateClistInput {
name: String!
description: String
starred: Boolean
user: CreateUserRelation!
ctags: CreateCtagRelation
}
input CreateUserRelation {
connect: ID!
}
input CreateCtagRelation {
create: [CreateCtagInput!]
connect: [ID!]
sync: [ID!]
}
input CreateCtagInput {
name: String!
}
And here is a screenshot of GraphQL Playground:

When using the #spread directive a typehint on the relationship in your model is required.
Taken from the docs there is the following example:
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Post extends Model
{
// WORKS
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
// DOES NOT WORK
public function comments()
{
return $this->hasMany(Comment::class);
}
}
Lighthouse uses the type hint to determine how it should handle the relationship.

Related

How to add graphql pagination on nested object

I am writing a graphql api endpoint where I have customer details as below.
{
customer_name:
customer_address:
[
{
address_1:
},
{
address_2:
},
]
}
I need to apply pagination on customer_address which is a list.
Is this possible? Or I can do it only at top level record? Please let me know what would be the best way to do it?
You can possible by using resolver like following
input PageInput{
limit: Int!
page: Int!
}
type CustomerAddressPage {
totalCount: Int!
edges: [CustomerAddress!]!
}
type CustomerAddress {
address: String
}
type Customer {
customerName: String
customerAddress(input: PageInput!): CustomerAddressPage
}
I don't know what kind of framework you use, in nestjs you can be done as follows.
#Resolver(() => Customer)
export class CustomerResolver {
#ResolveField(() => CustomerAddressPage)
customerAddress(
#Parent() customer: Customer,
#Args('input') input: PageInput,
): Promise<CustomerAddressPage> {
return {
};
}
}

Error Cannot return null for non-nullable type: 'String' within parent MyModelType' (/createMyModelType/id)

I am trying to trigger a mutation in the aws console. I have linked my resolver function to a None type data source.
However, when I define my mutation with an input type as a parameter, the error " Error Cannot return null for non-nullable type: 'String' within parent MyModelType' (/createMyModelType/id)." occurs. Everything is fine though if I replace the input type with key word arguments.
I am certain it has to do with my resolver mapping template.
Just if you're wondering why I am using a None type, I want to be able to trigger a subscription without making real database changes or mutations.
I am not sure how to make it work with input types. Here is my code for the template:
{
"version": "2017-02-28",
"payload": $util.toJson($context.args)
}
My Schema:
input CreateMyModelType5Input {
title: String
}
type Mutation {
createMyModelType5(input: CreateMyModelType5Input!): MyModelType5
}
type MyModelType5 {
id: ID!
title: String
}
type Subscription {
onCreateMyModelType5(id: ID, title: String): MyModelType5
#aws_subscribe(mutations: ["createMyModelType5"])
}
Query I am trying to run:
mutation createMyModelType($createmymodeltypeinput: CreateMyModelTypeInput!) {
createMyModelType(input: $createmymodeltypeinput) {
id
title
}
}
Query Variables for the mutation query
{
"createmymodeltype5input": {
"title": "Hello, world!"
}
}
So I have been working on passing my arguments in the graphql mutation and using the input type seemed the only straight forward way around.
However, I have been able to do it with this way:
mutation = """mutation CreateMyModelType($id: String!, $title: String!){
createMyModelType(id: $id, title: $title){
id
title
}
}
"""
input_params = {
"id": "34",
"title": "2009-04-12"
}
response = app_sync.createMyModelType(mutation, input_params)
this can be a good guide

How to document errors in graphql?

type Query {
"""
ErrorCode: EMAIL_DUPLICATED
type EmailDuplicatedError {
email: String!
source: UserSource!
}
enum UserSource {
Google
Facebook
Github
}
"""
register(email: String!, password: String!): AccessToken!
}
"""
The AccessToken scalar type is a string of 16 characters.
"""
scalar AccessToken
Hope you can get what I mean through the above schema. I'd like to know if there is any code generator can support errors documented this way, so I can reduce the code I write on both client and server side.
I don't want to define errors like the following
type Query {
register(email: String!, password: String!): RegisterResponse
}
type RegisterResponse {
accessToken: AccessToken
error: EmailDuplicatedError
}
type EmailDuplicatedError {
email: String!
source: UserSource!
}
enum UserSource {
Google
Facebook
Github
}
"""
The AccessToken scalar type is a string of 16 characters.
"""
scalar AccessToken
Because I'd like errors to be responded in errors field, and api only shows what you can get when you succeeded.
Thank you for your time reading this post.
There's some ways to do error handling with GraphQL, I'll recommend you two:
Using response extensions:
GraphQL JSON response.error has a field called extensions, you can use this to set a code field like:
{
"error": {
"errors": [
{
"message": "example",
"extensions": {
"code": "YOUR CODE"
}
}
]
}
}
Using unions:
There is an medium post by Sasha Solomon that talks about that:
https://sachee.medium.com/200-ok-error-handling-in-graphql-7ec869aec9bc
Using the examples in this post, the way to handle graphql errors with unions is like this:
type User {
id: ID!
name: String
}
type Suspended {
reason: String
}
type IsBlocked {
message: String
blockedByUser: User
}
type UnavailableInCountry {
countryCode: Int
message: String
}
"User, or reason we can't get one normally"
union UserResult = User | IsBlocked | Suspended
type Query {
user: UserResult!
}

Eloquent team-based roles

I have 3 tables: Roles, Teams, Users with a pivot table between each: role_team, role_user, team_user.
I'm having a hard time leveraging Eloquent to return only the roles that a user has for a specific team.
$team = Team::find(1);
foreach($team->users as $user) {
dump($user->teamRoles); // Get the roles this user has for the team
}
While I could do $user->roles()->where('team_id', $team->id)->get(), I'd like to specify it as a relationship. I tried setting up a hasManyThrough, but it doesn't seem to work in this specific case.
The need to use this as a relationship rather than query is because I'm using Lighthouse PHP for GraphQL and would like to easily be able to query for the roles like:
teams {
id name
users {
teamPivot {
roles { id name }
}
}
}
Any help leveraging Eloquent to make this happen would be greatly appreciated.
One possible solution, though not necessarily the one I'm looking for is to use the #method directive on a field.
Imagine the following schema:
type User {
id: ID!
email: String!
teams: [Team] #belongsToMany
teamMeta: TeamUser
}
type Team {
id: ID!
name: String!
users: [User] #belongsToMany
userMeta: TeamUser
}
type Role {
id: ID!
name: String!
team: Team #belongsTo
users: [User] #belongsToMany
}
type TeamUser {
user: User!
team: Team!
roles: [Role] #method(name: "getTeamRoles")
}
Where getTeamRoles looks like:
public function getTeamRoles()
{
return $this->user->roles()->where('team_id', $this->team->id)->get();
}
This configuration will allow the following GraphQL to work as desired:
users(first: 1, input: { id: 2 }) {
email teams {
name userMeta {
contactedAt
roles { id name }
}
}
}
This is currently the solution I'm running, but it would be nice to have a "pure" Eloquent answer for this instead of having to write a custom accessor for each relation of this type.
I think that you can achieve what you want by using Many to Many relationship.
Basically you will need to define write a method that returns the result of the belongsToMany method in both User and Roles models.
It will be something like this:
In User.php
public function roles(){
return $this->belongsToMany('App\Role');
}
In Role.php
public function users(){
return $this->belongsToMany('App\User');
}
Then, you will be able to do something like:
$team = Team::find(1);
foreach($team->users as $user) {
dump($user->roles);
}
For more reference you can see the official documentation: https://laravel.com/docs/6.x/eloquent-relationships#many-to-many
I hope this helps.

Cannot perform update query because update values are not defined

I am trying to execute a mutation like so
mutation creating {
createTeam(
payload: {
name: "Team von abc"
tacts:["94b4cbc2-b996-482f-b712-967bdb646e73"]
}
) {
id
name
}
}
This results in :
"message": "Cannot perform update query because update values are not
defined. Call \"qb.set(...)\" method to specify updated values.",
My graphql is defined like this:
input CreateTeamPayload {
name: String
tacts:[ID!]
}
type Team {
id: ID!
name: String
tacts: [Tact]!
}
type Query {
fetchTeams: [Team]!
fetchTeamById(id: ID!): Team
}
type Mutation {
createTeam(payload: CreateTeamPayload): Team!
}
My Team requires an ID from a "tact" so I provide him with an ID from a "tact" I created before. Is this approach wrong? How can I mutate types that reference other types? is there some documentation that actually does this?

Resources