GraphQL Server Built on Ktor
Disclaimer: I do not use Kotlin professionally, and I am learning the language and tools by building something and sharing my experiences here. This application is not production ready and is missing crucial pieces, like production-ready error handling. Please review and understand any code before reusing it, and feel free to leave comments on the GitHub project
With authentication in place on the Ktor server, we can move on to building out the skeleton of the GraphQL server, incorporating the authenticated user. The full source code for this part of the process can be found here. Topics that will be covered will be programmatically building the GraphQL server and schema as well as accessing the authenticated user from within the GraphQL execution context.
The first step is to finalize how the web client will provide the server with the JWT created by our authentication module.
I chose to use a session cookie because it is simple and because the API will only be consumed by my own client, if any,
allowing me to assume I won’t have any CORS issues. I installed
the session feature
in the auth module and configured it to verify the JWT stored on the session.
Within the validation phase, I create a new
UserPrincipal that wraps a
User instance, giving any authenticated
call quick access to the current user.
(Since originally writing this code, I learned a more idiomatic way to implement the same logic, and I really like the flow.)
To build out the GraphQL schema, I will use the kotlin-graphql package. With this package we will define the schema as a collection of classes and functions directly in Kotlin, instead of as a standard GraphQL schema SDL. Kotlin-graphql will take each function on the provided classes and treat them as mutations or queries on a generated GraphQL schema. For example, a single user query would look like
Next we will make the GraphQL functions aware of the application call context, giving it access to the authenticated user.
Kotlin-graphql has the concept of a
GraphQLContext annotated argument that can be added to any function. This argument will not be
presented on the GraphQL schema and is passed into the execution on each request. So now we will add a custom context
class to wrap the Ktor
ApplicationCall and pass it into the new
me function using the
And finally there is the process of defining the GraphQL server and adding it to the Ktor application. There are couple of pieces to this, but the majority of it is creating the server and defining how to execute the query against it:
To add this GraphQL server to the application we will define an installable module,
just like with the authentication. Along with the changes noted above, the module also configures the
and sets up a static route to serve up the HTML version of the GraphQL Playground, tweaking it slightly to default to
sending cookies for authentication.
And that has the server up and running. The next step will be to design out the domain of the application and create the necessary GraphQL queries and mutations for a client to be able to interact with it.