How to build a GraphQL server with Swift and Vapor

Alexander Steiner
6 min readFeb 9, 2020

--

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.

Getting started

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, Graphiti with 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.

Database Model

We’ll use the existing Todo database model that is provided by the template. Nothing needs to be changed here to support GraphQL:

Business Logic

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 id. The 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.

Field Names

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.

GraphQL Schema

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 Schema object.

Here are examples of queries we can send to our API and expect results back:

Conclusion

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.

--

--

Alexander Steiner
Alexander Steiner

Written by Alexander Steiner

Server Side Swift developer with background in iOS and web development.

No responses yet