A demo project that demonstrates a GraphQL API implemented with sangria, akka-http, circe.
After starting the server with
sbt run
# or, if you want to watch the source code changes
sbt ~reStart
You can run queries interactively using graphql-playground
by opening http://localhost:8080 in a browser or query the
/graphql
endpoint directly:
This demo contains several packages under src/main/scala
:
- common - common definitions that are used by all examples. It also contains some plumbing (like full HTTP routing) to make more advanced examples more simple.
- demos - step by step walkthrough from the most basic examples to more advanced GraphQL servers that use DB, auth, etc. Every demo object (like
Demo1Basics
) is self-contained and containsmain
method (extendsApp
), so you can run it. - finalServer - the final server implementation that includes all demonstrated elements
- model - defines model for the
Book
andAuthor
case classes as well as repositories (including SQL-based implementation)
I would also recommend to explore the /src/test/scala
folder - it contains several example tests.
In addition to the final server implantation the project contains step-by-step demonstration of various GraphQL and Sangria concepts. Every step is self-contained and builds up on top of previous steps:
- Most basic example of GraphQL Schema definition and query execution (Demo1Basics.scala)
- Using
deriveObjectType
to derive GraphQL object type based on theBook
case class (Demo2MacroDerivation.scala) - Exposing the GraphQL schema via HTTP API (Demo3ExposeGraphQLViaHttp.scala)
- Use an SQL database to load the book and author data returned by GraphQL API (Demo4AddingDatabase.scala)
- Use field arguments to provide pagination, sorting and filtering (Demo5PaginationSortingFiltering.scala)
- Representing book-author relation with an object type field (Demo6BookAuthorRelation.scala)
- Efficiently load author information with Fetch API (Demo7UsingFetchers.scala)
- Efficiently load author books information with Fetch API (Demo8FetchAuthorBooksRelation.scala)
- Guard GraphQL API from abuse with static query complexity analysis (Demo9QueryComplexityAnalysis.scala)
- Securing GraphQL API with OAuth and JWT tokens (Demo10Auth.scala)
In each class you will find comments that highlight separate steps (with // STEP: ...
)
and things that are new in comparison with previous steps (with // NEW: ...
).
If you add extra highlighting in the IDE (e.g. TODO highlighting in Intellij IDEA)
you can get additional visual hints:
The demo uses a simple H2 in-memory database (it would be re-created automatically when server starts).
DB schema DDL
create table "BOOKS" (
"BOOK_ID" VARCHAR NOT NULL PRIMARY KEY,
"TITLE" VARCHAR NOT NULL,
"AUTHOR_ID" VARCHAR NOT NULL,
"description" VARCHAR)
alter table "BOOKS"
add constraint "AUTHOR_FK" foreign key("AUTHOR_ID")
references "AUTHORS"("AUTHOR_ID") on update NO ACTION on delete NO ACTION
create table "AUTHORS" (
"AUTHOR_ID" VARCHAR NOT NULL PRIMARY KEY,
"NAME" VARCHAR NOT NULL,
"BIO" VARCHAR,
"BIRTH_DATE" DATE NOT NULL,
"DEATH_DATE" DATE)
The example book data was taken from Open Library.
In order to demonstrate the authentication and authorization, demo uses JWT tokens with basic OAuth bearer Authorization
header.
If you would like to try out the examples (in particular me
field), you can use following header:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyTmFtZSI6IkpvaG4gRG9lIiwiYm9va3MiOlsiT0wzMDMxMFciLCJPTDk5ODQzVyJdfQ.ffqCpfgWrY40k8JWj56mUpvW0ZfWLhTqrLHwMZeXgXc
The demo project implements following schema:
type Author {
id: String!
name: String!
bio: String
birthDate: Date!
deathDate: Date
books: [Book!]!
}
type Book {
id: String!
title: String!
description: String
author: Author
}
input BookInput {
id: String!
title: String!
description: String
authorId: String!
}
enum BookSorting {
Title
Id
}
"Represents local date. Serialized as ISO-formatted string."
scalar Date
type Me {
"The name of authenticated user"
name: String!
favouriteBooks: [Book!]!
}
type Mutation {
addBook(book: BookInput!): Book
deleteBook(id: ID!): Book
}
type Query {
"Gives the list of books sorted and filtered based on the arguments"
books(limit: Int = 5, offset: Int = 0, sortBy: BookSorting, title: String): [Book!]!
"Returns a book with a specified ID."
book(id: ID!): Book
authors(limit: Int = 5, offset: Int = 0): [Author!]!
author(id: ID!): Author
"Information about authenticated user. Requires OAuth token."
me: Me
}