Apollo graphql remote schema extending is dependent on remote fields - graphql

We are able to extend our remote schema, but with one major caveat: the new field cannot be queried for on it's own; it must be included with at least one field from the remote. Is it possible to query for just the extended field?
In the example below, I have extended "name" to include the field "catsName." If I query for "first," the query works. If I query for "catsName" and "first," the query works. If I query for just "catsName," it returns an internal server error with status code 400.
Note :
- When we extend non-remote fields, we do not have this issue.
- Our remote GraphQL engine uses Absinthe (Erlang/Elixir). We use Apollo locally. Our goal is to support the legacy Absinthe GraphQL implementation.
Working Query :
query{
user{
profile{
personal{
name{ // extend type name
catsName // New field
first // Original field
}}}}}
Non-working Query :
query{
user{
profile{
personal{
name{ // extend type name
catsName // New field
}}}}}
Error :
"message": "Field \"name\" of type \"UserPersonalName\" must have a
selection of subfields. Did you mean \"name { ... }\"?"
After further research we determined that the reason for this is that user, profile, personal, and name exist on the remote server. The extension of "catsName" exists locally. If we query for just catsName, our engine has no way of knowing that the root fields exist, and therefore returns an error. If we include at least one remote field in our query, the local engine knows to fetch the remote and is able to return all of the data.

Related

Custom fields with a GraphQL query

Possibly exposing my ignorance of apollo-server but hoping someone can help: so ATM I have some schemas stitched together with #graphql-tools; all very simple, cool. I can make queries without problem.
There's a desire to add custom fields to given queries, so that we add extra data from other sources into the requested existing query template.
To explain by example: say the schema looks like this:
type User {
id
projectId
}
I'm trying to develop something so that the query getUserById($id...) can provide a template like so:
query userById($id: ID!) {
userById(id: $id) {
id
project {
id
name
# whatever other fields I want from Project type
}
}
}
And then apollo/graphql would then make a separate, asynchronous request to fetch the project for that given User.
As I understand graphql-tools, I can see resolvers allow the ability to make async requests for extra data ... but my problem is by defining project { within the query template, an error is thrown because - of course - project is not defined in the actual Schema itself.
Is there a way to filter and remove fields from a given query, somewhere in the chain of events? A custom apollo-server plugin perhaps? As I said I'm exposing my ignorance here but I've got a little lost in how apollo behaves in tandem with GraphQl.

How can I conditionally change the object type name in a graphql query in gatsby at build time?

I have static queries in my components that gets data from a db. Here is an example:
{
allDbName {
nodes {
name
}
}
}
I need a way to conditionally change the object type name (allDbName in the above example) at build time depending on the database we are using. I have tried string interpolation, graphql fragments and variables, and importing graphql queries from another file, but none of these are possible or suitable for this purpose. All the fields will be identical, the only thing that needs to change is the object type name.

Error Message: Instance Data is not valid whilst updating a GraphQL schema

I had an issue with updating a graphql schema and got this message (in my case I was updating a FaunaDB schema). I couldn't find reference to this online.
Although this is not the most straightforward or descriptive error message ever conceived it simply means that (in this case) I had created a record which would not fit my new schema. (In my case I had added a required field). Although I had deleted records in the specific collection I had not deleted those which referenced that collection.
I received this error because I had entered a query with the same name as a query automatically created by Fauna (or perhaps created by a previous schema?).
This is the code that caused the error:
type User {
uid: ID! #unique
}
type Query {
user(uid: ID!): User
}
The solution was to rename the query:
type Query {
findUser(uid: ID!): User
}
An alternative possible solution with FaunaDB is to override the schema (not just update the schema). This is applicable if the user() query is still in your schema, unwanted, as a result of prior schema updates.

How to query Apollo GraphQL server with a specific context?

I am writing an Apollo GraphQL API that returns product information from various brands. A simplified version of the schema looks like this:
type Query {
products: [Product]!
}
type Product {
name: String!
brand: String!
}
I want to be able to query products from a specific brand. Normally this would be simple to achieve by adding a brand argument to the Product object:
type Query {
products(brand: String!): [Product]!
}
However, I have multiple GraphQL clients in different apps and each is associated with a specific brand so it seems redundant to always pass the same brand argument in every query. I also have many other objects in my schema (orders, transactions, etc.) that are specific to a brand and would require a brand argument.
Furthermore, my resolvers need to query a different API depending on the brand so even objects in my schema such as User, which are conceptually unrelated to a brand, would potentially need a brand argument so that the resolver knows which API to fetch from.
Is there a way to set the brand context for each client and have this context received by the server? Or maybe there is a better way to achieve this brand separation?
I would probably make Brand be a first-class type in your GraphQL query. That doesn't save you from having to qualify many of the queries you describe by a specific brand, but it at least gives you a common place to start from. Then you'd wind up with an API somewhat like:
type Query {
brand(name: String!): Brand
allProducts: [Product!]!
}
type Brand {
name: String!
products: [Product!]!
# users: [User!]!
}
type Product {
name: String!
brand: Brand! # typical, but not important to your question
}
If the differences between kinds of brands are visible at the API layer, you also could consider using a GraphQL interface to describe the set of fields that all brands have, but actually return a more specific type from the resolver.
The way you describe your application, it could also make sense to run one copy of the service for each brand, each with a different GraphQL endpoint. That would let you straightforwardly parameterize the per-brand internal object configuration and make the "current brand" be process-global context. The big constraints here are that, at a GraphQL level, one brand's objects can never refer to another, and if you have a lot of brands, you need some good way to run a lot of servers.

In my query, could I use the result of a parameter to get more info in that query?

Forgive my terribly-worded question but here's some code to explain what I'm trying to do (slug and value are provided outside this query):
const query = `{
post(slug: "${slug}") {
content
createdAt
id <--- I want this id for my reply query
slug
}
reply(replyTo: "id") { <--- The second query in question
content
createdAt
id
slug
}
user(id: "${value}") {
username
}
}`;
I just got started with GraphQL and I'm loving the fact that I can query multiple databases in one go. It'd be great if I could also perform some "queryception" but I'm not sure if this is possible.
When thinking in terms of GraphQL, it's important to remember that each field for a given type is resolved by GraphQL simultaneously.
For example, when your post query returns a Post type, GraphQL will resolve the content and createdAt fields at the same time. Once those fields are resolved, it moved on to the next "level" of the query (for example, if content returned a type instead of a scalar, it would then try to resolve those fields.
Each of your individual queries (post, reply, and user) are actually fields of the Root Query type, and the same logic applies to them as well. That means there's no way to reference the id returned by post within reply -- both queries will be fired off at the same time.
An exception to the above exists in the form of mutations, which are actually resolved sequentially instead of simultaneously. That means, even though you still wouldn't be able to use the result of post as a variable inside your reply query, you could use context to pass the id from one to the other if both were mutations. This, however, is very hackish and requires the client to request the mutations in a specific order.
A more viable solution would be to simply handle this on the client side by breaking it up into two requests, and waiting to fire the second until the first one returns.
Lastly, you may consider reworking your schema to prevent having to have multiple queries in the first place. For example, your Post type could simply have a replies field that would resolve to all replies that correspond with the returned post's id.

Resources