Run Scaffold

This tutorial describes how to use the souls g scaffold command to create a GraphQL CRUD API. If you need to review GraphQL Ruby, we recommend reading this graphql-ruby first.

scaffold

Run the souls g scaffold_all command

When the migration is finished, db/schema.rb will be updated.

The souls g scaffold_all command is based on the type of db/schema.rb with the files needed for CRUD.

The corresponding test is also automatically generated.

$ souls g scaffold_all
.
.
Created file! : ./app/graphql/types/edges/user_edge.rb
Created file! : ./app/graphql/types/connections/user_connection.rb
Created file! : ./app/graphql/resolvers/user_search.rb
Created file! : ./spec/factories/users.rb
Created file! : ./spec/mutations/base/user_spec.rb
Created file! : ./spec/queries/user_spec.rb
Created file! : ./spec/resolvers/user_search_spec.rb
Created file! : ./spec/policies/user_policy_spec.rb
๐ŸŽ‰ Generated SOULs CRUD Files

11 files were automatically generated based on the database schema.

Please refer to the SOULs guide for details on the files automatically generated by souls g scaffold .

souls g scaffold $MODEL_NAME

You can perform scaffold on a single Model.

$ souls g scaffold user

Executing the souls test command

$ souls test
Finished in 0.22436 seconds (files took 1.35 seconds to load)
36 examples, 30 failures

Failed examples:

30 tests have failed out of 36 tests.

Define test data in FactoryBot

Next, a FactoryBot that generates User Model test data

apps/api/spec/factories/

Defined in.

The SOULs API / Worker uses Faker, FactoryBot, and Gimei to generate test files.

Faker

https://github.com/faker-ruby/faker

FactoryBot

https://github.com/thoughtbot/factory_bot

Gimei

https://github.com/willnet/gimei

When you actually share the app with the client, convert it to human-friendly data as much as possible.

apps/api/spec/factories/users.rb
require "gimei"
FactoryBot.define do
  factory :user do
    uid { Faker::Internet.password }
    username { Gimei.kanji }
    screen_name { Faker::Internet.unique.username }
    last_name { Gimei.last.hiragana }
    first_name { Gimei.first.hiragana }
    last_name_kanji { Gimei.last.kanji }
    first_name_kanji { Gimei.first.kanji }
    last_name_kana { Gimei.last.katakana }
    first_name_kana { Gimei.last.katakana }
    email { Faker::Internet.unique.email }
    tel { Faker::PhoneNumber.subscriber_number(length: 10) }
    icon_url { "https://picsum.photos/200" }
    birthday { Faker::Date.birthday(min_age: 18, max_age: 65) }
    gender { Gimei.male }
    lang { "ja" }
    category { "user" }
    roles_mask { 1 }
    is_deleted { false }
  end
end
apps/api/spec/factories/articles.rb
FactoryBot.define do
  factory :article do
    association :user, factory: :user
    title { Faker::Book.unique.title }
    body { Faker::Quote.matz }
    thumnail_url { Faker::Internet.url }
    public_date { Time.now }
    association :article_category, factory: :article_category
    is_public { false }
    just_created { false }
    slug { Faker::Internet.password(min_length: 16) }
    tags { %w[tag1 tag2 tag3] }
    is_deleted { false }
    created_at { Time.now }
    updated_at { Time.now }
  end
end
apps/api/spec/factories/article_category.rb
FactoryBot.define do
  factory :article_category do
    name { Faker::Books::CultureSeries.culture_ship_class }
    tags { %w[tag1 tag2 tag3] }
    is_deleted { false }
    created_at { Time.now }
    updated_at { Time.now }
  end
end
apps/api/spec/factories/comment.rb
FactoryBot.define do
  factory :comment do
    association :article, factory: :article
    from { Faker::Games::SuperMario.character }
    body { Faker::JapaneseMedia::StudioGhibli.quote }
    is_deleted { false }
    created_at { Time.now }
    updated_at { Time.now }
  end
end

Run the test again

$ souls test
Comment Model ใƒ†ใ‚นใƒˆ
  Comment ใƒ‡ใƒผใ‚ฟใ‚’ๆ›ธใ่พผใ‚€
    valid Comment Model

