GraphQL is a query language for APIs developed by Facebook but open sourced in 2015. It is an alternative to REST-based APIs because it allows clients to decide which data they need instead of the server upfront deciding which data is delivered on each endpoint. It is a modern way of querying data, for example, some big companies next to Facebook like GitHub or Shopify have seen the advantages and implemented their APIs with GraphQL.
This Article can also be read on my personal blog: https://alexsteiner.de/blog/posts/graphql-with-vapor-part-1/
If you want to learn more about GraphQL go and check out their website. They provide great articles and tutorials to get started.
At the end of this article, we’ll have a running GraphQL server that we can reach via the web-based GraphQL query tool GraphiQL. As an example, we use a todo app. We are going to create new todos, list all existing once and also delete todos.
If you already have some experience with Vapor and have created a new application before, you will recognize the todo template, that comes with every new Vapor application, in this article. We are going to build the same functionality with GraphQL.
Let’s start by creating a new Vapor project. The easiest way to do that is using the toolbox which provides the command
vapor new GraphQL-Todo. This creates a new Swift Package Manager application with some default configuration for Vapor. We'll use this as a base to build our GraphQL server on top of that.
First, we need to modify our
Package.swift file and add new dependencies. The
GraphQLKit package provides a thin wrapper around the core functions to make them easier to use with
Vapor. It also includes the GraphQL implementation for Swift itself,
GraphQL. We don't need to import both packages directly as
GraphQLKit imports and exposes them automatically. The second new dependency is optional but provides us with a GraphiQL web page we can serve from our web server that gives as an IDE-like interface to send queries against our GraphQL server.
We’ll use the existing
Todo database model that is provided by the template. Nothing needs to be changed here to support GraphQL:
The REST template provides the following 3 endpoints:
- Get all todos as a list
- Create a new todo based on query input
- Delete an existing todo based on the todo identifier.
We will recreate the same endpoints in GraphQL. Therefore we create a class called
TodoAPI that holds the business and database logic for the endpoints. This gives us a nice abstraction of logic.
First, we will write the function that returns all
Todos that are stored in the database.
We have the function
getAllTodos. It takes a
Request object and to make it conform to later GraphQL functionality we also accept an argument of type
NoArguments. This type comes with the
Graphiti package and is just a typealias for an empty struct. We will discover how we can use other types of arguments in the next APIs. The rest of this function is normal Vapor related code. This function returns a future with an array of
Todo objects. The function body contains the query we make with Fluent. And because we have the
Request object given as a function parameter we can use it as an event loop for our database query.
Since Swift 5.1 we can omit the return keyword here because the function consists only of a single-line statement.
Now we start using an explicit argument type to create a new todo. We want to be able to create a new todo based on just a
title. To make this work with the GraphQL implementation we create a new struct that conforms to
Codable and we add all parameters we want to accept (in this case just the
title attribute but this list can be longer). In the function, we expect this
CreateTodoArguments type as the second argument instead of the
NoArguments type from our previous function. Then we initialize our
Todo object with the passed title argument and save it to the database by using our request object.
Finally, we also want to delete existing todos based on their identifier. We create the arguments struct for this function called
DeleteTodoArguments with the property
deleteTodo function expects this type as the second argument. This function returns a future of type
Bool to indicate that the deletion was successful. To delete the todo we use Fluents functionality. First, we search for the
Todo with the given id. If none was found we throw an
.notFound error which results in a Not-Found status message at the endpoint. Then we delete that todo and if it doesn't throw an error we transform the result to a boolean value of
true which indicate the successful delete progress.
When we want to expose the Todo type to our GraphQL schema we need to define names for each property. In most cases, these names will match the names of the properties of the struct.
Graphiti introduces the
FieldKeyProvider protocol. By making our Todo class conforming to this protocol we also create a new enum that can be represented by the type
String. This enum contains cases for each attribute we want to expose to our public API. This enables us to also have computed values for example.
The same we need to do for the
TodoAPI. Each endpoint and argument needs a case in an enum that represents the plain text name for later.
The only thing that is still missing is our GraphQL schema. It is a structure of all types we want to expose and endpoints that can be called. Endpoints are split into fetching endpoints which are grouped as queries and mutable endpoints that can modify data and are grouped as mutations.
If you want to know more about Queries and Mutation, I recommend the official GraphQL documentation.
For our todo example, we need to define our
Todo class, the query to get all todos and two mutations to create new todos and delete existing once. The
Schema type is based on two generics. One is the API which we created as
TodoAPI and one is the context which is the
Request object in our application.
Why can’t the compiler infer all fields of our ToDo type from the given Swift class? That’s because we could also have computed values that come from other sources or functions that work as properties and can also be added to types.
Register Schema on Vapor router
To serve our GraphQL schema as a REST endpoint we use the GraphQLKit functions to easily register the schema together with an instance of our
TodoAPI on our router. What it does is creating a GET and a POST endpoint at
/graphql. This is the default path for a REST-based GraphQL server. While we are in a development environment we also enable the GraphiQL web page to debug our application easily with a built-in IDE-like endpoint.
Testing our API
We can run the Vapor application now and then visit http://localhost:8080. We will see a dashboard where we can enter queries on the left-hand side and see results in the middle. On the right, we can find a documentation of our API that we exposed with our
Here are examples of queries we can send to our API and expect results back:
GraphQL is a great query language that can also be used with Swift and the great ecosystem Vapor provides. Some functionalities like subscriptions with websockets are not implemented yet but this will probably come in the future too.
This example demonstrates the type-safe syntax Swift provides combined with the flexibility in querying data GraphQL has. All the code you have seen in this article can be found in a GitHub project I created. This project can be used the same way the Vapor API template is used for new applications.
What experiences do you have with GraphQL and Server Side Swift? In which areas do you think can the community help to provide a better experience creating GraphQL applications in Swift? Let me know your thoughts in the comments, on Twitter or via email.