Install:
- Elixir 1.13.4
- Erlang/OTP 24
- Postgres SQL 13.0
To start your Phoenix server:
- Install dependencies with
mix deps.get
- Create and migrate your database with
mix ecto.setup
- Start Phoenix endpoint with
mix phx.server
or inside IEx withiex -S mix phx.server
Now you can visit localhost:4000/graphiql
from your browser and start querying.
Blogo is a toy project. It helped me to learn and apply graphql tools and concepts. The project models three related entities: Authors, Posts and Tags of a blog website.
The database diagram can be checked below:
An author wrote many posts and can be classified by many tags. Moreover a post could be written by many authors and also be classified by many tags. So all the relations are NxM
.
That is a interesting situation to work with graphql because it allows the consumer to write a query choosing which fields and relations should be loaded.
The app is hosted on render at https://blogo.onrender.com/graphiql.
Due to render free hosting constraints the loading time can take a little long more than expected.
You can find an author and all its posts along with its tags:
query{
author(id: "856ffaaa-bde4-45fa-8907-508aaf34e465"){
id
name
age
country
insertedAt
posts{
id
title
views
insertedAt
tags{
name
}
}
}
}
resulting:
{
"data": {
"author": {
"age": 70,
"country": "EUA",
"id": "856ffaaa-bde4-45fa-8907-508aaf34e465",
"insertedAt": "2022-11-06T20:03:58Z",
"name": "Bill Bryson",
"posts": [
{
"id": "8334c62b-c564-449c-8123-05614acb139d",
"insertedAt": "2022-11-06T20:03:58Z",
"tags": [
{
"name": "estrangeiro"
},
{
"name": "nao_ficcao"
}
],
"title": "Breve Historia de quase tudo",
"views": 23
}
]
}
}
}
Or you can find all the authors and posts related to a tag:
query{
tag(id: "4f0697c9-1933-4c33-a759-d5ad0b9268dd"){
id
name
posts{
id
title
}
authors{
id
name
}
}
}
resulting:
{
"data": {
"tag": {
"authors": [
{
"id": "b628218d-ba7f-4a02-9dd6-1c1a61a21df9",
"name": "Felipe Castilho"
},
{
"id": "32986637-d20d-4017-a825-a57f14277fc3",
"name": "Clarice Lispector"
}
],
"id": "4f0697c9-1933-4c33-a759-d5ad0b9268dd",
"name": "ficcao",
"posts": [
{
"id": "95f16281-895b-4c87-b886-bccbd17d9e1f",
"title": "A hora da estrela"
},
{
"id": "d86f73f4-c3dd-4530-8c1e-f793cac22341",
"title": "Felicidade Clandestina"
},
{
"id": "314d2808-5216-4485-961a-8bd95da2dcef",
"title": "O Legado do Folclore"
},
{
"id": "61ca7379-0ee9-48c2-b704-fb31cc7e5c3e",
"title": "Serpentario"
}
]
}
}
}
Besides the possibility to choose fields and relations, it is also possible to apply query filtering params on them.
The below query brings three tags sorted by name asc
and all its authors where some of the text fields matches with "brasil"
:
query{
tags(queryParams:{
limit: 3,
sortBy: [{field: NAME, order: ASC}],
}){
name
authors(queryParams:{
filters: {textSearch: "brasil"}
}){
name
age
country
}
}
}
resulting:
{
"data": {
"tags": [
{
"authors": [],
"name": "estrangeiro"
},
{
"authors": [
{
"age": 36,
"country": "Brasil",
"name": "Felipe Castilho"
}
],
"name": "fantasia"
},
{
"authors": [
{
"age": 36,
"country": "Brasil",
"name": "Felipe Castilho"
},
{
"age": 57,
"country": "Brasil",
"name": "Clarice Lispector"
}
],
"name": "ficcao"
}
]
}
}
Since graphql APIs allows the consumer to choose fields and relations, it is prudent to the backend to implement some limits. The Blogo project implements complexity limit and depth limit. A query could not ask for more than 250 fields and cannot have a depth level above 5.
Even though dataloader helps us reducing database round trips, this kind of approach prevents heavy load queries to be executed.
During the implementation I fould really good texts and tutorials that helped me a lot:
- Bruce Williams and Ben Wilson: Craft GraphQL APIs in Elixir with Absinthe
- Sheharyar Naseer: Error standardization
- Marcos Tapajós: Absinthe Graphql nested queries security
- Absinthe docs on hex: Custom Scalar Types
- Absinthe docs on hex: Complexity security