Data Modeling (AWS amplify, AppSync, GraphQL)
Tags: aws, graphql, appsync, amplify
Published: 2022-06-17 09:03:40

Before going to the data modeling we need to create the graphQL API.
Go to the link to create [the graphQL API](https://docs.amplify.aws/lib/graphqlapi/getting-started/q/platform/js/)
This may looks time consuming. But if you're new to Data Modeling give it a shot. You're basic will be far more then clear.

Before start creating schema we've to know about some directives we're going to use while creating graphQL schema. And if don't have any idea about what GraphQL is then get some basic about [GraphQl](https://graphql.org/learn/)

## Directives 
`@model @auth @hasOne @hasMany @belongTo @manyToMany @searchable @primaryKey and @index ....`

> `@model`

- Store objects in DynamoDB

- Configure CRUD resolvers

> `@auth`

- API authorization strategies

> `@hasOne, @hasMany, @manyToMany, @belongsTo`

- Create relationships between @model `object types`

> `@searchable`

- Active search and sorting at the same time from database.

> `@primaryKey, @index`

- Override default key property with custom data

## Data Modeling
Now we're going to setup database tables using API(GraphQL)

> `Data types`
1. Scalar type: title, content (Contains scalar value like ID, String, Int, Float, DateTime etc.)
2. Object type: Todo (which is a JavaScript Object)


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9tmzoexpdlqnw4ht0l2l.png)
Without `@model` directive, we'll not get any CRUD resolvers 
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ew0dld8txsaqe4n5y34j.png)

### 1. **`@model`**

By adding `@model` directive we will get all the CRUD resolvers 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9tmlys7gg5js17ehfkod.png)

| Resolvers     | Description   |
| ------------- |:-------------:|
| getTodo | Get one todo document using `id` or `primaryKey` |
| listTodo | Get all todo documents |
| createTodo | Insert one todo document|
| updateTodo | Update one todo document using `id` or `primaryKey` |
| deleteTodo | Delete one todo document using `id` or `primaryKey` |
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n20veks5gdcidwkw04wk.png)
 
### 2. **`@auth`**

By adding `@auth` directive we will get the permission to use CRUD resolvers. Otherwise we might get an error about unauthorization.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/juosq0uhraujgu9ai01y.png)
> `createTodo, updateTodo, deleteTodo` To create, update & delete a document in DB, we've to start the query with **`mutation`** 

> **`createTodo`**
- id: id is auto generated 
- title: title comes from `input` object `title` field
- content: content comes from `input` object `content` field
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1ie6ddvl1fg73buys3n4.png)
 

> `getTodo, listTodo` To get a document or all documents, the best practice is to start the query with **`query`**

> **`getTodo`**
- id: Provide id to get a document from DB.
- title, content data will come from DB 
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qik8phjy7aomunj99azy.png)

> **`listTodos`**
- id: Don't need id to get data
- title, content & other propertise will come from DB
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nfar5n0261dndm8yyzca.png)

 
>**`deleteTodo`**
- id: Provide id to delete match document
- whole document object will delete from DB
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fb0lu9ot1zb0c4x10685.png)


### 3. **`@index`**

By using secondary index or `@index` directive we can create another extra queryField

- To use **`todoByTitleAndRank`** we've to provide title as argument in the query field
- To use **`todoByRank`** we've to provide rank as argument in the query field

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9pedhvjr1mm2043ro49w.png)
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ickm6n3g8863wf5iojho.png)
 
## Setup relationships between models
| Directive  | Data flow | Relationship | Example|
| ------------- |:-------------:|-------------:|-------------:|
| `@hasOne` | One-directional | one-to-one | A project "has one" team |
| `@hasMany` | One-directional | one-to-many | A post "has many" comments |
| `@belongsTo` | Bi-directional | one-to-one or one-to-many | A project has one team or a team belongs to a project |
| `@ManyToMany` | Bi-directional | many-to-many | A blog has many tags and a tag has many blogs |

### 4. **`@hasOne`**

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gjgd3xbo7ccb68fgi23p.png)
- **One-directional :** We'll get child object inside of a parent object. Below here Child object is Team and Parent object is Project
- **One-to-one :** We'll get only one child inside of a parent object.

>**`createTeam`**
- teamName: Provide team name
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8smhidkq3evwjh88g7j5.png)

>**`createProject`**
- projectName: Provide project name
- projectTeamId: Provide the exact same id of Team. This default field will be created and matched with the team id field & get the team data. We can override this field.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/73644hbh3a1rbkhfzcqj.png)
 
>**`listProjects`**
- Team data will be found if projectTeamId === team.id.
- We'll get team data inside of project.
- But if we try to get project data inside of team it'll give us an error.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ffcg6opujfu8fljx81pw.png)

>**`@hasOne(fields: ["fieldName"])`**
- This will override the default field (projectTeamId) by fieldName (teamID). We will not find the default field again.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r7ukzfbfenu4lxg9teti.png)
- Now instead of matching projectTeamId === team.id, it is matching teamID === team.id.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6peqecm0sdr5h19tphnw.png)
 
### 5. **`@hasMany`**

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i78z45mdkltauim8r43e.png)
- **One-directional :** We'll get array of child objects inside of a parent object. Below here Child object is Comment and Parent object is Post
- **One-to-many :** We'll get one or more child inside of a parent object.

>**`createPost`**
- postName: Provide post title
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gp7gdpsy6onznowghq8g.png)

