Chapter 2 Set up your environment

2.1 Download the project

Clone the repository.

git clone https://github.com/wesovilabs-workshops/workshop-graphql-go.git
cd workshop-graphql-go

2.2 Project structure

In this workshop, we will make use of library github.com/99designs/gqlgen

./config

This package is used to load the configuration**

./database

This package contains both the database model and the database queries.

./graphql/exec

Generated code by 99designs/gqlgen

./graphql/model

Generated input objects by 99designs/gqlgen and the defined output models

./graphql/resolver

This package contains the resolvers for our API.

./graphql/scalar

This package is used for defining custom scalar types.

./resources/docker-compose

Docker compose descriptor and configuration for the containers.

./resources/config

Configuration file used when running application locally.

./resources/graphql

This folder contains the graphql schema used by our application.

2.3 Running the server

From the root directory you just need to execute

make deploy

or in case of you don’t have make command installed

GOARCH=amd64 GOOS=linux CGO_ENABLED=0 go build -o build/workshop.linux;
docker build -f resources/docker/Dockerfile -t=wesovilabs/workshop-graphql-go:local .;
docker-compose -f resources/docker-compose/docker-compose.yml run --rm -p9001:9001 api

To verify that application was launched correctly, just open The GraphQL Playground

To clean the launched containers you just need to run

make docker-stop

or

docker-compose -f resources/docker-compose/docker-compose.yml down -v

2.3.1 While you’re coding

The above commands launch the full environment: database and application. On the other hand, when we’re coding, we usually prefer restart the API to reload the changes but keeping the database engine up.

  1. Launching database container from docker-compose
make database

or

docker-compose -f resources/docker-compose/docker-compose.yml run --rm -p5456:5432 database
  1. Run the application from your IDE or by command line
make run

or

APP_CONFIG_PATH=resources/config/config.yml go run main.go

2.4 Workshop Application

The application is a movie cataloging tool.

The purpose of this workshop is to enrich the application with new functionality that will be required in upcoming chapters.

2.4.1 Database

Workshop database model

Workshop database model

Databases will be populated with below data when postgres container is launched.

directors
id full_name country
1 Tim Burton USA
2 James Cameron Canada
3 Steven Spielberg USA
4 Martin Scorsese UK
5 Alfred Hitchcock USA
6 Clint Eastwood UK
actors
id full_name country male
1 Johnny Depp USA true
2 Winona Ryder USA false
3 Russell Crowe Australia true
4 Joaquin Phoenix USA true
5 Al Pacino USA true
6 Robert de Niro USA true
movies
id title release_year genre budget trailer director_id
1 Edward Scissorhands 1990 SciFi 20 https://www.yout 1
2 Gladiator 2000 Drama 103 https://www.yout 7
movies_actors
movie_id actor_id
1 1
1 2
2 3
2 4

2.4.2 API

The below operations are already implemented in our project.

2.4.2.1 Queries

  • listDirectors:[Director!]: It returns the list of directors.
  • listActors:[Actor!]:It returns the list of actors.
  • listMovies:[Movie!]: It returns the list of movies.
  • getMovie(movieId:ID!):Movie: It returns the movie with given id.

2.4.2.2 Mutations

  • addMovie(request:MovieRequest):Movie!: It adds a new movie.
  • addDirector(request:DirectorRequest):Director!: It adds a new director.
  • deleteDirector(“Identifier of the director” direction:ID!):[Director!]: It deletes the director with the given id.

2.4.2.3 Subscriptions

  • listenDirectorMovies(directorId:ID!):Movie!: It opens a communication with the server and is notified when a new movie is created for the passed directorId in the request.

2.4.3 GraphQL schema

The graphql schema for our application looks like this:

schema {
    # The query root of Workshop GraphQL interface.
    query: Query
    # The root query for implementing GraphQL mutations.
    mutation: Mutation
    # The root query for implementing GraphQL subscriptions.
    subscription: Subscription

}

"""Available queries for Workshop API"""
type Query {
    """It returns the list of directors."""
    listDirectors:[Director!]
    """It returns the list of actors."""
    listActors:[Actor!]
    """It returns the list of movies."""
    listMovies:[Movie!]
    """It returns the movie with the fiven id"""
    getMovie("Movie identifier" movieId:ID!):Movie
}

"""Available mutations for Workshop API"""
type Mutation {
    """I adds a new movie"""
    addMovie(request:MovieRequest):Movie!
    """I adds a new actor"""
    addDirector(request:DirectorRequest):Director!
    """I deletes the director with the fiven identifier"""
    deleteDirector("Identifier of the director" directorId:ID!):[Director!]
}

"""Available subscriptions for Workshop API"""
type Subscription {
    """It returns the movies for a given director"""
    listenDirectorMovies(directorId:ID!):Movie!
}


"""Request info for creating a movie"""
input MovieRequest {
    "Name of the movie"
    title: String!
    "Year when the movie was released"
    year: Int
    "Genre for the movie, supported values should be: SciFi, Drama, Comedy or Action"
    genre: String
    "Budget for the movie, the value is provided in Euro"
    budget: Float!
    "URL in which we can watch the trailer of this movie"
    trailer: String
    "Identifier of director"
    directorId: ID!
}

"""Movie details"""
type Movie {
    "Unique identifier for each movie"
    id: ID!
    "Name of the movie"
    title: String!
    "Year when the movie was released"
    year: Int
    "Genre for the movie, supported values should be: SciFi, Drama, Comedy or Action"
    genre: String
    "Budget for the movie, the value is provided in Euro"
    budget: Float!
    "URL in which we can watch the trailer of this movie"
    trailer: String
    "The director details of the movie"
    director: Director!
    "List of actors for the movie"
    actors("Total of returned actors" total:Int=1): [Actor!]
}

"""Director details"""
type Director{
    "Unique identifier for each director"
    id: ID!
    "Full name of the director"
    fullName: String!
    "Country in which the director was born"
    country: String
}

"""Director creation request"""
input DirectorRequest{
    "Full name of the director"
    fullName: String!
    "Country in which the director was born"
    country: String
}

"""Actor details"""
type Actor {
    "Unique identifier for each actor"
    id: ID!
    "Full name of the actor"
    fullName: String!
    "Country in which the actor was born"
    country: String
    "Gender of actor: Supported values are male or female"
    gender: String
}