Change the exposed graphql schema through directives - graphql

Directives are nice to alter the behaviour of resolvers, but is there a way to directly change the exposed schema with them?
Example
expected superuser schema
type Query {
getBooks: [Book]
getAuthors: [Author]
}
expected normal user schema
type Query {
getBooks: [Book]
}
one definition to build them all
type Query {
getBooks: [Book] #allow(scopes: ["superuser"])
getAuthors: [Author]
}
The scope would be defined through the given context as i would build one schema for each possible scope.

Related

GraphQL | How to implement conditional nesting?

Please consider the following GraphQL schema:
type User {
id: ID!
events: [Event]
}
type Event {
id: ID!
user: User!
asset: Asset!
}
type Asset {
id: ID
price: Number!
name: String!
}
GraphQL is a fantastic framework for fetching nested objects, but I'm struggling to understand how conditional nesting is implemented.
Example:
I want to retrieve all events for a specific user where asset.price is greater than x.
Or
I want to retrieve all events for an asset that belongs to a list of users [].
Question: Is conditional nesting a concept in GraphQL and how is it implemented?
Side note: I use AWS AppSync and resolvers are fetching data from AWS DynamoDB.
You can define a filter/condition on any GraphQL query such as:
query {
users(permission: 'ADMIN') {
...
}
}
The permission param is passed to your resolver (say DynamoDb VTL template, Lambda etc) to be handled however you want - to GQL this is just another parameter.
You can carry this concept into nested field by creating an events resolver and you'd then call it like this:
query {
user(id: '123') {
name
events(minPrice: 200) {
nodes: {
id
eventName
eventDate
}
}
dob
...
}
}
In above case I am using a simple minPrice param but you could do more complex things such price ranges, even pass operators (eq, gt, ...). It's all irrelevant to GraphQL - all gets passed to the resolver.
How you implement that on backend depends on your setup. I use AppSync without Amplify and write my own VTL templates and build the DynamoDb request using the provided GQL fields.
Here is an SO post that shows how to create a date filter.

Strapi: Is there a to override plugin GraphQL schema?

I am trying to modify the GraphQL schema of the users-permissions plugin.
I want to change:
type UsersPermissionsLoginPayload {
jwt: String!
user: UsersPermissionsMe!
}
to:
type UsersPermissionsLoginPayload {
isAuthenticated: Boolean!
user: UsersPermissionsMe!
}
But I get many errors when I create ./extensions/users-permissions/config/schema.graphql.js:
...
There can be only one type named "UsersPermissionsLoginInput".
Field "UsersPermissionsLoginInput.identifier" can only be defined once.
Field "UsersPermissionsLoginInput.password" can only be defined once.
Field "UsersPermissionsLoginInput.provider" can only be defined once.
There can be only one type named "UsersPermissionsLoginPayload".
Field "UsersPermissionsLoginPayload.jwt" can only be defined once.
Field "UsersPermissionsLoginPayload.user" can only be defined once.
...
What is the correct way to customize/extend the schema?
Edit:
I see the types can simply be extended like so:
module.exports = {
definition: `
extend type UsersPermissionsLoginPayload {
isAuthenticated: Boolean!
}
`
}
that adds the isAuthenticated field to the UsersPermissionsLoginPayload type but does not remove the jwt field.
Is there no way to override the typedefs and resolvers? Do I need to implement my custom functionality using new types and new unique resolvers?

GraphQL - Unknown directive "unique"

