🙆‍♀️

【frourio】Railsとfrourioの比較(Scaffoldingとcreate-frourio-app)

2022/03/12に公開

まとめと感想

全体的にRailsはcontroller~modelの責務が大きかったのに対して、
frourioの方が細かく分離されている印象。

リクエストpath

Rails

routes.rbに記載

config/routes.rb
Rails.application.routes.draw do
  resources :users
  # get '/users', to: 'users#index'
  # get '/users/new', to: 'users#new'
  # post '/users', to: 'users#create'
  # get '/users/:id', to: 'users#show'
  # get '/users/:id/edit', to: 'users#edit'
  # patch '/users/id', to: 'users#update'
  # delete '/users/:id', to: 'users#destroy'
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end

frourio

ファイルpathとindex.tsで表現

ファイルpath
server
└──api
   └──tasks
      ├── $relay.ts
      ├── _taskId@number
      │   ├── $relay.ts
      │   ├── controller.ts
      │   └── index.ts
      ├── controller.ts
      └── index.ts
server/api/tasks/index.ts
import type { Task } from '$prisma/client'

export type Methods = {
  get: {
    query?: {
      limit?: number
      message?: string
    }

    resBody: Task[]
  }
  post: {
    reqBody: Pick<Task, 'label'>
    resBody: Task
  }
}

処理の振り分け

Rails

controllerで行う

app/controllers/users_controller.rb
class UsersController < ApplicationController
  before_action :set_user, only: %i[ show edit update destroy ]

  # GET /users or /users.json
  def index
    @users = User.all
  end

  # GET /users/1 or /users/1.json
  def show
  end

  # GET /users/new
  def new
    @user = User.new
  end

  # GET /users/1/edit
  def edit
  end

  # POST /users or /users.json
  def create
    @user = User.new(user_params)

    respond_to do |format|
      if @user.save
        format.html { redirect_to user_url(@user), notice: "User was successfully created." }
        format.json { render :show, status: :created, location: @user }
      else
        format.html { render :new, status: :unprocessable_entity }
        format.json { render json: @user.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /users/1 or /users/1.json
  def update
    respond_to do |format|
      if @user.update(user_params)
        format.html { redirect_to user_url(@user), notice: "User was successfully updated." }
        format.json { render :show, status: :ok, location: @user }
      else
        format.html { render :edit, status: :unprocessable_entity }
        format.json { render json: @user.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /users/1 or /users/1.json
  def destroy
    @user.destroy

    respond_to do |format|
      format.html { redirect_to users_url, notice: "User was successfully destroyed." }
      format.json { head :no_content }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_user
      @user = User.find(params[:id])
    end

    # Only allow a list of trusted parameters through.
    def user_params
      params.require(:user).permit(:name, :age)
    end
end

frourio

controllerで行う

server/api/tasks/controller.ts
import { defineController } from './$relay'
import { getTasks, createTask } from '$/service/tasks'

const print = (text: string) => console.log(text)

export default defineController({ getTasks, print }, ({ getTasks, print }) => ({
  get: async ({ query }) => {
    if (query?.message) print(query.message)

    return { status: 200, body: await getTasks(query?.limit) }
  },
  post: async ({ body }) => ({
    status: 201,
    body: await createTask(body.label)
  })
}))

DB接続周り

Rails

app/models/user.rb
class User < ApplicationRecord
end

frourio

処理を実行する純粋な関数だけserviceに残して、DB操作は別ディレクトリに分けても良い。

server/service/tasks.ts
import { depend } from 'velona'
import { PrismaClient } from '@prisma/client'
import type { Task, Prisma } from '$prisma/client'

const prisma = new PrismaClient()

export const getTasks = depend(
  { prisma: prisma as { task: { findMany(): Promise<Task[]> } } },
  async ({ prisma }, limit?: number) =>
    (await prisma.task.findMany()).slice(0, limit)
)

export const createTask = (label: Task['label']) =>
  prisma.task.create({ data: { label } })

export const updateTask = (
  id: Task['id'],
  partialTask: Prisma.TaskUpdateInput
) => prisma.task.update({ where: { id }, data: partialTask })

export const deleteTask = (id: Task['id']) =>
  prisma.task.delete({ where: { id } })

Discussion