Custom schema, interface, #fileByRelativePath and gatsby-image - graphql

I'm trying to get an interface working with the new #fileByRelativePath resolver extension, to keep compatible with v3.
I'm using Prismic for my content, and gatsby-source-prismic v2. I have two content types in Prismic, and created the interface to be able to more easily query and map over both for a home page index.
Here's the functioning (but with deprecated inferred resolvers) schema:
exports.createSchemaCustomization = ({ actions }) => {
const { createTypes } = actions
const typeDefs = `
interface indexPosts #nodeInterface {
id: ID!
uid: String!
data: Data!
type: String!
}
type Data {
title: Title!
date: Date!
featured: String!
featured_image: Featured_image!
body: Body!
}
type Title {
text: String!
}
type Featured_image {
localFile: File!
}
type Body {
html: String!
}
type PrismicGallery implements Node & indexPosts {
uid: String!
data: Data!
type: String!
}
type PrismicEssay implements Node & indexPosts {
uid: String!
data: Data!
type: String!
}
`
createTypes(typeDefs)
}
The problem comes after adding #fileByRelativePath to the Featured_image type definition. Doing so gives me an error during build:
"The "path" argument must be of type string. Received type undefined"
I'm unsure how to provide the necessary path argument, considering my images are third-party hosted. I'm trying to follow the brief guide at the end of this page and suspect the way to do it might be with a resolver or type builder and using 'source' to access the url field provided by both localFile and its parent, featured_image, but I can't figure it out!
I'm using gatsby-image and the childImageSharp convenience field to present the images, if that makes a difference at all!

I had exactly the same problem when I tried to use #fileByRelativePath. I managed to solve my problem by using #infer on the type that contained the File.
Try this:
type Featured_image #infer {
localFile: File!
}

Related

Query an interface that implements another interface in GraphQL

I'm currently working on an Activity Feed built using GraphQL Nexus and Apollo Server (3.9.0). It will receive a flurry of information that will have shared fields that are handled by a shared IActivity interface.
In addition, there will be types that will also share similar fields through other interfaces that are not included in IActivity, say for example IMedia and INews.
We are looking for a way to bring all the fields from IActivity and be able to query IMedia and INews within IActivity. We know that there is a possibility to query the concrete types, but we'd like to avoid it, as we want to extend the backend by adding new types of feeds (That share the same fields of those interfaces) without updating the client (it's a react-native application). We would use concrete types only when rendering custom UI components.
Here's an example:
interface IActivity {
id: ID!
name: String!
description: String
}
interface IMedia implements IActivity {
id: ID!
name: String!
description: String
url: String
type: String
}
interface INews implements IActivity {
id: ID!
name: String!
description: String
date: DateTime
author: String
content: String
}
type Video implements IActivity & IMedia {
id: ID!
name: String!
description: String
url: String
type: String
format: String
codec: String
}
type Image implements IActivity & IMedia {
id: ID!
name: String!
description: String
url: String
type: String
compressed: Boolean
codec: String
extension: String
}
type Audio implements IActivity & IMedia {
id: ID!
name: String!
description: String
url: String
type: String
bitrate: Boolean
}
type Post implements INews & IActivity {
id: ID!
name: String!
description: String
date: DateTime
author: String
content: String
comments: [Comment]
}
type Comment {
author: String
message: String
}
type FlashNews implements INews & IActivity {
id: ID!
name: String!
description: String
date: DateTime
author: String
content: String
rating: Int
}
type Query {
activityFeed(): [IActivity]
}
This is the query
query getFeed() {
activityFeed {
id
name
description
... on IMedia {
url
type
}
... on INews {
date
author
content
}
}
}
This queries all the fields from IActivity but none of IMedia nor INews. There are no GraphQL errors (and we lint them through graphql-codegen and the Nexus builder.)
Our belief was that having IActivity also share the same as other interfaces, we could query IActivity and then specify the other interface (e.g: IMedia) as we do with concrete types.
In hindsight, what we're trying to do is somehow a union type of interfaces (which I know it's not possible)...
Is there a workaround or a solution for what we're trying to accomplish?
Edit
We found out that this exact example IS valid, and the problem is within how GraphQL Nexus is configured.
Here's a code sandbox and its Git Repo using plain Apollo Server.
Edit 2:
Try the following query:
query getFeed {
activityFeed {
id
name
description
... on IMedia {
type
format
}
... on INews {
date
author
content
}
... on Video {
length
}
}
}
We have found the problem. It's been Apollo Client (front-end) which was not properly parsing the heuristic fragment matcher.
More information on how we solved the issue can be found here:
https://stackoverflow.com/a/64886593/1057052
We generated something like this:
schema: "./appsync/appSync.gql"
# documents: "./appsync/**/*.gql"
generates:
src/generated/graphql.schema.json:
plugins:
- 'fragment-matcher'
apolloClientVersion: 3
Then the interfaces were working properly!

