I tried writing CRUD with Rails + Vue + devise_token_auth

Introduction

I wanted to develop the front-end server and the back-end server separately. I wrote CRUD processing with login function in Vue.js and Rails (API mode).

environment

Ruby: 2.6.5 Ruby on Rails 6.0.3

setup

Rails project creation

rails _6.0.3_ new devise_token_auth_api --api -d postgresql

cd devise_token_auth_api

rails db:create

Gemfile

gem "devise"
gem "devise_token_auth"
gem "rack-cors"

devise_token_auth installation

bundle install

rails g devise:install

rails g devise_token_auth:install User auth

config/initializers/cors.rb


Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins 'localhost:8080'

    resource '*',
      headers: :any,
      expose: ['access-token', 'expiry', 'token-type', 'uid', 'client'],
      methods: [:get, :post, :put, :patch, :delete, :options, :head]
  end
end

config/initializers/devise_token_auth.rb


DeviseTokenAuth.setup do |config|
  config.change_headers_on_each_request = false
  config.token_lifespan = 2.weeks
  config.token_cost = Rails.env.test? ? 4 : 10
end

User model

app/models/user.rb


class User < ActiveRecord::Base
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable
  include DeviseTokenAuth::Concerns::User
end
rails db:migrate

routing

config/routes.rb


Rails.application.routes.draw do
  namespace :v1 do
    mount_devise_token_auth_for "User", at: "auth"
  end
end

API operation check

This time I will use Postman. Please refer to the following URL for how to use Devise_token_auth. https://devise-token-auth.gitbook.io/devise-token-auth/usage

Sign up

HTTP method: POST URL: http://localhost:3000/v1/auth/ Body: email, password スクリーンショット 2021-01-10 10.59.15.png

Sign in

HTTP method: POST URL: http://localhost:3000/v1/auth/sign_in Body: email, password スクリーンショット 2021-01-10 11.05.43.png

Sign out

HTTP method: DELETE URL: http://localhost:3000/v1/auth/sign_in Body: uid, access-token, client

During the sign-in process, you can see the required parameters in the Headers tab. スクリーンショット 2021-01-10 11.14.54.png

Vue project creation

vue create frontend
*Default ([Vue 2] babel, eslint)choose

cd frontend
yarn add axios

cd ..

Start command

cd frontend
yarn serve

Screen creation

frontend/src/App.vue


<template>
  <div id="app">
    <h3>Post to the bulletin board</h3>
    <div v-if="client === ''">
      <div>
        <h1>SignUp</h1>
        <label for="email">email</label>
        <input id="email" type="email" v-model="email" />
        <label for="password">password</label>
        <input id="password" type="password" v-model="password" />
        <button @click="signup">sign up</button>
      </div>
      <div>
        <h1>SignIn</h1>
        <label for="email">email</label>
        <input id="email" type="email" v-model="email" />
        <label for="password">password</label>
        <input id="password" type="password" v-model="password" />
        <button @click="signin">SignIn</button>
      </div>
    </div>
  </div>
</template>

<script>
import axios from "axios";
export default {
  data() {
    return {
      name: "",
      email: "",
      password: "",
      uid: "",
      access_token: "",
      client: "",
      title: "",
      content: "",
      tasks: [],
      comment: "",
      posts: [],
    };
  },
  methods: {
    signup() {
      axios
        .post("http://localhost:3000/v1/auth", {
          email: this.email,
          password: this.password,
        })
        .then((response) => {
          localStorage.setItem(
            "access-token",
            response.headers["access-token"]
          );
          localStorage.setItem("client", response.headers["client"]);
          localStorage.setItem("uid", response.headers["uid"]);
          this.access_token = response.headers["access-token"];
          this.client = response.headers["client"];
          this.uid = response.headers["uid"];

        });
    },
    signin() {
      console.log(this.email);
      console.log(this.password);
      axios
        .post("http://localhost:3000/v1/auth/sign_in", {
          email: this.email,
          password: this.password,
        })
        .then((response) => {
          console.log(response);
          localStorage.setItem(
            "access-token",
            response.headers["access-token"]
          );
          localStorage.setItem("client", response.headers["client"]);
          localStorage.setItem("uid", response.headers["uid"]);
          this.access_token = response.headers["access-token"];
          this.client = response.headers["client"];
          this.uid = response.headers["uid"];

        });
    },
    signout() {
      console.log(this.uid);
      console.log(this.access_token);
      console.log(this.client);
      axios
        .delete("http://localhost:3000/v1/auth/sign_out", {
          test: { test: "test" },
          headers: {
            uid: this.uid,
            "access-token": this.access_token,
            client: this.client,
          },
        })
        .then((response) => {
          console.log(response);
          this.access_token = "";
          this.client = "";
          this.uid = "";
          localStorage.removeItem("uid");
          localStorage.removeItem("access-token");
          localStorage.removeItem("client");
        });
    },
  },
};
</script>
<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

