How to develop AppSync schemas using Apollo tools - graphql

I'm using AppSync with Serverless and in the early phase of a new GraphQL implementation, that will eventually become rather large.
I've started off small, following the AWS recommended and published standards for schema development. My schema is starting to get larger and larger, and I'm left wanting better tools that I find are available in Apollo (it appears that with Apollo, you can extend types, modularize, etc).
The schema development is static thus far -- you point serverless to a schema and thats it. Is anyone aware of a library that supports an Apollo like environment (extending types, modular design, etc)? Possibly something that compiles a schema, that I could point serverless to? I have looked a little bit, but haven't really found anything that would work.

I finally found library that does this for me. If you're in need, take a look at graphql-s2s
I was able to take the below schema, transpile it, save it, and point serverless to the generated schema.
Original:
type TenantScope {
tenantId: String
}
type Asset inherits TenantScope {
type: String!
}
type Vehicle inherits Asset {
vin: String!
stockNum: String!
}
Transpiled:
type TenantScope {
tenantId: String
}
type Asset {
type: String!
tenantId: String
}
type Vehicle {
vin: String!
stockNum: String!
type: String!
tenantId: String
}

Related

Best practice for mailing addresses types

I am fairly new to GraphQL and i am writing a schema which will include companies and users. Each of these entities will have a mailing address associated with them. I am also using AWS Amplify to generate the resolvers and the DB (at least to get the project going).
I don't intend to retrieve an address separate from its user/company
Want to make the schema as modular as possible without repeating information unnecessarily
Here's an abbreviated version of what I started
type Address {
id: ID!
streetAddress1: String!
streetAddress2: String
city: String!
state: String!
zipCode: String!
country: String!
coordinates: String
}
type Company
{
id: ID!
name: String!
address: Address!
...
}
My question is whether it is best to have the address as a separate type in my schema or to lump each of the company/user types with their own address?
These are my concerns though
Separating address and entity increases complexity of the business logic and/or mutations.
In essence, turning the CRUD operations transactional in nature so that both user and address are mutated/queried.
Figured I would use an Address type without #model annotation and no #connection from the types that require it.

How to export field definitions in GraphQL

How can I export field definitions in GraphQL? For example, let's say I have
schema.graphql:
type Query {
comment: String!
}
can I instead do something like this?
query.graphql:
const query = comment: String!;
export query;
schema.graphql:
import { query } from './query.graphql';
type Query {
query
}
EDIT:
The real intent behind this question is how do you organize your Querys or Mutations by schema? For example:
type Mutation {
createComment(data: CreateCommentInput): Comment!,
createPost(data: CreatePostInput): Post!,
createUser(data: CreateUserInput): User!,
deleteComment(id: ID!): Comment!,
deletePost(id: ID!): Post!,
deleteUser(id: ID!): User!,
}
Right now my application only has three objects: Comments, Posts, and Users. My preference is to organize my Inputs, Resolvers, etc by schema (e.g. Comments, Posts, and Users), but that means at the end of the day I still have to put everything into a single Mutation type. This seems quite difficult to scale for much larger applications where there could be dozens and dozens of objects and relations, mutations, and more, and it would be nice to separate out individual mutations in their respective Comments folder, Users folder, etc. Thanks!
GraphQL does not support any kind of import/export syntax.
Certain libraries like graphql-tag and babel-plugin-import-graphql support importing fragments or type definitions through comments, but these libraries do not support importing individual field definitions.
At best, you could utilize template strings, but you'd have to utilize JavaScript/TypeScript files instead (assuming you're working with a Node.js server, but that's not totally clear from your question).
fields.js
export const fields = `
comment: String!
`
query.js
import { fields } from './fields'
export const typeDefs = `
type Query {
${fields}
}
`
At the end of the day, though, it's unlikely you have enough duplication between your type definitions to warrant doing something like this. Unless you have dozens of common fields across dozens of types, I would just deal with the duplication.
If you're just looking to modularize your schema across multiple files, you can use the extend to create multiple type definitions that will be merged into a single one. For example:
type Query {
someField: String!
}
extend type Query {
someOtherField: String!
}
extend type Query {
yetAnotherField: String!
}
Not that you must still define the type you're extending -- you cannot have extensions that reference a non-existent type. When using extensions, however, your "base" type doesn't necessarily have to have fields (as long as you have at least one extension). So you can do something like this as well:
type Query # Note the lack of curly brackets
extend type Query {
someOtherField: String!
}
There are also libraries out there, like this one, that let you merge type definitions without using the extend keyword. There are some known issues with this sort of programmatic merging, but YMMV.
Lastly, although I'm personally not a fan of doing things this way, you may find this pattern helpful in organizing your schema.

