Advanced configuration

Here is a sum-up of everything you can configure in a model declaration:

graphqlSchemaDeclaration.user = {
  model: models.user,
  // You can specify any of those, will default to this list if not set.
  actions: ['list', 'create', 'delete', 'update', 'count'],
  // You can specify any of those, no subscriptions are created by default.
  subscriptions: ['create', 'update', 'delete'],
  // Exclude from root allows you to only define a model as a relation
  // to other models. It will not be listed in the root of the GraphQL
  // tree but will be available as child of other nodes.
  // Can be useful when you don't want to set the default rights for
  // a model.
  excludeFromRoot: false,
  // You can exclude some fields from the models. They will be be listed nor
  // available in a response. Useful for passwords and secrets in general.
  excludeFields: ['surveyAnswerRequest']
  // "before" is a list of functions to be called before
  // each resolvers of the model.
  before: [
    async (args, context, info) => {
      // Global before hook only have args, context and info.
      // You can use many functions or just one.

      // Use it if you need to do something before each enpoint
      if (!context.bootDate) {
        throw new Error('Boot date is missing!')
      }

      if (info.xxx) {
        throw new Error('Xxx is provided when it should not!')
      }

      // Typical usage:
      // * Protect an endpoint
      // * Verify entity existance

      // ex:
      // if (!context.user.role !== 'admin') {
      //   throw new Error('You must be admin to use this endpoint!')
      // }

      // The function returns nothing
    }
  ],
  list: {
    // By default only attributes required in the GraphQL query
    // are fetched. You can disable this if you need to
    // post-process many properties not in the base query.
    // When false, all attributes will be fetched.
    removeUnusedAttributes: false,
    // Additional arguments can be added to the default ones.
    extraArg: { anotherArg: { type: GraphQLInt } },
    // A hook that will be called before the fetch in the database.
    // The returned object, "findOptions", will be directly given to
    // the find[One/All] after.
    before: async (findOptions, args, context, info) => {
      if (typeof findOptions.where === 'undefined') {
        findOptions.where = {}
      }
      findOptions.where = {
        [Op.and]: [findOptions.where, { departmentId: [1] }]
      }
      return findOptions
    },
    // A hook called after the data is fetched.
    // Can be used for post-processing or triggering log.
    after: async (result, args, context, info) => {
      if (result && typeof result.length !== 'undefined') {
        for (const user of result) {
          if (user.name === 'Test 5 c 2') {
            user.name = `Mr ${user.name}`
          }
        }
      }

      return result
    },
    // If you want to overwrite the list resolver totally, you can.
    // When doing so the after and before will not be called.
    resolver: async (source, args, context) => {
      // custom code
      return [{id: 1, ...}]
    }
  },
  // The followings hooks are just here to demo their signatures.
  // They are not required and can be omited if you don't need them.
  create: {
    extraArg: { anotherArg: { type: GraphQLInt } },
    before: async (source, args, context, info) => {
      // You can restrict the creation if needed
      return args.user
    },
    after: async (newEntity, source, args, context, info) => {
      // You can log what happened here
      return newEntity
    },// 
    subscriptionFilter: (payload, args, context) => {
      // Exemple of subscription check
      if (context.user.role !== 'admin') {
        return false
      }
      return true
    }
  },
  update: {
    extraArg: { anotherArg: { type: GraphQLInt } },
    before: async (source, args, context, info) => {
      // You can restrict the creation if needed
      return args.user
    },
    after: async (
      updatedEntity,
      entitySnapshot,
      source,
      args,
      context,
      info
    ) => {
      // You can log what happened here
      return updatedEntity
    },
    // 
    subscriptionFilter: (payload, args, context) => {
      // Exemple of subscription check
      if (context.user.role !== 'admin') {
        return false
      }
      return true
    }
  },
  delete: {
    extraArg: { anotherArg: { type: GraphQLInt } },
    before: async (where, source, args, context, info) => {
      // You can restrict the creation if needed
      return where
    },
    after: async (deletedEntity, source, args, context, info) => {
      // You can log what happened here
      return deletedEntity
    },
    // 
    subscriptionFilter: (payload, args, context) => {
      // Exemple of subscription check
      if (context.user.role !== 'admin') {
        return false
      }
      return true
    }
  },
  // When you want to add additional mutations,
  // you can add them here if they are related to the model.
  // Or in the server customMutations if they are not related.
  // This hook exists to easily understand what mutations impact the model.
  additionalMutations: {
     toggleMyProperty: {
        type: myUserOutputType,
        description:
          'Enable or disable a group for the current user.',
        args: {
          groupId: { type: GraphQLInt }
        },
        resolve: async (source, { groupId }, context, info) => {
          // your mutation code...
          return { user: updateUser }
        }
      }
    }
  },
  // You can add custom subscription if needed
  additionalSubscriptions: {
    fileUpdated: {
      type: modelTypes.outputTypes.file,
      args: {
        authorId: { type: GraphQLInt }
      },
      subscribe: withFilter(
        () => pubSub.asyncIterator('fileUpdated'),
        (payload, variables, context) => {
          if (!context.user || !payload) {
            return false
          }
          if (payload.fileUpdated.authorId !== variables.authorId) {
            return false
          }
          return true
        }
      )
    }
  }
}

You can also declare a fully custom endpoint who will not depend on the model. It will simply be added to the schema as a native GraphQL endpoint but no hooks will be available.

graphqlSchemaDeclaration.myCustomCarEndpoint = () => {
  const car = new GraphQLObjectType({
    name: 'questionsScore',
    description: 'A score per question',
    fields: {
      name: { type: GraphQLString },
      type: { type: GraphQLString },
      constructor: { type: GraphQLString }
    }
  })

  return {
    type: new GraphQLList(car),
    args: {
      constructor: { type: GraphQLString }
    },
    resolve: async (source, args, context) => {
      // Fetch the cars...
      return result
    }
  }
}

Last updated

Was this helpful?