Sign In screen

スクリーンショット 2021-01-10 11.24.18.png

Added task function

rails g controller v1/tasks
rails g model Task

Add model

db/migrate/YYYYMMDD_create_tasks.rb


class CreateTasks < ActiveRecord::Migration[6.0]
  def change
    create_table :tasks do |t|
      t.string :title
      t.text :content
      t.references :user
      t.timestamps
    end
  end
end
rails db:migrate

routing

config/routes.rb


Rails.application.routes.draw do
  namespace :v1 do
    resources :tasks
    mount_devise_token_auth_for 'User', at: 'auth'
  end
end

controller

app/controllers/v1/tasks_controller.rb


class V1::TasksController < ApplicationController
  
  before_action :set_task, only: [:show]
  before_action :authenticate_v1_user!
  def index
    tasks = Task.where(user_id: @current_v1_user.id)
    render json: tasks
  end

  def create
    user = User.find_by(email: params[:uid])
    task = Task.new(title: params[:title], content: params[:content], user_id: user.id)
    task.save
  end

  def show
    render json: @task
  end

  def update
  end

  def destroy
    task = Task.find(params[:id])
    task.destroy
    render json: true
  end

  private
  def set_task
    @task = Task.find(params[:id])
  end

end

frontend/src/App.vue


<template>
  <div id="app">
    <div v-if="client === ''">
      <div>
        <h1>SignUp</h1>
        <label for="email">email</label>
        <input id="email" type="email" v-model="email" />
        <label for="password">password</label>
        <input id="password" type="password" v-model="password" />
        <button @click="signup">SignUp</button>
      </div>
      <div>
        <h1>SignIn</h1>
        <label for="email">email</label>
        <input id="email" type="email" v-model="email" />
        <label for="password">password</label>
        <input id="password" type="password" v-model="password" />
        <button @click="signin">SignIn</button>
      </div>
    </div>
    <div v-if="client !== ''">
      <div>
        <h1>Task</h1>
        <button @click="signout">SignOut</button>
        <div v-for="task in tasks" :key="task.id">
          Task:{{ task.id }}, {{ task.title }}, {{ task.content }}
          <button @click="find_task(task.id)">task_find</button>
          <button @click="delete_task(task.id)">Delete</button>
        </div>
      </div>
      <div>
        <h3>Task</h3>
        <label for="task">task</label>
        <input id="task" type="text" v-model="title" />
        <label for="content">content</label>
        <input id="content" type="text" v-model="content" />
        <button @click="create_task">Create_Task</button>
      </div>
    </div>
  </div>
</template>

<script>
import axios from "axios";
export default {
  data() {
    return {
      name: "",
      email: "",
      password: "",
      uid: "",
      access_token: "",
      client: "",
      title: "",
      content: "",
      tasks: [],
      comment: "",
      posts: [],
    };
  },
  methods: {
    signup() {
      axios
        .post("http://localhost:3000/v1/auth", {
          email: this.email,
          password: this.password,
        })
        .then((response) => {
          localStorage.setItem(
            "access-token",
            response.headers["access-token"]
          );
          localStorage.setItem("client", response.headers["client"]);
          localStorage.setItem("uid", response.headers["uid"]);
          this.access_token = response.headers["access-token"];
          this.client = response.headers["client"];
          this.uid = response.headers["uid"];

          this.all_tasks();
        });
    },
    signin() {
      console.log(this.email);
      console.log(this.password);
      axios
        .post("http://localhost:3000/v1/auth/sign_in", {
          email: this.email,
          password: this.password,
        })
        .then((response) => {
          console.log(response);
          localStorage.setItem(
            "access-token",
            response.headers["access-token"]
          );
          localStorage.setItem("client", response.headers["client"]);
          localStorage.setItem("uid", response.headers["uid"]);
          this.access_token = response.headers["access-token"];
          this.client = response.headers["client"];
          this.uid = response.headers["uid"];

          this.all_tasks();
        });
    },
    signout() {
      console.log(this.uid);
      console.log(this.access_token);
      console.log(this.client);
      axios
        .delete("http://localhost:3000/v1/auth/sign_out", {
          test: { test: "test" },
          headers: {
            uid: this.uid,
            "access-token": this.access_token,
            client: this.client,
          },
        })
        .then((response) => {
          console.log(response);
          this.access_token = "";
          this.client = "";
          this.uid = "";
          localStorage.removeItem("uid");
          localStorage.removeItem("access-token");
          localStorage.removeItem("client");
        });
      this.tasks = [];
    },
    all_tasks() {
      axios
        .get("http://localhost:3000/v1/tasks", {
          headers: {
            uid: this.uid,
            "access-token": this.access_token,
            client: this.client,
          },
        })
        .then((response) => {
          console.log(response.data);
          this.tasks = response.data;
        });
    },
    find_task(task_id) {
      axios
        .get(`http://localhost:3000/v1/tasks/${task_id}`, {
          headers: {
            uid: this.uid,
            "access-token": this.access_token,
            client: this.client,
          },
        })
        .then((response) => {
          console.log(response);
          this.task = response.data;
        });
    },
    create_task() {
      console.log(this.uid);
      console.log(this.access_token);
      console.log(this.client);
      axios
        .post("http://localhost:3000/v1/tasks", {
          uid: this.uid,
          "access-token": this.access_token,
          client: this.client,
          title: this.title,
          content: this.content,
        })
        .then((response) => {
          console.log(response);
          this.all_tasks();
        });
    },
    delete_task(task_id) {
      axios
        .delete(`http://localhost:3000/v1/tasks/${task_id}`, {
          headers: {
            uid: this.uid,
            "access-token": this.access_token,
            client: this.client,
          },
        })
        .then((response) => {
          console.log(response);
          this.all_tasks();
        });
    },
  },
};
</script>
<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

