Introducing a "Connecting" Type in GraphQL - graphql

In my graphql schema, I have the two following (abbreviated) types.
type author {
id: Int!
name: String!
articles: [article!]
}
type article {
id: Int!
title: String!
abstract: String!
createdAt: DateTime!
}
That is to say, one author has many articles.
A new requirement has come in, which is to also show whether the author is the first or last named author on the article. How best to introduce this information into the schema without it being a breaking change? The following applies.
Both author and article have other relationships not shown. It doesn't make logical sense to have the new information sit directly on the article.
The information on the authors position in the article is in the link table in the database. Conceptually there is the author <-> author_article <-> article tables with the many to many illustrating the fact one author can publish many articles, and articles can have many authors. Ideas I've had.
Simply change the schema to the following.
type author {
id: Int!
name: String!
articles: [article!]
articleInformation: [articleInformation!]
}
type articleInformation {
firstAuthorship boolean
lastAuthorship boolean
article article
}
type article {
id: Int!
title: String!
abstract: String!
createdAt: DateTime!
}
to allow the querier to get the articleInformation if they choose, but this feels somewhat clunky.
use something like an interface that a normal article, and an "articleWithInformation" can implement.
Can someone with more experience than me at working with graphql offer some insight?

Related

How to query a third party package trait in Laravel lighthouse

I read Laravel Lighthouse documentation and searched around the web I did not find how to query a trait in a third party package in Laravel.
I'm using the qirolab/laravel-reactions package, it has the reactionSummary() trait.
I was asking how can I add this relation in the lighthouse query?
type Post {
id: ID!
title: String!
excerpt: String!
image_url: String!
slug: String!
source: Source! #belongsTo
reactionSummary: ???????
created_at: DateTime!
updated_at: DateTime!
}
I have a purpose with my question beside solving my issue, understanding how lighthouse work with packages or how to integrate third party packages with lighthouse?
You need to define the schema for the reaction model in graphql and in the post schema you need to define an array of the reaction type.
According to the model (https://github.com/qirolab/laravel-reactions/blob/master/src/Models/Reaction.php) the reaction graphql schema would look something like this:
type Reaction {
reactBy: User! #belongsTo
type: String
reactable: Reactable! #morphTo
}
Your post schema would change to
type Post {
id: ID!
title: String!
excerpt: String!
image_url: String!
slug: String!
source: Source! #belongsTo
reactionSummary: [Reaction]
created_at: DateTime!
updated_at: DateTime!
}
As I can see from the migration file https://github.com/qirolab/laravel-reactions/blob/master/migrations/2018_07_10_000000_create_reactions_table.php. The reaction has a polymorphic relation.
That means the returning type can vary depending on which kind of model is set as the reactable_type in the field. Therefore you need to define your own Union type.
A Union is an abstract type that simply enumerates other Object Types. They are similar to interfaces in that they can return different types, but they can not have fields defined.
Source: https://lighthouse-php.com/5/the-basics/types.html#union
See also polymorphic relations and the union section: https://lighthouse-php.com/5/eloquent/polymorphic-relationships.html#one-to-one
I hope that gives you the direction on how you can proceed.

Do I have to create a new type to define array of objects in GraphQL schemas?

I’m trying to replicate a REST API that I’ve built in the past and one part that got me thinking was if one of my tables had an array of objects. So for example, I have a table called Profile and it contains arrays Experience and Education that would strictly be under Profile but has its own fields as well but not its own table.
While I was adding fields in GraphQL, I bumped into this not really having a solid solution other than to create new types and then associating them with relationships and then have a resolver or a front-end make sure a Profile is created first before the Experience/Education portion is. I’m not sure if it’s the right way to do it or if there is a better way to do it. Down below is a snippet of what I ended up using… looking at the admin page, there are created tables for Profile, Experience and Education which is expected. But is there a way to only have just Profile and accomplish something similar? Or is this more of a way of life with GraphQL?
type Profile {
id: ID! #id
handle: String!
company: String
website: String
location: String
status: String!
githubUsername: String
experience: [Experience!] #relation(link: INLINE)
education: [Education!] #relation(link: INLINE)
}
type Experience {
id: ID! #id
title: String!
company: String!
}
type Education {
id: ID! #id
title: String!
company: String!
}
In Prisma, you can use embedded types. You would drop the #relation directive and add #embedded directives to the types you're embedding:
type Profile {
id: ID! #id
handle: String!
company: String
website: String
location: String
status: String!
githubUsername: String
experience: [Experience!]
education: [Education!]
}
type Experience #embedded {
title: String!
company: String!
}
type Education #embedded {
title: String!
company: String!
}
However, this only possible if you're using MongoDB for your database and there's some specific limitations listed in the docs when using embedded types.

Query to get data ordered by the number of items in a relation

