question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Splicing Keystone field resolvers into custom queries/mutations/types

See original GitHub issue

We can now define custom queries and mutations on a list and use custom GraphQL types for the output (#1503). This lets us do things like this…

const STAFF_SALES_QUERY = `
   select store, employee, sum(sale_value) as "salesLastMonth"
   from sales
   where store = :store
      and sale_date < date_trunc('month', now())
      and sale_date >= date_trunc('month', now() - interval '1 month')
   group by store, employee;
`;

keystone.createList('Store', {
   // ...
   queries: [
      {
         types: [
            `type StaffSalesReportItem {
               store: ID
               employee: ID
               salesLastMonth: Number
            }`,
         ],
         schema: 'StaffSalesReport(store: ID!): StaffSalesReportItem',
         resolver: async (obj, { store }) => {
            const { rows } = await keystone.adapters.KnexAdapter.knex.raw(STAFF_SALES_QUERY, { store });
            return rows;
         },
      },
   ],
});

Super handy but, as above, the output types often relate to existing Keystone list. Since the whole thing is resolved with custom code there’s no way to drill into these fields and though they were relationship fields.

What we really want is for the type above to be closer to…

type StaffSalesReportItem {
   store: Store
   employee: Employee
   salesLastMonth: Number
}

So we drill into the relationship and do queries like…

query {
   StaffSalesReport ( store: "A8622468-9B70-41E1-97E3-620CF708CB0F" ) {
      store { id name }
      employee { id name email role { paygrade } startDate }
      salesLastMonth
   }
}

Note, we’re not just resolving the related item; we might be drilling into their relationships too. We want to hand off processing that entire part branch of the query. Regardless, I think this should be possible using they existing Keystone config.

Completely made up syntax and probably wrong but imagine something like this:

keystone.createList('Store', {
   // ...
   queries: [
      {
         types: [
            // Note, now referencing existing List output types..
            `type StaffSalesReportItem {
               store: Store
               employee: Employee
               salesLastMonth: Number
            }`,
         ],
         schema: 'StaffSalesReport(store: ID!): StaffSalesReportItem',
         resolver: async (obj, { store }, auth, info) => {
            const { rows: items } = await keystone.adapters.KnexAdapter.knex.raw(STAFF_SALES_QUERY, { store });
            await keystone.lists.stores.resolveItemsForGql({ items, path: '.store', auth, info });
            await keystone.lists.employees.resolveItemsForGql({ items, path: '.employee', auth, info });
            return items;
         },
      },
   ],
});

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:2
  • Comments:7 (4 by maintainers)

github_iconTop GitHub Comments

2reactions
emmatowncommented, Aug 20, 2020

Using a hypothetical GraphQL schema extension API but using the real current Keystone API for getting the items, this is how you should solve this:

extendGraphQLSchema({
  typeDefs: gql`
    type StaffSalesReportItem {
      store: Store
      employee: Employee
      salesLastMonth: Float
    }
    extend type Query {
      StaffSalesReport(store: ID!): StaffSalesReportItem
    }
  `,
  resolvers: {
    StaffSalesReport: {
      store(rootVal, args, ctx) {
        return keystone.lists.Store.itemQuery({ where: { id: rootVal.store } }, ctx);
      },
      employee(rootVal, args, ctx) {
        return keystone.lists.Employee.itemQuery({ where: { id: rootVal.employee } }, ctx);
      },
    },
    Query: {
      async StaffSalesReport(_, { store }) {
        const { rows } = await keystone.adapters.KnexAdapter.knex.raw(STAFF_SALES_QUERY, { store });
        return rows;
      },
    },
  },
});

So what we need to do here is:

  • Have a GraphQL schema extension API that actually lets you write resolvers for fields on types and etc.
  • Use dataloader or something like it in itemQuery
  • Document how to do this
2reactions
molombycommented, Aug 23, 2019

Hey @MadeByMike the example uses Knex but the tasks isn’t actually coupled to it so I’ve removed the tag. This is much more about GraphQL customisation.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Resolvers - Apollo GraphQL Docs
A resolver is a function that's responsible for populating the data for a single field in your schema. It can populate that data...
Read more >
Resolvers – GraphQL Tools
Resolvers are per field functions that are given a parent object, arguments, and the execution context, and are responsible for returning a ...
Read more >
Resolvers - TypeGraphQL
Field resolvers in TypeGraphQL are very similar to queries and mutations - we create them as a method on the resolver class but...
Read more >
GraphQL with Keystone wants field type must be Output Type ...
The key problem with field Photo which implemented as File type in keystone.js. Below my GraphQL Schema: import { GraphQLSchema, GraphQLID, ...
Read more >
Execution - GraphQL
Root fields & resolvers# · obj The previous object, which for a field on the root Query type is often not used. ·...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found