34
30

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Vue(Vuex, axios) + ExpressでRESTful APIに超入門する

Last updated at Posted at 2018-12-15

株式会社diffeasyでフロントエンドエンジニアをやっているカオルコです!
普段、バックエンドの方が用意してくれたデータをひたすら表示するお仕事をしていますが、APIリクエスト投げた向こう側の世界、のぞいてみたくなりませんか・・・!?

というわけで、超シンプルなRESTful APIをJavaScriptで実装して、データの流れを追っかけてみました😎
言葉遣い、用語の使い間違いがあればそっと教えて下さいmm

この記事は diffeasy Advent Calendar 2018 15日目の記事です。

リポジトリ

前提

  • npm(6.4.1)
  • vue/cli(3.1.1)
  • Vue(2.5.17)
  • Vuex
  • axios
  • Express(4.16.0)

▼フォルダ構成

practice-express/
   ├ client/
   │      ├ package.json
   │      └ ...
   └ server/
          ├ package.json
          └ ...

client/配下にVueのソースを、server/配下にExpressのソースを置いていきます。

フロントエンドの構築

インストール

# vue/cliをインストールしていない場合
npm install -g @vue/cli

cd ~/workspace/practice-express
vue create client

# vue createが終了したら
cd client
npm insall
npm install axios --save

# 起動後、`http://localhost:8080/`にアクセス
npm run serve

Hello, Vue🎉

Storeを構成する

practice-express/
   ├ client/
   │      ├ package.json
   │      ├ ...
   │      ├ store/
   │      │     ├ modules/
   │      │     │       ├ test.js
   │      │     │       └ ...
   │      │     ├ index.js
   │      │     └ mutation.js 
   │      └ ...

今回はstore/index.jsにVuexのインスタンス定義をし、store/modules/配下にapiを投げるモジュールを書いていきます。
store/mutation.jsには定数を定義しています。(→ミューテーション・タイプに定数を使用する

リクエスト投げ投げモジュールを作成

client/store/modules/test.js
import axios from 'axios'
const API_URI = 'http://localhost:3000'

import {
  TEST, TEST_FAILURE, TEST_SUCCESS
} from '../mutation'

export default {
  state: {
    test: null
  },
  getters: {
    test: state => {
      return state.test
    }
  },
  actions: {
    [TEST] (params = null) {
      let data = {}
      if (params) data = params.data
      axios.get(API_URI + '/test', {data: data})
      .then(res => {
        if (res.status === 200 || res.status === 304) {
          if (res.data.error) {
            this.commit(TEST_FAILURE, res.data)
          } else {
            this.commit(TEST_SUCCESS, {data: res.data, callback: params.callback})
          }
        }
      })
      .catch(error => {
        throw error
      })
    }
  },
  mutations: {
    [TEST_FAILURE] (state, data) {
      console.log('TEST_FAILURE')
    },
    [TEST_SUCCESS] (state, data) {
      console.log('TEST_SUCCESS')
      state.test = data.data.text
      if (data.callback) {
        data.callback(data.data)
      }
    }
  }
}

これでthis.$store.dispatch('TEST')とコンポーネント内で呼ぶと、http://localhost:3000/testへgetリクエストが発生するようになりました!

バックエンドの構築

インストール

cd ~/workspace/practice-express

mkdir app
cd app

npm init
npm install express-generator -g

# expressをviewエンジンなしでインストール
express --no-view

# 起動後、`http://localhost:3000/`にアクセス
DEBUG=myapp:* npm start

Hello, Express🎉

自動生成されたファイルを眺めてみる

practice-express/
   └ server/
          ├ ...
          ├ package.json
          ├ app.js
          ├ bin/
          │   └ www
          ├ public/
          └ routes/
              └ index.js

bin
環境立ち上げ時に実行される処理を置くところ

public/
静的ファイルを置くところ
http://localhost:3000/はここのindexを表示するみたい

routes/
ルーティング処理を置くところ
クライアントからの要求に対しての処理群

app.js
メインファイル
共通する設定や処理はここに書く

APIを実装してみる

http://localhost:3000/testへGETリクエストが来たら、{"text":"success!"}を返すAPIを作ります。

まずは、ルーティングを作成。

app.js
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');

var indexRouter = require('./routes/index');
var testRouter = require('./routes/test'); // ←追加

var app = express();

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', indexRouter);
app.use('/test', testRouter); // ←追加

module.exports = app;

実際に処理をするファイルを作成

/routes/test.js
var express = require('express')
var router = express.Router()

router.get('/', function(req, res, next) {
  const data = {
    text: 'success!'
  }
  res.send(data)
})

module.exports = router

expressを再起動し、http://localhost:3000/testへアクセスしてみます。
{"text":"success!"}と表示されたら大成功🎉

VueからExpressにリクエストを投げ、レスポンスデータを表示する

フロントエンドとバックエンド、それぞれのピースはできたので、組み合わせてみます。
buttonをクリックするとtextareaにレスポンスデータが表示されるコンポーネントを作成しました。


<template>
  <div>
    <button type="button" @click="click">get</button>
    <textarea v-model="test"></textarea>
  </div>
</template>

<script>
export default {
  name: "HelloWorld",
  computed: {
    test () {
      return this.$store.getters.test
    }
  },
  methods: {
    click () {
      this.$store.dispatch('TEST')
    }
  }
};
</script>

スクリーンショット 2018-12-15 17.37.14.png

スクリーンショット 2018-12-15 17.36.56.png

🙃

CORS(オリジン間リソース共有)

異なるドメインに対してアクセスを行ったとき、許可なくレスポンスデータを読み込むことができない仕組みです。
スキーム・ホスト・ポート番号で評価されます。
(→ https://developer.mozilla.org/ja/docs/Web/HTTP/CORS)

今回はhttp://localhost:8080http://localhost:3000でポートを分けて実装したので怒られたようです。

Express側で、http://localhost:8080からのレスポンスデータ読み込みを許可します。

/server/app.js
// CORSを許可する処理を追加
app.use(function(req, res, next) {
  res.header("Access-Control-Allow-Origin", "http://localhost:8080");
  res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
  next()
})

スクリーンショット 2018-12-15 17.47.15.png

🎉🎉🎉

最後に

マークアップエンジニア出身なこともあり、JavaScript以外の言語に馴染みがないので、APIを作って学んでみるのにExpressはとてもいいなと思いました!(何をやってるか読解するのにストレスがあまりない)
今回はGETのみでしたが、次回はCRUD制覇したり、認証まわりの実装にチャレンジしたりしたいと思います!

Have a nice RESTful!

34
30
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
34
30

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?