Goal: support graphQL field resolvers and subscriptions through commands.
Sub-Goals:
Support interop with RPC and Rest
Minimize duplication
avoid leaking AWS specifics
maintain type safety
Assumptions
There is only a single graphQL api per service
Topics
schema loading/building
schema retrieval
command configuration/field binding
schema validation
error handling
authentication and middleware
errors
graphql client
subscriptions definition
subscription filters
subscription invalidation
subscriptions client
subscriptions auth
schema loading/building
GraphQL requires a GraphQL schema, much like our API gateway impl requires a OpenAPI Spec.
This schema must accurately reflect the types, functions, and subscriptions that the service provides.
Schema First A common pattern is to define the schema in a .graphql file and load it into the service. This is the main approach provided by CDK for AppSync.
However, AppSync has some behaviors that can only be configured within the schema and are AppSync specific. We'd prefer to avoid leaking AppSync specific specifications (like how we don't include API Gateway fields in the Open API Spec). For example, subscriptions must have an associated decorator mapping it to one or more mutations.
Others problems with schema first include locality and composition. Most eventual service configuration are defined local to the problem and then get composed to the final result. This means that a command/workflow/event/etc is created by itself in whatever local file system a user chooses. There is not a single point that the entire set of commands/workflows/events need to be defined by the developer. Schema first would require the developer to return to a single source to create all of the graphQL schema. A lack of composition also means that a plugin could not provide a graphQL type/resolver.
Code First One solution to needing to inject special fields would be to generate the schema from what we know instead of requiring the schema to be written by the developer. We know some information like the types and fields covered by the commands, but we do not have the complete type information.
Code First - Zod We have some type information from zod types. Currently zod is optional for APIs and RPC, but graphQL would require accurate input and output data. The larger issues are: 1) I was not able to find a nice library for zod to graphql 2) this was likely because zod does not map to graphql nicely. GraphQL requires names for everything, which is not provided by zod and graphql has special types like ID that zod cannot express.
Proposal
I think the final approach here should be a hybrid one.
Accept an optional graphQL schema
support mutating the schema via intrinsic functions - solves for the locality and composition issue
Use the graphql lexer/parser to update the given schema as needed.
Future: Write a library or find one that can turn zod into graphql types
// optionally load an existing schema, else start from empty
// can only be called once
graphQL.fromFile(path);
// optional code first operations
// append types
graphQL.type`
type Query {
getItems: String
}
`
// optionally set the schema object, otherwise the default
// can only be called once
graphQL.schema({
query: "Query",
mutation: "Mutation"
})
// add a field to an existing type (or create it)
graphQL.appendType("Query")`
getItems(filter?: String): String
`
schema retrieval
Developers have access to the OpenAPI spec via an intrinsic function
ApiSpecification.generate(); // generate allows the spec to be filtered
The same should be true for graphQL
GraphQLSchema.generate();
command configuration/field binding
Commands can support graphQL field/function binding within the same interface. Just need to provide the type and field to resolve.
At runtime, this will create an app sync data source and resolver (JS resolver?) that invokes the command lambda data source. We'll need to support AppSync as a possible input to the lambda OR create another lambda for the command for app async. We'll need to map headers and other values to the command.
Goal: support graphQL field resolvers and subscriptions through commands.
Sub-Goals:
Assumptions
Topics
schema loading/building
GraphQL requires a GraphQL schema, much like our API gateway impl requires a OpenAPI Spec.
This schema must accurately reflect the types, functions, and subscriptions that the service provides.
Schema First A common pattern is to define the schema in a
.graphql
file and load it into the service. This is the main approach provided by CDK for AppSync.However, AppSync has some behaviors that can only be configured within the schema and are AppSync specific. We'd prefer to avoid leaking AppSync specific specifications (like how we don't include API Gateway fields in the Open API Spec). For example, subscriptions must have an associated decorator mapping it to one or more mutations.
Others problems with schema first include locality and composition. Most eventual service configuration are defined local to the problem and then get composed to the final result. This means that a command/workflow/event/etc is created by itself in whatever local file system a user chooses. There is not a single point that the entire set of commands/workflows/events need to be defined by the developer. Schema first would require the developer to return to a single source to create all of the graphQL schema. A lack of composition also means that a plugin could not provide a graphQL type/resolver.
Code First One solution to needing to inject special fields would be to generate the schema from what we know instead of requiring the schema to be written by the developer. We know some information like the types and fields covered by the commands, but we do not have the complete type information.
Code First - Zod We have some type information from zod types. Currently zod is optional for APIs and RPC, but graphQL would require accurate input and output data. The larger issues are: 1) I was not able to find a nice library for zod to graphql 2) this was likely because zod does not map to graphql nicely. GraphQL requires names for everything, which is not provided by zod and graphql has special types like
ID
that zod cannot express.Proposal
I think the final approach here should be a hybrid one.
schema retrieval
Developers have access to the OpenAPI spec via an intrinsic function
The same should be true for graphQL
command configuration/field binding
Commands can support graphQL field/function binding within the same interface. Just need to provide the type and field to resolve.
At runtime, this will create an app sync data source and resolver (JS resolver?) that invokes the command lambda data source. We'll need to support AppSync as a possible input to the lambda OR create another lambda for the command for app async. We'll need to map headers and other values to the command.
schema validation
error handling
authentication and middleware
errors
graphql client
subscriptions definition
subscription filters
subscription invalidation
subscriptions client
subscriptions auth