Development with GraphQL and Apollo.
GraphQL in conjunction with Apollo server gives developers an easy tool for querying and testing APIs. In this article, we will specifically be covering how to use these technologies in conjunction with TypeScript, TypeORM, and PostgreSQL to set up an API to returning user data from a database. Apollo server’s sandbox feature allows the developer to stage queries, mutations, and fragments with pre-determined data in order to replicate complex series of actions. Other tools, such as Postman, can also be useful for this but lack the ease of use Apollo offers in conjunction with graphQL.
Below is an example of a user entity in our database. This user contains many fields we would expect of a typical user entity including user_id, user_name, and user_email. The access_token and refresh_token are used in API authentication and will not be covered in this article. The last two fields represent arrays containing a user’s friends and songs “tracks” recommended by that user’s friends. These are the main two fields we will be testing by adding and modifying entities.
In addition to the user entity, I have provided the friend entity. Similar to the user entity each friend contains standard information regarding the entity. It is important to note that we have separated the entity id “id” from the user_id as the primary column. This will allow multiple of the friend entities to exist for a given user_id but only one user entity for a given user_id. This will be important later as we want a user to have multiple friends. The “ManyToOne” field for this entity represents the parent object for which the entity belongs, useful if this list should become separated from the parent object. The last field (friend_status) will be important to us as it represents if the friendship is pending or authenticated. A boolean value of false means that the friend receiving the request has not yet accepted it and a status of true means that it has and the opposing user has had a friend entity created in relation to their user entity with a status of true.
Now that we have our database entities and assuming we have already set up our Apollo server (not covered here) we are ready to begin building our inputs and resolvers. Inputs represent values the resolvers will expect to perform their function along with the data type of each. Below are two inputs for mutations we will use to create a pending friend request (CreateFriendInput) along with a mutation to authenticate that friend request.
Resolvers perform the necessary actions to complete a given query, mutation, or fragment. Query, as the name implies, queries the database to return information. Mutations are resolvers that create or alter a given entity in the database. Fragments allow a developer to break complex queries down into reusable parts to keep their code DRY. In this example, we will only be using mutation resolvers as we want to create and friend entity and then subsequently modify the existing friend entity while creating a corresponding entity for the friend who sent the request. Below are the resolvers for our CreateFriend and ConfirmFriend mutations.
The createFriend resolver above creates a friend entity and attaches it to the friends array on the user the friend request is being sent to. The friend_status passed in will be false as the friendship has not yet been accepted. The confirmFriend resolver will find the original friend entity created and change its status to true while creating the opposing entry on the sender’s friend list with a value of true. Now that we have our entities, inputs, and resolvers defined we are ready to move to Apollo and begin testing our mutations. In the process of testing our mutations, Apollo will generate queries for us to use on the front end to get the data we need. This is immensely helpful as we know the queries we are testing with will work when we move to the front end. Below is an example of a few users in our database who have not become friends, yet.
Using the above createFriend mutation Boudreaux will send Mr. Okra a friend request, which will create and attach a friend entity to Mr. Okra’s user entity with information about the request being sent. The result of this mutation called with “createFriendData” is illustrated below. The friend status of the friend entity is created with a value of false to signify that the user needs to authenticate the friendship for it to appear on both user’s friends lists. Note that we are finding the user for which the friend request is being sent to by their email address.
At this point we will use the confirm friend mutation in conjunction with confirmFriendData to update the friend_status of the friend entity we created with the last call in addition to creating a new friend entity representing Mr. Okra on Boudreaux’s friends list.
Finally, we can see that each user has a reference to the other’s friend status, ready to be rendered on the page. In the process of building and testing this data the objects createFriendData and confirmFriendData represent the data we will need to send to our graphQL server in order to perform the mutations. In this way we know the backend of our app is working properly and we are ready to shift focus to the client-side in completing our user story of being able to send, approve, and view a friendship status.