Find the implementation here.
It is a best practice to start with the contract between the backend and the frontend when starting building a new app. In the GraphQL world, that means the schema.
The app will show users, posts, and comments, so let's define these types with some fields:
type User {
id: ID!
name: String!
avatar: String!
friends(nextToken: String): PaginatedUsers
posts(limit: Int, nextToken: String): PaginatedPosts
}
type Post {
id: ID!
author: User!
text: String!
date: AWSDateTime!
comments(limit: Int, nextToken: String): PaginatedComments!
}
type Comment {
id: ID!
post: Post!
author: User!
text: String!
date: AWSDateTime!
}
Returning paginated types is a best practice for APIs, as we've discussed in the Pagination chapter. The above types return Paginated*
types in place of lists, so let's define those ones next:
type PaginatedUsers {
users: [User!]!
nextToken: String
}
type PaginatedComments {
comments: [Comment!]!
nextToken: String
}
type PaginatedPosts {
posts: [Post!]!
nextToken: String
}
Since we'll be using DynamoDB as the database, the schema defines a cursor-based pagination scheme. Here, there is a nextToken
both as an input and an output for all paginated fields.
For queries, the clients will need a way to get the current user, as well as any user and post by ID:
type Query {
currentUser: User!
post(id: ID!): Post
user(id: ID!): User
}
Moving on to data changes, this example project implements only two: creating a post and adding a comment:
type Mutation {
createPost(userId: ID!, text: String!): Post!
addComment(userId: ID!, postId: ID!, text: String!): Comment!
}
A real social network would implement a lot more functionality, such as editing and deleting content, make, approve, and reject friend requests, and so on. But for our example, these will be enough.