How to combine multiple field in GraphQL Object

I am trying my first Graphql Schema design. Is it possible that single field inside object type refer to a complex object?
enum KeyPrefix {
WS
WK
SI
ENT
}
input generalKey {
keyPrefix:KeyPrefix!
key:Int!
}
type Item
{
pk: generalKey!
data: String!
name: String!
}
It gives me error as below.
The type of Item.pk must be Output Type but got: generalKey!
input is a keyword reserved for describing input to a GraphQL query. Your schema should look like this:
enum KeyPrefix {
(your values)
}
type PrimaryKey {
prefix: KeyPrefix!
key: Int!
}
type Item {
pk: PrimaryKey!
data: String!
name: String!
}
When you define a Query in your GraphQL schema, you will want to use an input, like so:
input PrimaryKeyInput {
prefix: KeyPrefix!
key: Int!
}
type Query {
getItemByPrimaryKey(input: PrimaryKeyInput!): Item
}
This will allow a client to ask for an Item using the same fields you have on PrimaryKey.

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

How do I retrieve all items in an ArangoDB collection using GraphQL

I am using the ArangoDB FOXX GraphQL Example to retrieve data from a couple of collections (each one named the same as the type). Everything is fine when I want to retrieve one item using the _key property, but when I try to retrieve all items in a collection (see diagnosticItems) I receive an error. My schema definition is:
export const typeDefs = [`
enum Status {
up
down
}
type DiagnosticItem {
_key: String!
title: String!
status: Status
locationKey: String
lastUpdatedDate: String
location: Location #aql(exec: "FOR location in Location filter location._key == #current.locationKey return location")
}
type Location {
_key: String!
title: String!
}
type Query {
diagnosticItem(_key: String!): DiagnosticItem
diagnosticItems: [DiagnosticItem]
location(_key: String!): Location
}
`];
The full StackBlitz is here.

Graphql with nested mutations?

I am trying to figure out how to mutate a nested object with graphql mutations, if possible. For instance I have the following schema:
type Event {
id: String
name: String
description: String
place: Place
}
type Place {
id: String
name: String
location: Location
}
type Location {
city: String
country: String
zip: String
}
type Query {
events: [Event]
}
type Mutation {
updateEvent(id: String, name: String, description: String): Event
}
schema {
query: Query
mutation: Mutation
}
How can I add the place information inside my updateEvent mutation?
Generally speaking, you should avoid thinking of the arguments to your mutations as a direct mapping to object types in your schema. Whilst it's true that they will often be similar, you're better off approaching things under the assumption that they won't be.
Using your basic types as an example. Let's say I wanted to create a new event, but rather than knowing the location, I just have the longitude/latitude - it's actually the backend that calculates the real location object from this data, and I certainly don't know its ID (it doesn't have one yet!). I'd probably construct my mutation like this:
input Point {
longitude: Float!
latitude: Float!
}
input PlaceInput {
name
coordinates: Point!
}
type mutation {
createEvent(
name: String!
description: String
placeId: ID
newPlace: PlaceInput
): Event
updateEvent(
id: ID!
name: String!
description: String
placeId: ID
newPlace: PlaceInput
): Event
)
A mutation is basically just a function call, and it's best to think of it in those terms. If you wrote a function to create an Event, you likely wouldn't provide it an event and expect it to return an event, you'd provide the information necessary to create an Event.
If you want to add a whole object to the mutation you have to define a graphql element of the type input. Here is a link to a small cheatsheet.
In your case it could look like this:
type Location {
city: String
country: String
zip: String
}
type Place {
id: String
name: String
location: Location
}
type Event {
id: String
name: String
description: String
place: Place
}
input LocationInput {
city: String
country: String
zip: String
}
input PlaceInput {
id: ID!
name: String!
location: LocationInput!
}
type Query {
events: [Event]
}
type Mutation {
updateEvent(id: String, name: String, description: String, place: PlaceInput!): Event
}
schema {
query: Query
mutation: Mutation
}

Resources