How to nest qraphql resolvers so as to group them - graphql

I'm wrapping a series of REST APIs in GraphQL and I'm having trouble getting a resolver to work.
My typeDefs
const typeDefs = `
type Page {
id: String
path: String!
title: String!
heading: String
type: String
html: String
summary: String
}
type Site {
page(path: String!): Page
}
type Query {
site: Site
}`
and my resolvers
const resolvers = {
Query: {
site: {
page: async (_root, { path }) => getPage(path)
}
}
}
If I try this query
{
site {
page(path: "/services/research-request") {
id
path
title
html
type
summary
}
}
}
I get back
{
"data": {
"site": null
}
}
However if I don't nest the page within the site
const typeDefs = `
type Page {
id: String
path: String!
title: String!
heading: String
type: String
html: String
summary: String
}
type Query {
page(path: String!): Page
}`
and use resolvers
const resolvers = {
Query: {
page: async (_root, { path }) => getPage(path)
}
}
then I try this query
{
page(path: "/services/research-request") {
id
path
title
html
type
summary
}
}
I get back, correctly,
{
"data": {
"page": {
"id": "d290f1ee-6c54-4b01-90e6-d701748f0851",
"path": "/services/research-request",
"title": "Research | Requests",
"html": "<p>This is a really well put together Research Requests page<p>\n",
"type": "page",
"summary": "A short piece of summary text"
}
}
}
I'm going to be pulling in data from a number of APIs to populate my graph so wanted to group the various parts according to site vs user, etc but I'm missing something obvious when trying to nest the resolvers. What am I doing wrong?

No 'free' nesting in graphql ... nest > next level depth > next TYPE ... site resolver, site.page resolver ... but also own site id (for client cache), rebuild/adjust all clients code etc. ...
Namespacing should be better: sitePage, siteOptions, userProfile, userSettings ... use alias (or replace in files) for renaming in client.

Related

Gatsby's mapping between markdown files

I'm creating a multi-author site (using gatsby-plugin-mdx) and have the following file structure:
/posts
- /post-1/index.mdx
- /post-2/index.mdx
- ...
/members
- /member-a/index.mdx
- /member-b/index.mdx
- ...
In the frontmatter of the post page I have an array of authors like
authors: [Member A, Member B]
and I have the name of the author in the frontmatter of the author's markdown file.
I'd like to set the schema up so that when I query the post, I also get the details of the authors as well (name, email, etc.).
From reading this page it seems like I need to create a custom resolver... but all the examples I see have all the authors in one json file (so you have two collections, MarkdownRemark and AuthorJson... while I think for my case all my posts and members are in MarkdownRemark collection.
Thanks so much!
I end up doing something like this. Surely there's a cleaner way, but it works for me. It goes through all the Mdx and add a field called authors, which is queried, to all Mdx types.
One problem with this is that there's also authors under members, which is not ideal. A better approach is to define new types and change Mdx in the last resolver to your new post data type. Not sure how to get that to work though. At the end, I could query something like:
query MyQuery {
posts {
frontmatter {
title
subtitle
}
authors {
frontmatter {
name
email
}
}
}
}
exports.createResolvers = ({ createResolvers }) => {
const resolvers = {
Mdx: {
authors: {
type: ["Mdx"],
resolve(source, args, context, info) {
return context.nodeModel.runQuery({
query: {
filter: {
fields: {
collection: { eq: "members" }
},
frontmatter: {
memberid: { in: source.frontmatter.authors },
},
},
},
type: "Mdx",
firstOnly: false,
})
}
}
},
}
createResolvers(resolvers)
}

dynamic slug not populating the url with graphql and nuxt

I am using Strapi, Graphql and Nuxt.js to get a list of projects and then display a single project based on the button that is clicked which should carry along a slug of the project name in route params.
The Graphql query looks like below and does work in playground if I pass along a slug in a variable.
query GetProject($slug: String!) {
projects(where: {slug: $slug}) {
id
name
slug
}
}
Query Variables
{
"slug": "tunnel-to-new-york"
}
the result is
{
"data": {
"projects": [
{
"id": "5ea7904136a59018ac9ffb54",
"name": "Tunnel to New York",
"slug": "tunnel-to-new-york"
}
]
}
}
In the projects page the button is
<v-btn to="/projects/`${slug}`">Model + Details</v-btn>
and in the Apollo query on the projects list page
apollo: {
projects: {
prefetch: true,
query: projectsQuery,
variables() {
return { slug: this.$route.params.slug };
}
}
},
what gets sent to the address bar is
http://localhost:3000/projects/%60$%7Bslug%7D%60
if i type in the slug like
http://localhost:3000/projects/tunnel-to-new-york
the error is missing parameter - its returning an array instead of an object
the single project query is
query GetProject($slug: String!) {
projects(where: {slug: $slug}) {
id
name
slug } }
and in the _slug.vue
apollo: {
project: {
prefetch: true,
query: projectQuery,
variables () {
return { slug: this.$route.params.slug }
}
}
}
Any insights would be appreciated!
so the issue was not binding the 'to' in the button - the colon was missing and changed {slug} to {projects.slug}
<v-btn :to="`/project/${project.slug}`">Model + Details</v-btn>
thanks to Jeffery Biles https://www.youtube.com/watch?v=NS0io3Z75GI&list=PLPwpWyfm6JACZm5kqu6p4s7XHXbAQ7fP-

Query an array with unstructured objects on GraphQL

I'm trying to use GraphQL to query an unstructured array with objects in Gridsome. It is currently looking very messy and it feels like there should be a better way to do this.
The data that gets loaded into GraphQL from the CMS looks like this:
{
title: "Homepage",
top_image: "imgurl.jpg",
page_builder: [
{
type: "slider",
field: "data example",
different_field: "data example"
},
{
type: "call_to_action",
field_for_cta: "data example",
different_cta_field: "data example"
}
]
}
As you can see the objects in page_builder will have different fields depening on how the client is building this section.
When I try to query this in GraphQL. It will become very messy:
<page-query>
query {
data: pages(path: "/pages") {
title,
top_image,
page_builder {
type,
field,
different_field,
type,
field_for_cta,
different_cta_field
#this list will have way more fields depending on all the page builder elements
}
}
}
</page-query>
Is there a way to organize this fields by type and only return the fields of this specific type?
Assuming gridsome supports fragments, you can do something like this:
<page-query>
query {
data: pages(path: "/pages") {
title,
top_image,
page_builder {
...A #include(if: $includeA)
...B #include(if: $includeB)
...C #include(if: $includeC)
}
}
}
# Note: Replace PageBuilderType with appropriate type
fragment A on PageBuilderType {
# your fields here
}
fragment B on PageBuilderType {
# your fields here
}
fragment C on PageBuilderType {
# your fields here
}
</page-query>
You can then define the variables when calling createPage as shown here:
api.createPages(({ createPage }) => {
createPage({
path: '/my-page',
component: './src/templates/MyPage.vue',
queryVariables: {
includeA: someCondition,
includeB: someCondition,
includeC: someCondition,
},
})
})
}

