-
Notifications
You must be signed in to change notification settings - Fork 4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(appsync): code-first schema allows for object type definition #9417
Changes from 41 commits
46df9f0
84a7b4c
f3350e3
ede2020
d9271ec
7904e62
4591a70
da20dcd
6ede81e
4d48a2d
63e35b3
1b82340
fad768a
5bec482
96021db
0184c25
2a7c641
076ebdf
a351a04
8637009
0ecd95f
7005315
28f7e93
fc98566
c27a158
72d8be4
bff3e3b
47d9c89
1e2dd60
d8ee61c
1d1aec9
81563c0
a1e34a3
e7b57a4
7c8115f
04705e4
bd6868c
050b160
08aa720
d5bd67c
315ffbf
de4c2ad
2dc962b
912a674
4880a53
5337c5a
1da6d11
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -60,7 +60,7 @@ const api = new appsync.GraphQLApi(stack, 'Api', { | |||||
const demoTable = new db.Table(stack, 'DemoTable', { | ||||||
partitionKey: { | ||||||
name: 'id', | ||||||
type: AttributeType.STRING, | ||||||
type: db.AttributeType.STRING, | ||||||
}, | ||||||
}); | ||||||
|
||||||
|
@@ -161,3 +161,187 @@ api.grantMutation(role, 'updateExample'); | |||||
// For custom types and granular design | ||||||
api.grant(role, appsync.IamResource.ofType('Mutation', 'updateExample'), 'appsync:GraphQL'); | ||||||
``` | ||||||
|
||||||
### Code-First Schema | ||||||
|
||||||
CDK offers the ability to generate your schema in a code-first approach. | ||||||
A code-first approach offers a developer workflow with: | ||||||
- **modularity**: organizing schema type definitions into different files | ||||||
- **reusability**: simplifying down boilerplate/repetitive code | ||||||
- **consistency**: resolvers and schema definition will always be synced | ||||||
|
||||||
The code-first approach allows for dynamic schema generation. You can generate your schema based on variables and templates to reduce code duplication. | ||||||
|
||||||
#### Code-First Example | ||||||
|
||||||
We are going to reference the [example](#Example) through a code-first approach. | ||||||
|
||||||
```ts | ||||||
import * as appsync from '@aws-cdk/aws-appsync'; | ||||||
import * as db from '@aws-cdk/aws-dynamodb'; | ||||||
|
||||||
const api = new appsync.GraphQLApi(stack, 'Api', { | ||||||
name: 'demo', | ||||||
schemaDefinition: appsync.SchemaDefinition.FILE, | ||||||
schemaDefinitionFile: join(__dirname, 'schema.graphql'), | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. might want to provide an example that includes that this file contains |
||||||
authorizationConfig: { | ||||||
defaultAuthorization: { | ||||||
authorizationType: appsync.AuthorizationType.IAM | ||||||
}, | ||||||
}, | ||||||
}); | ||||||
|
||||||
const demoTable = new db.Table(stack, 'DemoTable', { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
partitionKey: { | ||||||
name: 'id', | ||||||
type: db.AttributeType.STRING, | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
}, | ||||||
}); | ||||||
|
||||||
const demoDS = api.addDynamoDbDataSource('demoDataSource', 'Table for Demos', demoTable); | ||||||
|
||||||
// Schema Definition starts here | ||||||
|
||||||
const demo = api.addType('demo', { | ||||||
definition: { | ||||||
id: appsync.GraphqlType.string({ isRequired: true }), | ||||||
version: appsync.GraphqlType.string({ isRequired: true }), | ||||||
}, | ||||||
}); | ||||||
|
||||||
``` | ||||||
|
||||||
#### GraphQL Types | ||||||
|
||||||
One of the benefits of GraphQL is its strongly typed nature. We define the | ||||||
types within an object, query, mutation, interface, etc. as **GraphQL Types**. | ||||||
|
||||||
GraphQL Types are the building blocks of types, whether they are scalar, objects, | ||||||
interfaces, etc. GraphQL Types can be: | ||||||
- [**Scalar Types**](https://docs.aws.amazon.com/appsync/latest/devguide/scalars.html): Id, Int, String, AWSDate, etc. | ||||||
- **Object Types**: types that you generate (i.e. `demo` from the example above) | ||||||
- **Interface Types**: abstract types that define the base implementation of other | ||||||
Intermediate Types | ||||||
|
||||||
More concretely, GraphQL Types are simply the types appended to variables. | ||||||
Referencing the object type `Demo` in the previous example, the GraphQL Types | ||||||
is `String!` and is applied to both the names `id` and `version`. | ||||||
|
||||||
#### Intermediate Types | ||||||
|
||||||
Intermediate Types are abstractions above Scalar Types. They have a set of defined | ||||||
fields, where each field corresponds to another type in the system. Intermediate | ||||||
Types will be the meat of your GraphQL Schema as they are the types defined by you. | ||||||
|
||||||
Intermediate Types include: | ||||||
- [**Interface Types**](#Interface-Types) | ||||||
- [**Object Types**](#Object-Types) | ||||||
|
||||||
#### Interface Types | ||||||
|
||||||
**Interface Types** are abstract types that define the implementation of other | ||||||
intermediate types. They are useful for eliminating duplication and can be used | ||||||
to generate Object Types with less work. | ||||||
|
||||||
You can create Interface Types in two ways: | ||||||
|
||||||
1. Interface Types can be created ***externally***. | ||||||
```ts | ||||||
const node = new appsync.InterfaceType('Node', { | ||||||
definition: { | ||||||
id: appsync.GraphqlType.string({ isRequired: true }), | ||||||
}, | ||||||
}); | ||||||
``` | ||||||
2. Interface Types can be created ***externally*** from another Interface Type. | ||||||
```ts | ||||||
const node = new appsync.InterfaceType('Node', { | ||||||
definition: { | ||||||
id: appsync.GraphqlType.string({ isRequired: true }), | ||||||
}, | ||||||
}); | ||||||
const superNode = new appsync.InterfaceType.extendInterface('SuperNode', node, { | ||||||
definition: { | ||||||
speicalId: appsync.GraphqlType.string(), | ||||||
}, | ||||||
}); | ||||||
``` | ||||||
|
||||||
#### Object Types | ||||||
|
||||||
**Object Types** are types that you declare. For example, in the [code-first example](#code-first-example) | ||||||
the `demo` variable is an **Object Type**. **Object Types** are defined by | ||||||
GraphQL Types and are only usable when linked to a GraphQL Api. | ||||||
|
||||||
You can create Object Types in three ways: | ||||||
|
||||||
1. Object Types can be created ***externally***. | ||||||
```ts | ||||||
const api = new appsync.GraphQLApi(stack, 'Api', { | ||||||
name: 'demo', | ||||||
schemaDefinition: appsync.SchemaDefinition.CODE, | ||||||
}); | ||||||
const demo = new appsync.ObjectType('Demo', { | ||||||
defintion: { | ||||||
id: appsync.GraphqlType.string({ isRequired: true }), | ||||||
version: appsync.GraphqlType.string({ isRequired: true }), | ||||||
}, | ||||||
}); | ||||||
|
||||||
api.appendToSchema(object.toString()); | ||||||
``` | ||||||
> This method allows for reusability and modularity, ideal for larger projects. | ||||||
For example, imagine moving all Object Type definition outside the stack. | ||||||
|
||||||
`scalar-types.ts` - a file for scalar type definitions | ||||||
```ts | ||||||
export const required_string = new appsync.GraphqlType.string({ isRequired: true }); | ||||||
``` | ||||||
|
||||||
`object-types.ts` - a file for object type definitions | ||||||
```ts | ||||||
import { required_string } from './scalar-types'; | ||||||
export const demo = new appsync.ObjectType('Demo', { | ||||||
defintion: { | ||||||
id: required_string, | ||||||
version: required_string, | ||||||
}, | ||||||
}); | ||||||
``` | ||||||
|
||||||
`cdk-stack.ts` - a file containing our cdk stack | ||||||
```ts | ||||||
import { demo } from './object-types'; | ||||||
api.appendToSchema(demo.toString()); | ||||||
``` | ||||||
|
||||||
2. Object Types can be created ***externally*** from an Interface Type. | ||||||
```ts | ||||||
const node = new appsync.InterfaceType('Node', { | ||||||
definition: { | ||||||
id: appsync.GraphqlType.string({ isRequired: true }), | ||||||
}, | ||||||
}); | ||||||
const demo = new appsync.ObjectType.implementInterface('Demo', node, { | ||||||
defintion: { | ||||||
version: appsync.GraphqlType.string({ isRequired: true }), | ||||||
}, | ||||||
}); | ||||||
``` | ||||||
> This method allows for reusability and modularity, ideal for reducing code duplication. | ||||||
|
||||||
3. Object Types can be created ***internally*** within the GraphQL API. | ||||||
```ts | ||||||
const api = new appsync.GraphQLApi(stack, 'Api', { | ||||||
name: 'demo', | ||||||
schemaDefinition: appsync.SchemaDefinition.CODE, | ||||||
}); | ||||||
api.addType('Demo', { | ||||||
defintion: { | ||||||
id: appsync.GraphqlType.string({ isRequired: true }), | ||||||
version: appsync.GraphqlType.string({ isRequired: true }), | ||||||
}, | ||||||
}); | ||||||
``` | ||||||
> This method provides easy use and is ideal for smaller projects. | ||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.