ArticleCategory Model ใƒ†ใ‚นใƒˆ
  ArticleCategory ใƒ‡ใƒผใ‚ฟใ‚’ๆ›ธใ่พผใ‚€
    valid ArticleCategory Model

Finished in 1.3 seconds (files took 1.28 seconds to load)
36 examples, 0 failures

Randomized with seed 43312

In this way, tests are an integral part of the SOULs framework. Immediate detection of errors will greatly reduce overall development time.

Check CRUD API (Registration / Display / Update / Logical Delete / Delete)

Now the CRUD API for the User table.

SOULs framework provides two native methods for deletion. Logical deletion sets the is_deleted flag, effectively removing the object while keeping it in the database. Physical deletion removes the object from the database irretrievably.

Data registration, update, logical deletion, and physical deletion are defined in mutation/base .

app/graphql/mutations/base/

The display of data is defined in queries , resolvers .

app/graphql/queries

app/graphql/resolvers

Information about data types is defined in types.

app/graphql/types

Please refer to the SOULs guide for the details of the files.

Now let's take a look at the API generated by the souls g scaffold command.

Registration --createUser Mutation

Let's execute the following query to confirm that User data can be registered.

Sample query

mutation {
  createUser(
    input: {
      uid: "uniq-id"
      username: "SOULs"
      email: "info@test.com"
      }
  ) {
    userEdge {
      node {
        uid
        id
        username
        email
      }
    }
  }
}

If successful, the following response will be returned:

{
  "data": {
    "createUser": {
      "userEdge": {
        "node": {
          "uid": "uniq-id",
          "id": "VXNlcjo0",
          "username": "SOULs",
          "email": "info@test.com"
        }
      }
    }
  }
}

View --userSearch Resolver

We use a resolver to retrieve registered data.

query {
  userSearch(filter: { isDeleted: false }) {
    edges {
      node {
        id
        uid
        username
        email
      }
    }
    nodes {
      id
    }
    pageInfo {
      hasNextPage
    }
  }
}

If successful, the following response will be returned:

{
  "data": {
    "userSearch": {
      "edges": [
        {
          "node": {
            "id": "VXNlcjo0",
            "uid": "uniq-id",
            "username": "SOULs",
            "email": "info@test.com"
          }
        }
      ],
      "nodes": [
        {
          "id": "VXNlcjo0"
        }
      ],
      "pageInfo": {
        "hasNextPage": false
      }
    }
  }
}

We have confirmed that data has been registered successfully.

Update --updateUser Mutation

Next, we can update data using the following query:

mutation {
  updateUser(input: {
    id: "VXNlcjo0"
    username: "SOULs API"
    }) {
    userEdge {
      node {
        id
        username
        email
      }
    }
  }
}

If successful, the following response will be returned.

{
  "data": {
    "updateUser": {
      "userEdge": {
        "node": {
          "id": "VXNlcjo0",
          "username": "SOULs API",
          "email": "info@test.com"
        }
      }
    }
  }
}

The username has been successfully updated.

Logical Delete --deleteUser Mutation

SOULs framework delete sets the is_deleted flag to true . In the actual operation of the application, in order to prevent data loss due to incorrect operation by the user, is_deleted is set to true instead of actual deletion.

Now let's execute the following query:

mutation {
  deleteUser(input: { id: "VXNlcjo0" }) {
    user {
      id
      username
      email
    }
  }
}

If successful, the following response will be returned.

{
  "data": {
    "deleteUser": {
      "user": {
        "id": "VXNlcjo0",
        "username": "SOULs API",
        "email": "info@test.com"
      }
    }
  }
}

is_deleted flag is now true .

Physical Delete --destroDeleteUser Mutation

In the SOULs framework, destroyDelete Mutation actually deletes data.

mutation {
  destroyDeleteUser(input: { id: "VXNlcjo0" }) {
    user {
      id
      username
      email
    }
  }
}

If successful, the following response will be returned.

{
  "data": {
    "destroyDeleteUser": {
      "user": {
        "id": "VXNlcjo0",
        "username": "SOULs API",
        "email": "info@test.com"
      }
    }
  }
}

Data has been deleted.

Like the User Model, the Artice and ArticleCategory are complete.

Please refer to the documentation for details on the required queries and parameters.

Click DOCS on the right to view the document.

GraphQL document