Task screen

スクリーンショット 2021-01-10 11.51.00.png GitHub https://github.com/yodev21/devise_token_auth_app

At the end

Try implementing CRUD with login function when front end and back end are separated There were many addictive points and it was an opportunity to learn.

There are still many improvements and things I would like to try, so I would like to focus on the following in the future. ・ Data acquisition function across multiple tables ・ Introduction of Vue Router ・ Introduction of Vuex

Reference article

[Rails] use devise token auth devise-token-auth

Recommended Posts

I tried writing CRUD with Rails + Vue + devise_token_auth
[Rails] I tried playing with the comment send button
[Vue Rails] "Hello Vue!" Displayed with Vue + Rails
I tried Rails beginner [Chapter 1]
I tried Rails beginner [Chapter 2]
I tried UPSERT with PostgreSQL.
I tried BIND with Docker
I tried installing Ruby on Rails related plugin with vim-plug
[Rails] I tried to create a mini app with FullCalendar
I tried automatic deployment with CircleCI + Capistrano + AWS (EC2) + Rails
[Rails] I tried to implement batch processing with Rake task
I tried using JOOQ with Gradle
I tried morphological analysis with MeCab
I tried to interact with Java
I tried UDP communication with Java
[Rails] I tried deleting the application
I tried GraphQL with Spring Boot
I tried Flyway with Spring Boot
I tried customizing slim with Scaffold
I tried to implement the image preview function with Rails / jQuery
I tried to make a group function (bulletin board) with Rails
I tried to introduce CircleCI 2.0 to Rails app
I tried using Realm with Swift UI
I tried to get started with WebAssembly
I tried using Scalar DL with Docker
I tried using OnlineConverter with SpringBoot + JODConverter
I tried time-saving management learning with Studyplus.
I tried playing with BottomNavigationView a little ①
I tried using OpenCV with Java + Tomcat
I tried Lazy Initialization with Spring Boot 2.2.0
I tried to implement ModanShogi with Kinx
I rewrote the Rails tutorial test with RSpec
I tried barcode scanning using Rails + React + QuaggaJS
I tried to verify AdoptOpenJDK 11 (11.0.2) with Docker image
I tried to make Basic authentication with Java
I tried to manage struts configuration with Coggle
[Rails] I tried to raise the Rails version from 5.0 to 5.2
I tried to organize the session in Rails
I also tried WebAssembly with Nim and C
I made blackjack with Ruby (I tried using minitest)
I tried Eclipse MicroProfile OpenAPI with WildFly Swarm
I want to play with Firestore from Rails
Rails beginners tried to get started with RSpec
I made a LINE bot with Rails + heroku
I tried to break a block with java (1)
[Rails] I want to load CSS with webpacker
I made a portfolio with Ruby On Rails
I tried Getting Started with Gradle on Heroku
I tried Spring.
I tried tomcat
rails + devise + devise_token_auth
I tried youtubeDataApi.
I tried refactoring ①
I tried FizzBuzz.
I tried JHipster 5.1
I tried what I wanted to try with Stream softly.
I tried to implement file upload with Spring MVC
I tried to read and output CSV with Outsystems
I tried to implement TCP / IP + BIO with JAVA
[Java 11] I tried to execute Java without compiling with javac
I started MySQL 5.7 with docker-compose and tried to connect