Graphql Object that implements an Interface is not not inheriting resolver from Interface

I am using graphql-tools to build a GraphQL Schema, esentially I have this structure
const typeDefs = `
type Query {
author(name: String): Author
}
interface Person {
id: Int
name: String
citizenship: String
type Author implements Person {
id: Int
name: String
citizenship: String
`
and I have the following resolvers
const resolvers = {
Query: {
author(_,args) {
return Author.find({where: args});
}
}
Person: {
citizenship() {
return "Example Citizenship";
}
}
}
I make the schema executable
const schema = makeExecutableSchema({
typeDefs,
resolvers,
inheritResolversFromInterfaces: true
});
and the optional argument inheritResolversFromInterfaces: true its supposed to allow me to inherit the citizenship resolver from Person to Author, based on the apollo graphql-tools documentation (link). That way when an Author is queried, the "Example Citizenship" string will appear.
However it does not, the query returns with
"author": {
"id": 1,
"name": "Peter",
"citizenship": null
}
Resolved, the feature inheritResolversFromInterfaces: true was added in version v2.24.0, need to have that version in order to use that feature.

Any reason I am getting back the query name in the GraphQL results?

Using the makeExecutableSchema with the following Query definition:
# Interface for simple presence in front-end.
type AccountType {
email: Email!
firstName: String!
lastName: String!
}
# The Root Query
type Query {
# Get's the account per ID or with an authToken.
getAccount(
email: Email
) : AccountType!
}
schema {
query: Query
}
And the following resolver:
export default {
Query: {
async getAccount(_, {email}, { authToken }) {
/**
* Authentication
*/
//const user = security.requireAuth(authToken)
/**
* Resolution
*/
const account = await accounts.find({email})
if (account.length !== 1) {
throw new GraphQLError('No account was found with the given email.', GraphQLError.codes.GRAPHQL_NOT_FOUND)
}
return account
}
}
}
When I query with:
query {
getAccount(email: "test#testing.com") {
firstName
lastName
}
}
I am getting the following result in GraphiQL:
{
"data": {
"getAccount": {
"firstName": "John",
"lastName": "Doe"
}
}
}
So, any reason I am getting this "getAccount" back in the result?
Because getAccount is not a query name. It's just a regular field on the root query type Query.
And having results on the exact same shape as the query is one of the core design principles of GraphQL:
Screenshot from http://graphql.org/ site
Query name in GraphQL goes after query keyword:
query myQueryName {
getAccount(email: "test#testing.com") {
firstName
lastName
}
}

Resources