>**`createComment`**
- commentContent: Provide comment string
- postCommentsId: Provide the exact same id of Post. This default field will be created and matched with the Post id field & get the comment data. We can override this field.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0q6pt31gh2ojwu4j077j.png)
- Adding another comment
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/43o62edv3b66bwry1qdb.png)

>**`listPosts`**
- Comments will be found if postCommentsId === post.id
- We'll get array of comment  inside of post.
- But if we try to get post data inside of comments it'll give us an error.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l6zdjrwg4mhwbicsb7fl.png)

>**`@hasMany(indexName: "byPost", fields: ["id"]) 
postID: ID! @index(name: "byPost", sortKeyFields: ["content"]`**
- This will override the default field (postCommentsId) by property of index (postID). We will not find the default field again.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/px42d2985ei5m20b297y.png)
- Adding post
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7lnazwtfu9gjjo65i4qv.png)
- Adding comment 1
- postID: Provide the exact same id of Post. This field will override the postCommentsId field and match the Post id field & get the comment data.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t0m4rtsazgni7az9b1br.png)
- Adding comment 2
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wpvkirizh4polxdclt17.png)
- Now instead of matching postCommentsId === post.id, it is matching postID === post.id.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/52lrebwohagz6c3nuvli.png)

### 6. **`@belongsTo`**

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5dr3qptqynnba2imc3e8.png)

- **Bi-directional :** We'll get array of child objects inside of a parent object & we'll also get parent inside of. Below here Child object is Comment and Parent object is Post
- **Difference** 

| Directive  | Can be found child data inside parent object| Can be found parent data inside child object| 
| ------------- |:-------------:|-------------:|
| `@hasOne` | Yes | No | 
| `@hasMany` | Yes | No | 
| `@belongsTo` | Yes | Yes|

- I'm explaining `@belongsTo` directive for `@hasMany` relation. You should try out `@belongsTo` directive for `@hasOne` relation. There is no change in creating `@hasOne` and `@hasMany` schema and data table.Just we will find parent data inside of a child object.

>**`createPost`**
- Provide postName
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4qd4qp094ocraz36gm6a.png)

>**`createComment`**
- Creating relation between two table by postCommentsId
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3pw6w2lqu4hzp7h9y3mu.png)
- Add comment 2
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6xq7ez01nyd8l60o9yyj.png)

>**`listPost`**
- Getting child (comments) data inside parent (post) object
- Getting parent (post) data inside child (comments) object
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8o50oucio3xmfrgonafi.png)

>**`@hasMany(indexName: "byPost", fields: ["id"]) 
postID: ID! @index(name: "byPost")`**
- This will override the default field (postCommentsId) by property of index (postID). We will not find the default field again.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kiyenqea15hvesjznzq9.png)
>**`createPost`**
- Provide postName
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u01xmbev93m66kan8p2j.png)
 
>**`createComment`**
- postID: Provide the exact same id of Post. This field will override the postCommentsId field and match the Post id field & get the comment data.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7nqoow6kka6iz7dl9ewl.png)
- Adding comment 2
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cyhdobd2je5ijjsplkvq.png)

>**`listPost`**
- Getting child (comments) data inside parent (post) object
- Getting parent (post) data inside child (comments) object
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7sgq82svkpji5nnbck3k.png)

### 7. **`@manyToMany`**

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2cjb3thaq88gqyt9nko9.png)
- This relation will create 3 data table.

| Table | get | list | create | update | delete| subscription | Found Data Object |
| ------------- |:-------------:|-------------:|------------- |:-------------:|-------------:|-------------:| -------------:|
| `Post`| Yes | Yes | Yes | Yes | Yes | Yes | Post, Tag, PostTags |
| `Tag` | Yes | Yes | Yes | Yes | Yes | Yes | Post, Tag, PostTags |
| `PostTags` | Yes | Yes| Yes | Yes | Yes | Yes | Post, Tag, PostTags |


- **Bi-directional :** We'll get one table inside of another table.

>**`createPost`**
- postName: Provide post name to create a post.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o80c2wd27pw9hvykatji.png)

>**`create Tag`**
- label: Provide label title to create a tag label.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vfzbc1cgi76ravopk4nl.png)

>**`createPostTags`**
postID: Use to provide post object.
tagID: Use to provide tag object.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x0n0s5uuj8288mmx3eh3.png)
 
## Advanced
**`Create multiple relationships between multiple model`**


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2kfn4rrz0ss9yf2hi8si.png)
Relations between models are
- Department has one manager and many employees
- Manager has one department and many employees
- Employees has one department and one manager

>**`createDepartment`**
- Provide Department name in the departmentName input
- We don't have manager and employee id. So we'll update the department later.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fpunt3flyb1nx33zfcpk.png)

>**`createManager`**
- Provide manager name  in the managerName input
- Provide department id in the departmentID input.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v9r68udb8o34ewkurouc.png)

>**`createEmployee`**
- Provide employee name in the employeeName input
- Provide department id in the departmentID input.
- Provide manager id in the managerID input.
- And we're getting department and manager info in the Employee table
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d8zel0llxoibnxpc4m32.png)

>**`updateDepartment`**
- Provide selected department id in the id input
- Provide manager id in the managerID input
- and we're getting employee and manager info in department table.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b9y7145za5vulbgy1kn7.png)
 
>**`listManagers`**
- And we're getting department and employee info in manager table.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/strurdey8gwxg67su1nw.png)

That's it for now. Thanks for your patience. If this blog help you please give  a react.
Next [blog](https://dev.to/arifulhasantotul/filter-and-result-aws-amplify-appsync-graphql-3mhd) will be about filtering...