I just updated GraphQL from version 0.13.2 to 14.0.2. When starting the server, I get the error message: Error: Unknown directive "unique". This is my schema:
const { gql } = require('apollo-server')
// type Query is the root query
exports.typeDefs = gql`
type User {
username: String! #unique
password: String!
}
type Query {
getAllUsers: User
}
Note even though I'm using gql from apollo-server it's using GraphQL under the hood.
As you can see what is causing is the issue is that I've made it so the username has to be unique. The updated version of GraphQL must not have this directive anymore. Sure enough, removing #unique solves the issue. I still want username to be unique. I've read that you can create custom directives. How do I go about doing this?
I've encountered a similar scenario to yours when working in fully custom directives with the upgrade of graphql-tools to v14 the definition of the directive is needed within the schema. You can specify by field, object, mutation where your directive will work.
directive #requireAuth on FIELD_DEFINITION
To work in something like this, at field level
extend type Query {
me: String! #requireAuth
user(userId: ID!):User!
users: [User]!
}
And my class that extends SchemaDirectiveVisitor it's something like this
import { SchemaDirectiveVisitor } from "apollo-server";
export class YourCustomDirective extends SchemaDirectiveVisitor {
// Your code for the directive
}
In the link provided, there is the available methdos to use in order to have your custom logic at field, object, scalar, etc level. Hope this helps.
Schema Directive Visitor

Validation error of type UndefinedFragment: Undefined fragment

I've a graphql-jave v8.0 app running on Spring boot v1.5.10, and I'm trying to utilize 'fragment' feature of GraphQL to fetch limited number of fields with the following schema type definition:
type School {
id: ID
name: String
address: String
age: String
jobTitle: String
...
}
fragment UserFields on School {
age
jobTitle
}
type Query {
user (id: String!): School!
}
schema {
query: Query
}
When I execute this query:
{
user (id: "123")
{
... UserFields
}
}
The expected result should be:
{
"user": {
"age": "12",
"jobTitle": "student"
}
}
However, It results in the following error
"message": "Validation error of type UndefinedFragment: Undefined fragment
UserFields # 'user'",
Off course I can do this with explicitly passing the field name in the query but for the sake of example, I'm interested in utilizing fragment feature of GraphQL.
Any idea what I'm doing wrong please?
Fragments aren't defined in the schema, they're something for letting you create abstractions whilst building complex queries -- the main purpose is to allow you to avoid repetition when querying the same type in multiple parts of your query.
As Andrew said, and as the official docs exemplify, fragments are not defined in the schema but (ad hoc) in the query:
{
user (id: "123") {
... UserFields
}
}
fragment UserFields on School {
age
jobTitle
}
Unconditional fragments (like the one here) are used to avoid repetition. Imagine having multiple places where you want to select age and jobTitle in the same operation.
Conditional fragments, on the other hand, are used to make a conditional selection depending on the concrete interface implementation or union subtype.

can some one explain this code to me

Good day im newbie here and im tackling graphql and im having some problem on mutation can someone explain this block of code for me thank you
RootMutation: {
createAuthor: (root, args) => { return Author.create(args); },
createPost: (root, { authorId, tags, title, text }) => {
return Author.findOne({ where: { id: authorId } }).then( (author) => {
console.log('found', author);
return author.createPost( { tags: tags.join(','), title, text });
});
},
},
Sure, this is an example of two mutations in a GraphQL server. We can break it down to understand what is going on.
First let's look at the type system. A GraphQL schema normally has two root fields query and mutation (and sometimes subscription). These root fields are the root of your data hierarchy and expose the queries (GET requests) and mutations (POST, PUT, DELETE, etc requests) that you have access to.
By the looks of it you are implementing a schema with a root mutation type that looks like this:
type Mutation {
createAuthor: Author
createPost: Post
}
A type in GraphQL is made up of a set of fields each of which can have an associated resolver. Resolvers in GraphQL are like the event handlers you would attach to endpoints in REST.
The code that you have above is defining two resolvers that will handle the logic associated with the createAuthor and createPost mutations. I.E. the code in the createPost resolver is what will be run when I issue a query like this:
mutation CreatePost($post: CreatePostInput!) {
createPost(input: $post) {
id
title
tags
text
}
}
The GraphQL runtime parses the query and routes the operation to the correct resolver depending on the content of the query. In this example, it would see that I am calling the createPost mutation and would make sure to call the createPost resolver which in your case looks like this:
createPost: (root, { authorId, tags, title, text }) => {
return Author.findOne({ where: { id: authorId } }).then( (author) => {
console.log('found', author);
return author.createPost( { tags: tags.join(','), title, text });
});
},
To understand how a resolver works, let's look at the GraphQLFieldResovler type definition from graphql-js
export type GraphQLFieldResolver<TSource, TContext> = (
source: TSource,
args: { [argName: string]: any },
context: TContext,
info: GraphQLResolveInfo
) => mixed;
As you can see a GraphQLFieldResolver is a function that takes 4 arguments.
source: The source is the parent object of the current field. For example if you were defining a resolver for a field fullName on the User type, the source would be the full user object.
args: The args are any input arguments for that resolver. In my query above it would contain the value of the $post variable.
context: Context is a global context for a GraphQL execution. This is useful for passing information around that a resolver might need. For example, you include a database connection that you can use from your resolvers without importing it in every file.
info: The info object contains information about your GraphQL schema, the query, and other information such as the path to the current resolver being executed. This is useful in many ways. Here is one post talking about how you can use it to precompute queries: (https://scaphold.io/community/blog/querying-relational-data-with-graphql/)
This idea of having types and field resolvers is part of what makes GraphQL so powerful. Once you've defined you type system and the resolvers for their fields you can structure your schema however you want and GraphQL will always make sure to call the correct resolver no matter how deeply nested a query might be.
I hope this helps :)

Resources