So, I'm building an app where I have a backend written in Rails and a client written in Vue with Amplify. My database is MySQL and I'm using AWS AppSync with a GraphQL as data source (pointing to my database).
The AWS Amplify has a framework that allows me to generate the schemas based on the table names and columns with one simple command: amplify api add-graphql-datasource. But because I'm using rails migrations, my database is using Rails conventions: pluralized tables with snake cased columns.
Now, the problem with that is the GraphQL schemas are all ugly and not using the correct conventions (singular names for the types and inputs, with camel cased props). Example:
My backend has the following migration:
class CreatePosts < ActiveRecord::Migration[6.0]
  def change
    create_table :posts do |t|
      t.belongs_to :site, null: false
      t.string :title
      t.string :url
      t.text :body
      t.timestamps
    end
  end
end
And the schema generated for this is:
type posts {
  id: Int!
  site_id: Int!
  title: String
  url: String
  body: String
  created_at: AWSDateTime!
  updated_at: AWSDateTime!
}
type Query {
  getPosts(id: Int!): posts
  listPostss: [posts]
  // ...
}
schema {
  query: Query
  // ...
}
Not to mention this:
input CreatepostsInput {
  id: Int!
  site_id: Int!
  title: String
  url: String
  body: String
  created_at: AWSDateTime!
  updated_at: AWSDateTime!
}
So, AWS Amplify is new, it's not mature as Rails, and on top of that I didn't find any adapter or transformer to handle the problem in the client... my hope is to find a way to handle it on Rails.
I need to be able to completely change the Rails conventions without breaking anything: migrations, associations, how to manage associations (create_xxx, build_xxx).
This app is really new, so I can recreate all the migrations from scratch.
Thanks
I can see some things you can do:
Tables:
In your migration, you can change the table name, but now you need to let your model know what's the table name with self.table_name.
# migration
class CreatePosts < ActiveRecord::Migration[6.0]
  def change
    create_table :post do |t| # singular 'post'
      ...
    end
  end
end
#model
class Post
  self.table_name = "post" # i think you can also use a symbol :post
end
Attributes:
You need to avoid using Rails migration methods that follow Rails conventions like t.belongs_to or t.references or t.timestamps.
# migration
class CreatePosts < ActiveRecord::Migration[6.0]
  def change
    create_table :post do |t|
      # do not use this below
      # t.belongs_to :site, null: false
      t.bigint :siteId, null: false
      t.string :title
      t.string :url
      t.text :body
      # do not use this below
      # t.timestamps
      t.datetime :createdAt, default: ->{'CURRENT_TIMESTAMP'}
      t.datetime :updatedAt, default: ->{'CURRENT_TIMESTAMP'}
    end
  end
end
Relationships:
You also need to update your relationships in your models
class Post
  belongs_to :site, foreign_key: 'siteId'
end
More information can be found in the Rails API. Make sure you check the documenation for other relationship methods.
Timestamps:
Since timestamps columns (created_at, updated_at) are not the ones expected by ActiveRecord anymore, you might need to override the ActiveRecord::Timestamp module to have them continue working as you would expect. One of the easiest option is to update your ApplicationRecord or a single model class with the following:
class ApplicationRecord # or class Post
  before_create :set_timestamps
  before_save :set_timestamps
  private
  def set_timestamps
    self.createdAt = DateTime.current if self.new_record?
    self.updatedAt = DateTime.now
  end
end
Or this other option taken from https://stackoverflow.com/a/52276865/1845602
class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true
  class << self
    private
    def timestamp_attributes_for_create
      super << 'my_created_at_column'
    end
    def timestamp_attributes_for_update
      super << 'my_updated_at_column'
    end
  end
end
Auto generated Inputs:
I might be wrong here, but it seems the table name is being injected into the input name like Create<table>Input, so if that's the case, you can name your table Post instead of post.
input CreatepostsInput {
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With