Why is TYPE_ADDED_TO_INTERFACE considered a breaking change?

I am using the Apollo Server implementation of GraphQL, together with the Apollo Engine, and specifically the functionality to check whether a schema diff contains any breaking changes. I'd like to understand better why TYPE_ADDED_TO_INTERFACE is considered to be a breaking change, and if anyone can provide an example of a graphql query that would break, as a consequence?
I'm using the apollo/2.9.0 darwin-x64 node-v10.10.0 to perform the schema check with the apollo service:check command.
For example, if I have this schema:
interface Animal {
id: ID
}
type Dog implements Animal {
id: ID
favoriteToy: String
}
And then add this to the schema:
type Cat implements Animal {
id: ID
}
This is considered a breaking change. Why?
I can see that if someone is making a query for all the Animal objects, and has a ... on Dog fragment in the query, they would start getting Cat objects back with only the interface fields, until they also add a ... on Cat fragment. Is that what's considered breaking?
Having a type implement an interface it previously did not should not break existing queries. To your point, even if the inline fragment is omitted, the results will still be valid (they could result in an empty object being returned if no interface fields were selected, but that's still a valid response).
I could, however, foresee issues in specific clients resulting from this kind of change. For example, when using Apollo client, we often create an IntrospectionFragmentMatcher to specifically help the client correctly cache results from union or interface fields.
To support result validation and accurate fragment matching on unions and interfaces, a special fragment matcher called the IntrospectionFragmentMatcher can be used. If there are any changes related to union or interface types in your schema, you will have to update the fragment matcher accordingly.
In other words, having the schema change in this way could break client caching behavior. I suspect for clients that do schema-based code-generation, like apollo-android, this could also result in some runtime weirdness.

Is there a good pattern for merging the results of two graphQL schemas?

I've been looking around to see if a pattern exists for merging two graphQL datasets which contain the same type of data. What I mean is, given the following two type definitions;
type StevesBooks {
title: String
author: String
}
type DavesBooks {
title: String
author: String
}
If both of those are implemented as graphQL schemas separately/remotely, each with their own set of resolvers, is there a pattern or paradigm I can draw from to create a third graphQL instance that combines these two, so I can query "SteveAndDavesBooks" at the same time?
I found mergeSchemas in the apolloServer API but that solves a different problem to this one and all of the conflict-resolution methods in there require you to "choose a side" so to speak, rather than combine the results.
Is there already-written plugin or library somewhere that will help to achieve the above or do you think this is going to be something bespoke that I need to do myself?
I saw that query federation is on the roadmap - does anyone know any more details about that because that sounds like what I'm after.

GraphQL versioning, not nullable to nullable

At the begining we have schema
type User {
id: ID!
name: String!
birthday: String!
}
After some time it was decided that birthday is not mandatory for new users, so the schema should change to
type User {
id: ID!
name: String!
birthday: String
}
This is breaking change.
How to versioning case like this in GraphQL?
As mentionned in these official best practices under the section versioning, it isn't recommanded to do traditional API versioning in GraphQL even though there isn't any technical problems in doing so. I would feel that we are corrupting our model since it would introduce a technical field in a business API... which is definitely not ideal.
To help manage the API's evolution, they introduced the concept of field deprecation as described in this link.
To summarize in a few words, I would say that you have basically two solutions:
Do versioning anyway;
Set your field birthday as deprecated and add a new one with an equivalent name (birthday_date maybe?). Obviously, don't forget to tell your api clients to use this new field. In the long run, remove the deprecated field.

Resources