Let’s say I have this typical datamodel, the one used in many tutorials:
type User {
id: ID! #unique
name: String!
posts: [Post!]!
}
type Post {
id: ID! #unique
title: String!
content: String!
published: Boolean! #default(value: "false")
author: User!
}
Is there a query I can build to get a list of, let’s say, the 10 Users with more Posts??
Basically I need to query ordering by “count” of Posts… but I haven’t found a way to do it
Any help will be highly appreciated
Cheers
As #shivam-panday said in a comment, this is currently not implemented in Prisma (See issue: https://github.com/prisma/prisma/issues/95 )
This comment especially explains your problem:
It would be great to be able to order by "to-many" related fields as well (by the count of related items).
For example, to get a list of the top 10 most voted-for links (assuming votes is a related field of type [Vote!]!):
query {
allLinks(first: 10, orderBy: votes_DESC) {
id
url
description
_votesMeta {
count
}
}
}
Currently, to get that list you'd have to query for every Link and then sort/slice it on the client, which is potentially a ton of overfetching.
Comment in question: https://github.com/prisma/prisma/issues/95#issuecomment-320433296
I came across the same issue with Prisma and this is a major problem. Retrieving all the users with all their posts, and sorting by the number of their posts is not a practical solution.
The workaround I can think of is to track the number of posts (every time when adding/deleting)and store it in User.
type User {
id: ID! #unique
name: String!
postsCount: Int!
posts: [Post!]!
}
This way, Prisma will expose the sorting options for the postsCount.
I am not sure if Prisma 2 offers a proper solution for this issue... Does anybody know?

GraphQL data modelling - extended types (Prisma)

In my Prisma Data Model I started out with a basic User type like this:
type User {
name: String!
email: String! #unique
password: String!
}
Now a User can have two roles: either as a candidate or as a user associated with an employer. If a candidate, the user should also have a set of applications and a set of qualifications, if associated with an employer it should have an access level and a reference to the employer.
First off, is there any way to extend basic types in GraphQL data modelling? If so, how would I go about doing it?
If there is not, I can see three different methods used, and I'm curious what are the pros and cons of each approach:
Having two separate types CandidateUser and EmployerUser, each with the fields name, email, password. I see two problems with this approach: The #unique tag on email is not reliable, and I would have to write a custom verification to make sure the field is unique across both types; and having a single login-function that takes email and fetches the users corresponding data is no longer trivial: it needs to do a lookup in both tables.
Like this:
type CandidateUser {
name: String!
email: String! #unique
password: String!
applications: [Application!]!
qualifications: [Qualification!]!
}
type EmployerUser{
name: String!
email: String! #unique
password: String!
employer: Employer!
accessRight: AccessRight!
}
Again two separate types, but with a RootUser containing name, email and password, and with CandidateUser and EmployerUser each having a one-to-one reference to a RootUser. This would enforce the #unique tag on the email field, but lookup would still be nontrivial.
type RootUser{
name: String!
email: String! #unique
password: String!
}
type CandidateUser {
rootUser: RootUser!
applications: [Application!]!
qualifications: [Qualification!]!
}
type EmployerUser{
rootUser: RootUser!
employer: Employer!
accessRight: AccessRight!
}
Extending User to have the fields within EmployerUser and CandidateUser as optional parameters. This is a pretty simple approach, but I would need custom handling to enforce requiring fields (as in, I can not mark for instance employer as required as that field would not exist for a Candidate).
type User{
name: String!
email: String! #unique
password: String!
applications: [Application!]!
qualifications: [Qualification!]!
employer: Employer
accessRight: AccessRight
}
I really want to ask if there is a better way of solving this. I'm still pretty new to GraphQL and not the best data modeler to begin with, but I'd greatly appraciate any nudge in the right direction :)
And if I do not have any other choice but the three I listed, which one would make the most sense?
What you're trying to do is implementing an interface type:
An Interface is an abstract type that includes a certain set of fields that a type must include to implement the interface.
interface User {
name: String!
email: String! #unique
password: String!
}
This means that any type that implements User needs to have these exact fields, with these arguments and return types. So now your Candidate type can implement User:
type Candidate implements User {
name: String!
email: String! #unique
password: String!
applications: [Application!]!
qualifications: [Qualification!]!
}
Interfaces are useful when you want to return an object or set of objects, but those might be of several different types. Have a look at the interface abstract type documentation for more information.
Update:
Since this is a Prisma GraphQL question now, you should be aware that Prisma does not support Interfaces or Union Types as yet. Issue #83 and issue #165 discuss both respectively as feature requests.
However, there is this great article that discuss the workarounds for such approach:
GraphQL Interfaces (and Union Types) with Prisma and Yoga
Which boils down to 2 options:
Storing all data with optional type-specific fields under one type (the interface) in Prisma, and then splitting the data back between the primitive types in the app server.
Storing the data in each primitive type on Prisma, and stitching things for queries on the app server.

GraphQL Prisma - define "vote" type that links to two users

I've just started using Prisma. Before was mainly using firebase and mongodb to define my schemas.
I'm trying to define the following schema:
Vote {
id: ID!
from: User! # The user who voted
for: User! # The user that received a vote
rate: Float!
}
Basically, what I want to achieve is enable users to vote for other users (give them a score).
In, say, MongoDB I would do it by creating a separate collection like following:
{
id: DocumentID
from: String // id of the user who voted
for: String // id of the user that received a vote
rate: Number
}
In here I just specify those fields (from and for) as strings and after link them with the User collection by the application logic.
For sure, it's gonna be different in GraphQL Prisma. But I'm still a bit confused on how the relationships are built. And what really happens underneath.
How can I create such schema using Prisma GraphQL?
When there are more than one relational field to the same type, you need to use the #relation directive to make it unambiguous.
type Vote {
id: ID! #unique
votingUser: User! #relation(name: "VoteAuthor")
votedUser: User! #relation(name: "VoteReceiver")
rate: Float!
}
type User {
id: ID! #unique
receivedVotes: [Vote!]! #relation(name: "VoteReceiver")
givenVotes: [Vote!]! #relation(name: "VoteAuthor")
name: String!
}

Resources