Graphql - How to include schema from other types - graphql

Let us say I have the following type:
type Foo {
id: ID!
field1: String
}
Now, I wish to define another type, which includes the earlier type. Something like this:
type Bar {
...Foo,
field2: String
}
How do I achieve the above in graphql? I want to basically first create a type, and then include that type in the definition of other types so that I don't have to type all the attributes multiple times.
I am using Amplify / AWS Appsync so if there's any special directive that I could use that would also be helpful

GraphQL has the concept interfaces for this. Appsync, AWS's GraphQL implementation, supports interfaces.
[Edit:] GraphQL does not support "...spread" syntax for interfaces. Fields are defined explicitly. Spread syntax does figure in GraphQL, but in the form of Fragments, resuable units of fields for reducing repetition in queries.
interface Character {
id: ID!
name: String!
friends: [Character]
appearsIn: [Episode]!
}
type Human implements Character {
id: ID!
name: String!
friends: [Character]
appearsIn: [Episode]!
starships: [Starship]
totalCredits: Int
}
type Droid implements Character {
id: ID!
name: String!
friends: [Character]
appearsIn: [Episode]!
primaryFunction: String
}
Amplify, which automagically creates AppSync schemas, resolvers and data sources, is apparently a more difficult story. The amplify-cli repo has an open feature request, Does the GraphQL Transformer support interfaces?. I am no Amplify expert, but a quick look at the loooong feature request comment thread suggests the answer for Amplify is "not out-of-the-box", but "maybe works in narrow circumstances or with advanced customization".

Related

Laravel lighthouse graphql query by relation table value

Is it possible in Laravel lighthouse GraphQL to query by relation table values?
For example, I have defined two types.
type Translation {
id: ID!
form_group_id: Int!
group: Group #hasOne
translation_key: String!
lv: String
en: String
ru: String
}
type Group {
id: ID!
name: String!
translations: [Translation]! #hasMany
}
and I want to query by group name.
The code below is just an example of the idea.
translation(group.name: String! #eq): Translation #find
I have an idea that this, can be possible with custom resolver, but is there a way using built in eloquent resolvers?
Event tho this is an experimental feature, you can use complex where conditions. This will allow you to fetch whatever type by its relation attributes. We use it on our application, and for the use we have, this is working as expected :)

Apollo-tooling: including a defined type in another defined type

This may be a basic Graphql question or it may be related Apollo Tooling.
I am trying to use Apollo Tooling to generate typescript types from my client side schema. I have a NavItem type which looks like this:
type NavItem {
id: ID!
to: String!
icon: String!
text: String!
highlight: String!
children: [NavItemChild]
}
type NavItemChild {
id: ID!
to: String!
icon: String!
text: String!
highlight: String!
}
Basically a NavItem can have multiple NavItemChildren. When I go to generate types using apollo codegen:generate src/graphql/types --target=typescript --outputFlat I get an error
Field "children" of type "[NavItemChild]" must have a selection of subfields. Did you mean "children { ... }"?
What am I doing wrong and how should I correct it?
The problem was in the query I was trying to generate types for:
It looks, in part, like this:
links {
to,
icon,
text,
highlight,
children,
}
Since we declared children as a non primitive type (String, Int etc) its expected that we define the sub fields that we expect to get back. Therefore changing it to
links {
to,
icon,
text,
highlight,
children {
to,
icon,
text,
highlight,
}
}
works fine

GraphQL & Prisma: why does one redefine types in the application schema when they are already part of the Prisma database schema?

Hi — I’ve been following along with a GraphQL/Prisma tutorial (https://www.howtographql.com/graphql-js/6-authentication/) and I’m wondering why one redefines types in the application schema when they are already part of the Prisma database schema and could be imported from there.
The answer the tutorial gives is “To hide potentially sensitive information from client applications”. What does this mean exactly? Why do we replicate definitions in ‘schema.graphql’ and ‘datamodel.prisma’? Because the definitions are slightly different (i.e. the 'datamodel' contains tags like #unique)? And how are we hiding things from client applications? I remain perplexed....
Specifically in ‘schema.graphql’ I have
type User {
id: ID!
name: String!
email: String!
links: [Link!]!
}
and in 'datamodel.prisma' I have
type User {
id: ID! #unique
name: String!
email: String! #unique
password: String!
links: [ Link!] !
}
The schema doesn't have the password field, that is likely what is meant by "hide potentially sensitive information".
This is common practice in any API to not return all data from the persistent storage.

AWS-Amplify API module: how to make GraphQL fields unique?

AWS-Amplify provides a couple of directives to build an GraphQL-API. But I haven't found out how to ensure uniqueness for fields.
I want to do something like in GraphCool:
type Tag #model #searchable {
id: ID!
label: String! #isUnique
}
This is an AWS-Amplify specific question. It's not about how to do this with generic GraphQL. It's very specifically about how to do this with AWS-Amplify's API module. (https://aws-amplify.github.io/docs/js/api)
Hey thanks for the question. This is not yet possible by default using the amplify-cli but you could do this yourself using pipeline resolvers and an extra index on your DynamoDB table. The steps to do this are as follows:
Create a GSI on the table where the label is the HASH KEY.
Create a pipeline resolver on the Mutation.createTag field in your schema. You can turn off the auto-generated Mutation.createTag mutation by changing your #model definition to #model(mutations: { update: "updateTag", delete: "deleteTag" }).
Create a function named LookupLabel that issues a Query against the new GSI where the label = $ctx.args.input.label. If this returns a value, throw an error with $util.error("Label is not unique"). If it returns no values then continue.
Create a function named CreateTag that issues a PutItem against the Tag table.
Add those two functions in order to your pipeline resolver.
You can read more about pipeline resolvers here https://docs.aws.amazon.com/appsync/latest/devguide/pipeline-resolvers.html.
As of writing amplify does not yet support custom & pipeline resolvers but you can read more about the feature here https://github.com/aws-amplify/amplify-cli/issues/574 as it will be supported in the future. For now you can add the resolver manually in the AWS AppSync console or via your own CloudFormation template that targets the id of the API created by Amplify. It would also be helpful if you create an issue here (https://github.com/aws-amplify/amplify-cli/issues) and tag this as a feature request because it would be possible to automate this with an #unique directive but this would need to be planned.
Thanks
Update: now you can use #primarykey and #index annotations:
https://docs.amplify.aws/cli/migration/transformer-migration/#what-is-changing
basic:
profile #model {
name
email #primaryKey - has to be unique
other
}
so if you needed something like:
profile #model {
name
email: String! #hasOne
other
}
email #model {
email: String! #primaryKey
}
if you are on an older version see below
I will eventually be testing this out to see if this works but you might be able to do something like rename the id to a string!
so...
type Tag #model #key["id"] {
id: String!
}
or:
type Customer #model #key(fields: ["email"]) {
email: String!
username: String
}
this second one is taken directly from the docs: https://docs.amplify.aws/cli/graphql-transformer/key#designing-data-models-using-key
The docs were updated recently so hopefully they are easier for everyone to understand.
If you need a more advanced workflow with allot of keys, and stuff like that then you just have to separate things out and make more types for example:
type Customer #model {
id: String!
email: Email! #hasOne
username: String
}
type email #model #key(fields: ["email"]) {
email: String!
}

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.

Resources