[Tutorial] Creating simple blog app with Rails 6 and GraphQL Part #1
Tags: graphql, rails, ruby
Published: 2019-06-28 13:42:59

In this post I will try to show you how to create a simple blog app with GraphQL. GraphQL is one of the most popular topics this year, however I won't try to explain what is it. There are already many articles about it.
Also, don't treat this post as The Only Great Truth, I haven't even created "real" app using this technology yet, however I think that I can make a basic tutorial about usage of this technology.

It's my first post here, so hi everyone! Please leave your feedback, no matter how bad this tutorial is. Honestly I imagined it to be way better, but after spending few hours on it I don't want to just delete it (I though creating something like this is faster). Also, if you see mistakes in this tutorial, please correct me.

## Introduction
In this tutorial I will use:
*  Rails 6.0.0.rc1
*  RSpec-Rails 4.0.0.beta2
*  Graphql gem for Rails, v. 1.9.7

There is a github repo with code from this guide aviable (https://github.com/kuskoman/Rails-GraphQL-tutorial)

Lets start from creating new Rails application in API-only mode, without test framework.
```shell
rails new graphql-blog --api -T
```
Now we need to add some gems to our Gemfile.
Lets modify our :development and :test group.
```ruby
group :development, :test do
  [...]
  gem 'rspec-rails', '~> 4.0.0.beta2'
  gem 'factory_bot_rails', '~> 5.0', '>= 5.0.2'
  gem 'faker', '~> 1.9', '>= 1.9.4'
end
```

Then, lets create :test group below.
```ruby
group :test do
  gem 'database_cleaner', '~> 1.7'
  gem 'shoulda-matchers', '~> 4.1'
end
```

Lets say a few words about our testing stack and why are we requiring test dependencies in :development: they aren't neccessary. However, if you don't require RSpec in dev dependencies you will need to execute RSpec-related commands from test enviroment. Also, spec files won't be automatically created by generators.
Faker with Factory Bot Rails are also very useful- they allow us to seed database from console really quickly (for example `FactoryBot.create_list(:article, 30)`.

Lets install our gems.
```shell
bundle
```

Then, we can start to preparing our test enviroment. Lets start from generation RSpec install.
```shell
rails g rspec:install
```
then, we can require our Factory Bot methods. To do that we need to change `spec/rails_helper.rb` file by adding
```ruby
RSpec.configure do |config|
  [...]
  config.include FactoryBot::Syntax::Methods
  [...]
end

```

Now, lets add `database_cleaner` config to same file.
```ruby
RSpec.configure do |config|
  [...]
  config.include FactoryBot::Syntax::Methods
  [...]
  config.before(:suite) do
    DatabaseCleaner.clean_with(:truncation)
    DatabaseCleaner.strategy = :transaction
  end

  # start the transaction strategy as examples are run
  config.around(:each) do |example|
    DatabaseCleaner.cleaning do
      example.run
    end
  end
  [...]
```
Then, we need to configure Shoulda-matchers. This config is also stored in same file, but **outside** `RSpec.configure`.
```ruby
Shoulda::Matchers.configure do |config|
  config.integrate do |with|
    with.test_framework :rspec
    with.library :rails
  end
end
```

Yeah! We finished setting up our test suite. Now we can move to... installing GraphQL.

Lets move back to our gemfile, and add graphql gem.
```ruby
[...]
gem 'graphql', '~> 1.9', '>= 1.9.7'
[...]
```

Now we can generate its installation using rails generator.
```shell
rails g graphql:install
```

Because of `--api` flag during project creation we will probably see this information in console:
```
Skipped graphiql, as this rails project is API only
  You may wish to use GraphiQL.app for development: https://github.com/skevy/graphiql-app
```
GraphiQL is an app like Postman, but designed only for GraphQL and working in web browser. Of course we will need a tool instead web version of GraphiQL. We can just [use its standalone version](https://github.com/graphql/graphiql), or for example [GraphQL Playground](https://github.com/prisma/graphql-playground) (which is unfortunately built on Electron, as well as have some annoying issues. But not annoying enough to force me to create issue on Github).
Recantly [Postman](https://www.getpostman.com/) also started supporting GraphQL. 

Well, lets get back to the topic. As we can see, we created entire file structure in `app/graphql` folder. Let me say a few words about them.
`mutations` is a place for (yeah, that was unexpected) mutations. You can treat them a bit like post actions in REST api.
`types` is a place for types, as well as queries stored in `query_type.rb`. We can change this behavior, but we won't do it in this tutorial.

Unfortunately, before we start making GraphQL stuff, we need to create models, which are just same as in every other application.
Lets start from user model. This model will have secure password, so we need to add
```ruby
gem 'bcrypt', '~> 3.1', '>= 3.1.13'
```
to our Gemfile (then obviously run `bundle`).

Because I (usually) love TDD I will start from generating user model, then immidiately moving to its specification.

```shell
rails g model user name:string password:digest
```

Now, lets create specification for our user model.
`spec/models/user_spec.rb`


```ruby
require 'rails_helper'

RSpec.describe User, type: :model do
  let!(:user) { build(:user) }

  it 'has a valid factory' do
    expect(user).to be_valid
  end

  describe 'validation' do
    it { should validate_presence_of(:name) }
    it { should validate_presence_of(:password) }

    it { should validate_length_of(:name).is_at_least(2).is_at_most(32) }
    it { should validate_length_of(:password).is_at_least(6).is_at_most(72) }

    it { should validate_uniqueness_of(:name).case_insensitive }

    context 'username validation' do
      it 'should accept valid usernames' do
	valid_usernames = ['fqewqf', 'fFA-Ef231', 'Randy.Lahey', 'jrock_1337', '1234234235' ]

        valid_usernames.each do |username|
          user.name = username
            expect(user).to be_valid
          end
        end
      end
       
    it 'should not accept invalid usernames' do
      invalid_usernames = ['!@3', 'ff ff', '...', 'dd@dd.pl', 'wqre2123-23-', '-EW213123ed_d', '', '---', 'pozdro_.dla-_wykopu']

      invalid_usernames.each do |username|
        user.name = username

        expect(user).to be_invalid
      end
    end
  end
end

```

Now we can run our tests using `rspec` command and see failing 5 of 7 examples (yes, in real TDD it would be 7/7).

Let me add something about password validation- I wanted to allow user to use all alphanumeric characters and ., - and _, but not more than one time in a row and not in begining and end of ths nickname.

Lets fill our user model with code.

```ruby
class User < ApplicationRecord
  has_secure_password

  validates :name, presence: true, length: { minimum: 3, maximum: 32 },
  	format: { with: /\A[0-9a-zA-Z]+([-._]?[0-9a-zA-Z]+)*\Z/}, uniqueness: { case_sensitive: false }
  # actually pasword from has_secure_password has a 72 character limit anyway, but w/e
  validates :password, presence: true, length: { minimum: 6, maximum: 72 }
end
```

All tests should pass now. However, before we move to creating type lets change a bit user factory located in `spec/factories/user.rb` to add a bit of randomness.
```ruby
FactoryBot.define do
  factory :user do
    name { Faker::Internet.username(separators = %w(._-)) }
    password { Faker::Internet.password(6, 72) }
  end
end
```

Now we can finally move to GraphQL stuff. 

Lets start from creating user type. Lets (I think I'm using this word a little bit too frequently and its confusing) create `user_type.rb` in `app/graphql/types` directory.

```ruby
module Types
	class UserType < Types::BaseObject
	  description "Just user, lol"
	  field :id, ID, null: false
	  field :name, String, null: false
	  field :created_at, GraphQL::Types::ISO8601DateTime, null: false
	end
end
```

GraphQL Ruby syntax is pretty clear and I dont think that I need to explain anything in this code (change my mind).

Now lets make our first query.
Lets move to `app/graphql/types/query_type.rb`. We will see example file, generated by command `rails g graphql:install`.

```ruby
module Types
  class QueryType < Types::BaseObject
    # Add root-level fields here.
    # They will be entry points for queries on your schema.

    # TODO: remove me
    field :test_field, String, null: false,
      description: "An example field added by the generator"
    def test_field
      "Hello World!"
    end
  end
end
```

We can test how it works if we want to. Lets launch `rails server` and connect to it using one of tools mentioned eariler. I will use [GraphQL Playground](https://github.com/prisma/graphql-playground), because I'm feeling most comfortable with this tool. After opening it will ask us about local file or endpoint. Lets choose endpoint and enter our application graphql endpoint adress (usually `localhost:3000/graphql`).

![](https://thepracticaldev.s3.amazonaws.com/i/k1e9fvn8lx2l9s0df98j.png)

We should be able to access our current schema now.

![](https://thepracticaldev.s3.amazonaws.com/i/i73wyrlnrn71bvzp2zio.png)

By default GraphQL Playground will ask server about current schema every 2000 milliseconds, what can be really annoying if we have opened console. We can change this behaviour by editing config file.

We need to click `Application -> Settings` in the top bar of application, or just hit `ctrl + comma`.
Now we should be able to see line responsible for refreshing schema.

```json
{
  "schema.polling.interval": 2000,
}
```

We can change it's value, but we need to remember that refreshing schema not enough often is also not a good idea.
Now lets get back to our GraphQL endpoint. Now we are able to make queries and mutations on automatically generated dummy fields. We can for example make a query:

```graphql
query {
  testField
}
```

and recive json as response from server.

```json
{
  "data": {
    "testField": "Hello World!"
  }
}
```

We should now make a test for our first query, but since it's very simple (yeah, it's not an excuse) I will skip it. If you want to make test first, you can jump to bottom of this part of guide and read about testing mutations. Query tests are very similiar.
Anyway, lets move again to `app/graphql/types/query_type.rb` and enter code like this:

```ruby
module Types
  class QueryType < Types::BaseObject
    field :user, UserType, null: false do
      description "Find user by ID"
      argument :id, ID, required: true
    end

    def user(id:)
      User.find(id)
    end
  end
end
```

Now lets say something about it.
`field :user` will create a new field named user and automaticaly look for a method user.
`null: false` tells that this field can't be empty. It means querying for user which does not exist will raise error.
`description ...` will create something like documentation, which we are able to use from our tools.
`argument :id, ID, required: true` requires to specify user id to use this query.

Lets manually test how it works.

```graphql
query {
  user(id: 1) {
    name
  }
}
```

The code above should raise error, because this value can't be null, and we haven't created first user yet.

```json
{
  "error": {
    "error": {
      "message": "Couldn't find User with 'id'=1",
      "backtrace": [...],
      "data": {}
  }
}

```

Lets fix it by adding users to database.
Lets open Rails console
`rails c`
and produce some users
`FactoryBot.create_list(:user, 10)`

Now, if we query again, we should see requested user data in JSON format.

```json
{
  "data": {
    "user": {
      "name": "keren"
    }
  }
}
```

We can create another query.
`app/graphql/types/query_type.rb`

```ruby
module Types
  class QueryType < Types::BaseObject
    field :user, UserType, null: false do
      description "Find user by ID"
      argument :id, ID, required: true
    end

    field :all_users, [UserType], null: true do
      description "Find all users"
    end

    def user(id:)
      User.find(id)
    end

    def all_users
      User.all
    end
  end
end
```

Now we can move to creating first mutations.

Because of lack of tools for organizing GraphQL and/or only because my lack of knowledge about them, we are going to place entire mutation in test file as a string. Of course, if we want to, we can move it to another support file, but I don't like doing it that way.
Some time ago I found a very useful piece of code in [this article](https://pradyumna.io/2018/05/20/testing-your-graphql-rails-app.html).
We will start from copying it to `spec/support/graphql/mutation_variables.rb` file.

```ruby
module GraphQL
	module MutationVariables
		def mutation_variables(factory, input = {})
			attributes = attributes_for(factory)

			input.reverse_merge!(attributes)

			camelize_hash_keys(input).to_json
		end

		def camelize_hash_keys(hash)
			raise unless hash.is_a?(Hash)

			hash.transform_keys { |key| key.to_s.camelize(:lower) }
		end
	end
end
```

What does it do? It takes :factory and optional attributes as argument. When we pass factory it is transforming it, allowing us to use them as mutation variables. If we also pass additional arguments, they will replace attributes from factory ([here we can read about camelize_hash_keys method ](https://apidock.com/rails/Hash/reverse_merge)).

After creating this module, we can include it to RSpec tests.

`spec/rails_helper.rb`

```ruby
RSpec.configure do |config|
  [...]
  config.include GraphQL::MutationVariables
  [...]
end
```

Other helper I really like is RequestSpecHelper I saw in (this tutorial)[https://scotch.io/tutorials/build-a-restful-json-api-with-rails-5-part-one].

It looks like this:

```ruby
module RequestSpecHelper
  def json
    JSON.parse(response.body)
  end
end
```

then we also need to add
```ruby
RSpec.configure do |config|
  [...]
  config.include RequestSpecHelper
  [...]
end
```
in our `rails_helper.rb`.

For now we will create a not really useful mutation, just to show how it works.
First thing we have to do is creating new file in `app/graphql/mutations`. Lets name it `create_user.rb`. Before filling in this file lets create `spec/graphql/mutations/create_user_spec.rb` and move to this file.


```ruby
require 'rails_helper'

RSpec.describe 'createUser mutation', type: :request do
	describe 'user creation' do
		before do
			post('/graphql', params: {
			  query: %(
					mutation CreateUser(
						$name: String!,
						$password: String!,
					) {
						createUser(input: {
							name:$name,
							password:$password,
						}) {
							user {
								id
								name
							}
							errors,
						}
					}
				),
			  variables: mutation_variables(:user, input_variables)
			})
		end

		context 'when input is valid' do
			let(:user_attrs) { attributes_for(:user) }
			let(:input_variables) { user_attrs }

			it 'returns no errors' do
				errors = json["data"]["createUser"]["errors"]
				expect(errors).to eq([])
			end

			it 'returns username' do
				user_name = json["data"]["createUser"]["user"]["name"]
				expect(user_name).to eq(user_attrs[:name])
			end
		end

		context 'when input is invalid' do
			context 'when username is empty' do
				let(:input_variables) { {"name": ""} }

				it 'returns errors' do
					errors = json["data"]["createUser"]["errors"]
					expect(errors).not_to be_empty
				end

				it 'does not return user' do
					user = json["data"]["createUser"]["user"]
					expect(user).to be_nil
				end
			end

			context 'when password is invalid' do
				let(:input_variables) { {"password": "d"} }

				it 'returns errors' do
					errors = json["data"]["createUser"]["errors"]
					expect(errors).not_to be_empty
				end

				it 'does not return user' do
					user = json["data"]["createUser"]["user"]
					expect(user).to be_nil
				end
			end
		end
	end
end
```

After running `rspec` we should see result:
`14 examples, 6 failures`

Lets create base mutation file:
`app/graphql/mutations/base_mutation.rb`

```ruby
module Mutations
	class BaseMutation < GraphQL::Schema::RelayClassicMutation
		object_class Types::BaseObject
		input_object_class Types::BaseInputObject
	end
end
```

Then lets fill our mutation.
`app/graphql/mutations/create_user.rb`

module Mutations
	class CreateUser < BaseMutation
		argument :name, String, required: true
		argument :password, String, required: true

		field :user, Types::UserType, null: true
		field :errors, [String], null: false

		def resolve(name:, password:)
			user = User.new(name: name, password: password)
			if user.save
				{
					user: user,
					errors: [],
				}
			else
				{
					user: nil,
					errors: user.errors.full_messages,
				}
			end
		end
	end
end

After running `rspec` again we will se just a same result. It's because we need to also add mutation to mutation_type.rb like we did with queries and query_type.
Default `app/graphql/types/mutation_type.rb` seems like this:

```ruby
module Types
  class MutationType < Types::BaseObject
    # TODO: remove me
    field :test_field, String, null: false,
      description: "An example field added by the generator"
    def test_field
      "Hello World"
    end
  end
end
```

We need to replace it with:

```ruby
module Types
  class MutationType < Types::BaseObject
  	field :create_user, mutation: Mutations::CreateUser
  end
end
```

Lets get back to our mutation. What is happening here?
`argument :name, String, required: true` we are requiring name here, mutation without this argument will return error. Also we are specyfing type of input object (String).
`argument :password, String, required: true` same thing as above, just related to password.

`field :user, Types::UserType, null: true`
`field :errors, [String], null: false`
We are returning two objects- UserType, which can be null if user is not created, and errors, which is an empty array if user is created, or has `user.errors.full_messages,` if it isn't.

Now we can play with our mutation in our favourite GraphQL tool.

```graphql
mutation {
  createUser(input:{
    name:"DDD",
    password:"leszkesmieszke"
  }) {
    user {
      id
      name
    }
  }
}
```

```json
{
  "data": {
    "createUser": {
      "user": {
        "id": "15",
        "name": "DDD"
      }
    }
  }
}
```


Thats first part of tutorial. Honestly, I'm not sure if I'm going to create rest of them, it probably depends on reactions of this one. As I said, I imagined this tutorial as more than a bit better than it is.