HOME  >  Web開発 >  【Nuxt.js + Express】Expressで開発したAPIをNuxt.jsで実行する〜取得

Web開発

【Nuxt.js + Express】Expressで開発したAPIをNuxt.jsで実行する〜取得

【Nuxt.js + Express】Expressで開発したAPIをNuxt.jsで実行する〜取得

目次

  1. Nuxt.jsのプロジェクトを作成する
  2. 全件データを取得し、テーブルに表示する
    1. Expressにcorsを導入する
    2. Expressのポートを変更する。
    3. Nuxt.jsでデータ全件取得APIをたたく
  3. id検索してデータを取得し、テーブルに表示する
  4. まとめ

今回は、前回の記事でExpressで開発したAPIをNuxt.jsにて実行できる、簡易的な名簿アプリケーションを作ってみたいと思います。

今回はNuxtプロジェクトの作成から、データの取得までをご紹介します。

下記が前回までの記事です。まだみていない方は下記から先にご覧ください。

【Node.js + Express】ExpressでREST APIを開発してみる〜概要・取得
https://www.dailyupblog.com/backend_development/1037/

【Node.js + Express】ExpressでREST APIを開発してみる〜登録・更新・削除
https://www.dailyupblog.com/backend_development/1056/

それでは早速いきましょう。

Nuxt.jsのプロジェクトを作成する

まず、Nuxt.jsのプロジェクトを作成していきましょう。

任意のディレクトリに「nuxt-api-test」フォルダを作成し、cdコマンドでこのディレクトリに移動しておきましょう。

そして、下記コマンドでNuxtプロジェクトを作成しましょう。

npx create-nuxt-app

色々と質問が聞かれますが、基本デフォルトで大丈夫ですが、今回Elementを使うので、Elementはインストールしておきましょう。

また、どちらでも大丈夫ですが、言語はTypeScriptにしました。

その後、Nuxtを起動してみましょう。

「nuxt-api-test」フォルダ内で下記を実行してください。

//npmの場合
npm run dev

//yarnの場合
yarn dev

その後、ブラウザで「http://localhost:3000」にアクセスして、下記のように表示されれば成功です。

全件データを取得し、テーブルに表示する

続いては、名簿データベース内のデータを全件取得した上で、テーブルにて表示させてみましょう。

Expressにcorsを導入する

Nuxt.jsの方をいじる前にまずExpressの方でやらなければならないことがあります。

今の状態でNuxt.jsでAPIを叩いてもエラーが出てデータが表示されません。

これは、異なったポート番号間での通信を行うことによって起きるCORS(Cross-Origin Resource Sharing)エラーが原因です。

通常HTTP通信におけるオリジン(データの送信元)は同一でないとアクセスできないという制限が課せられています。(SOP)

複数のドメインにまたがって情報や機能を組み合わせたい場合、この制限を解除する必要が出てきます。

というわけでExpressでもこのcorsを解除するために専用のプラグインを導入しましょう。

前回作ったExpressのディレクトリである「express-test」にcdで移動しましょう。

下記コマンドを実行しましょう。

//npmの場合
npm start cors

//yarnの場合
yarn add cors

インストールが完了したら、app.jsを開いて、下記のように追記してください。

var createError = require("http-errors");
var express = require("express");
var cors = require("cors"); //追記
var path = require("path");
var cookieParser = require("cookie-parser");
var logger = require("morgan");

var indexRouter = require("./routes/index");
var usersRouter = require("./routes/users");
var helloRouter = require("./routes/hello");
var meiboRouter = require("./routes/meibo");

var app = express();

// view engine setup
app.set("views", path.join(__dirname, "views"));
app.set("view engine", "jade");

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("/public", express.static("public"));
app.use(cors()); //追記

app.use("/", indexRouter);
app.use("/users", usersRouter);
app.use("/hello", helloRouter);
app.use("/meibo", meiboRouter);

// catch 404 and forward to error handler
app.use(function (req, res, next) {
  next(createError(404));
});

// error handler
app.use(function (err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get("env") === "development" ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render("error");
});

module.exports = app;

corsをrequireして、ミドルウェアを導入します。

これでcorsの導入は完了です。

Expressのポートを変更する。

今回、Nuxt側のポートが「3000」なので、Express側のポートは「5000」に変更しましょう。

Expressはポートがデフォルトで3000になってしまいます。

「bin」ディレクトリ直下の「www」を開いてください。

15行目あたりの記述を下記のように変更してください

var port = normalizePort(process.env.PORT || "3000");
↓↓↓
var port = normalizePort(process.env.PORT || "5000");

これでポートの変更は完了です。

Nuxt.jsでデータ全件取得APIをたたく

さていよいよNuxtでExpressのAPIを実行してみましょう。

まず、HTTP通信を行うためにaxiosを導入します。

「nuxt-api-test」ディレクトリにcdコマンドで移動しましょう。

そして、下記コマンドでaxiosをインストールしてください。

//npmの場合
npm install axios

//yarnの場合
yarn add axios

まず、「nuxt-api-test」ディレクトリ直下に「api」フォルダを作成してください。

その中にindex.tsを作成して、下記のように記述してください。

import axios from "axios";

const myApi = axios.create({
  baseURL: "http://localhost:5000/meibo",
  responseType: "json"
});

export default {
  async getMeiboData() {
    try {
      const response = await myApi.get("");
      return response.data;
    } catch (error) {
      console.log(error);
    }
  }
};

このindex.tsファイルにAPIを実行する処理をまとめていきます。

axiosをrequireしてください。

import axios from "axios";

下記でaxios.createでリクエストURIと取得するデータの形式を設定しておきます。

const myApi = axios.create({
  baseURL: "http://localhost:5000/meibo",
  responseType: "json"
});

下記でデータ全件取得APIを実行する処理を書きます。

URLは「http://localhost:5000/meibo/」となるので、.get()の引数には「””」を入れます。

そして取得したデータをreturnで返します。

取得したデータはオブジェクトの中で「data」の中に入っているので、「response.data」になります。

  async getMeiboData() {
    try {
      const response = await myApi.get("");
      return response.data;
    } catch (error) {
      console.log(error);
    }
  }

続いて、「pages」フォルダ内のindex.vueを開いてください。

index.vueには下記のように記述してください。

<template>
  <div class="container">
    <h1>名簿アプリケーション</h1>
    <template>
      <el-table :data="tableMeiboData" style="width: 100%">
        <el-table-column prop="id" label="ID" width="180"></el-table-column>
        <el-table-column prop="name" label="Name" width="180"></el-table-column>
        <el-table-column prop="intro" label="Intro"></el-table-column>
      </el-table>
    </template>
  </div>
</template>

<script>
import Vue from "vue";
import meiboApi from "~/api/index";

export default Vue.extend({
  data () {
    return {
      tableMeiboData: []
    };
  },
  methods: {
    async getMeiboData () {
      const res = await meiboApi.getMeiboData();
      this.tableMeiboData = res.data;
    }
  },
  mounted () {
    this.getMeiboData();
  }
});
</script>

<style scoped>
.container {
  max-width: 1200px;
  margin: 0 auto;
}
</style>

ひとつずつ見ていきましょう。

まず、template内ですが、今回、Elementのテーブルを使用します。

<template>
  <div class="container">
    <h1>名簿アプリケーション</h1>
    <template>
      <el-table :data="tableMeiboData" style="width: 100%">
        <el-table-column prop="id" label="ID" width="180"></el-table-column>
        <el-table-column prop="name" label="Name" width="180"></el-table-column>
        <el-table-column prop="intro" label="Intro"></el-table-column>
      </el-table>
    </template>
  </div>
</template>

テーブルに出力するデータは「tableMeiboData」に入れるので、「:data=”tableMeiboData”」で指定しておきましょう。

テーブルに書き出すのは、「id」「name」「intro」なので、カラム3つ分記述します。

続いて、script内です。

下記のように先ほど作った「api」フォルダ内のindex.tsを読み込見ましょう

import meiboApi from "~/api/index";

「tableMeiboData」をdata()の中に記述しておきましょう。

methods内にはapi/index.ts内に記述してあるデータ取得APIを呼び出して、返ってきた値を取得するような関数を記述をします。

最終的に取得したデータは「tableMeiboData」中に格納します。

mountedではこの関数を実行します。

  data () {
    return {
      tableMeiboData: []
    };
  },
  methods: {
    async getMeiboData () {
      const res = await meiboApi.getMeiboData();
      this.tableMeiboData = res.data;
    }
  },
  mounted () {
    this.getMeiboData();
  }

ここまでできたら、完了です。

「express-test」を「npm start」して実行して、「nuxt-api-test」も「yarn dev / npm run dev 」して実行しましょう。

ブラウザで「http://localhost:3000」にアクセスして下記のようにデータベースのデータがテーブルで表示されたら成功です。

id検索してデータを取得し、テーブルに表示する

続いては、idで検索して、データを絞り込みで取得し、テーブルに表示するというのをやっていきます。

idで検索ですね。

それではいきましょう。

まず、今回、id絞り込み検索APIを使うので、「api」フォルダ内のindex.tsに下記のように追記しましょう。

import axios from "axios";

const myApi = axios.create({
  baseURL: "http://localhost:5000/meibo",
  responseType: "json"
});

export default {
  async getMeiboData() {
    try {
      const response = await myApi.get("");
      return response.data;
    } catch (error) {
      console.log(error);
    }
  },
 //追記
  async getIdMeiboData(id: string) {
    try {
      const response = await myApi.get(`/${id}`);
      return response.data;
    } catch (error) {
      console.log(error);
    }
  }
};

getIdMeiboData関数を追記しました。

idのパスパラメーターをつけてid絞り込み検索ができるapiを叩く処理を記述しています。

続きまして、テーブル部分ですが、ここは重複して使うパーツですので、コンポーネント化しましょう。

「components」フォルダ内にTable.vueを作成しましょう。

Table.vueには下記のように記述してください。

<template>
  <el-table :data="getTableData" style="width: 100%">
    <el-table-column prop="id" label="ID" width="180"></el-table-column>
    <el-table-column prop="name" label="Name" width="180"></el-table-column>
    <el-table-column prop="intro" label="Intro"></el-table-column>
  </el-table>
</template>

<script>
import Vue from "vue";

export default Vue.extend({
  props: {
    getTableData: {
      type: Array,
      default: () => { }
    }
  },
  data () {
    return {

    }
  }
});
</script>

propsで取得データを受け取り、el-tableに表示させましょう。

propsのデータのtypeは配列ですので、Arrayになります。

続いて、検索フォームもコンポーネントとして作ります。

「components」フォルダ内にSearchForm.vueを作成しましょう。

SearchForm.vueには下記のように記述しましょう。

<template>
  <el-form :inline="true" :model="form" class="demo-form-inline">
    <el-form-item label="ID検索">
      <el-input v-model="form.id" placeholder="IDを入力してください"></el-input>
    </el-form-item>
    <el-form-item>
      <el-button type="primary" @click="searchData(Path.RESULT)">検索</el-button>
    </el-form-item>
  </el-form>
</template>

<script>
import Vue from "vue";
import { Path } from "~/constants";

export default Vue.extend({
  data () {
    return {
      form: {
        id: ''
      },
      Path: Path
    }
  },
  methods: {
    searchData (path) {
      this.$router.push({ path, query: { id: this.form.id } });
    }
  }
});
</script>

入力フォームはElementのel-formを使います。

今回はidを入力するフォームですので、dataにはformを用意し、その中にidを用意しましょう。

また、検索ボタンを押した際にページ遷移しますので、$router.pushの関数を記述しましょう。

遷移する際に、検索フォーム入力された値をクエリパラメータで渡す記述をしましょう。

さて、次はコンポーネント化したテーブルとフォームを読み込ませるため、一度「pages」フォルダ内のindex.vueを開いてtemplate内の一部を追記及び変更します。

下記のように記述してください。

<template>
  <div class="container">
    <h1>名簿アプリケーション</h1>
    <SearchForm /> //追記
    <Table v-bind:getTableData="tableMeiboData" /> //変更
  </div>
</template>

<script>
import Vue from "vue";
import meiboApi from "~/api/index";

export default Vue.extend({
  data () {
    return {
      tableMeiboData: []
    };
  },
  methods: {
    async getMeiboData () {
      const res = await meiboApi.getMeiboData();
      this.tableMeiboData = res.data;
    }
  },
  mounted () {
    this.getMeiboData();
  }
});
</script>

<style scoped>
.container {
  max-width: 1200px;
  margin: 0 auto;
}
table {
  border-collapse: collapse;
  border-spacing: 0;
  width: 100%;
}
table th,
table td {
  padding: 0.8em;
  border-top: 1px solid #000;
  border-bottom: 1px solid #000;
  text-align: left;
}
</style>

続いて、検索結果のページを作成します。

「pages」フォルダ内にresult.vueを作成してください。

result.vueには下記のように記述してください。

<template>
  <div class="container">
    <h1>名簿アプリケーション</h1>
    <SearchForm />
    <Table v-bind:getTableData="tableMeiboData" />
  </div>
</template>

<script>
import Vue from "vue";
import meiboApi from "~/api/index";

export default Vue.extend({
  data () {
    return {
      tableMeiboData: []
    };
  },
  methods: {
    async getIdMeiboData () {
      const res = await meiboApi.getIdMeiboData(this.$route.query.id);
      this.tableMeiboData = res.data;
    }
  },
  mounted () {
    this.getIdMeiboData();
  },
  watch: {
    '$route' (to, from) {
      this.getIdMeiboData()
    }
  },
});
</script>

<style scoped>
.container {
  max-width: 1200px;
  margin: 0 auto;
}
</style>

methods内で先ほど「api」フォルダ内のindex.tsに追記した関数を呼び出しています。

第二引数にはクエリパラメーターの値を入れています。

ここでポイントなのが、mounted内でgetIdMeiboData関数を呼び出していますが、watch内でも同じようにこの関数を呼び出しています。

これは下記記事でも紹介している動的ルートマッチングの性質によるものです。

【Vue + vue-cli】vue-routerでパラメーターを設定、取得 – 動的ルートマッチングとパラメーター
https://www.dailyupblog.com/web_development/834/#chapter-4

同コンポーネントの再利用の際にライフサイクルフックが呼び出されないのを防ぐため、「watch」でパラメーターの変化を監視してあげます。

そのための記述です。

ここまでできましたら、nuxtを起動して、ブラウザで「http://localhost:3000」にアクセスしてみてください。

もちろんexpressの起動もしておいてください。

すると下記のように検索フォームが表示されます。

試しに「r457kb68yw」と入力して、検索ボタンを押してみてください。

「マイケル」の情報だけが表示されるはずです。

まとめ

今回は前回の記事で作ったExpressのAPIをNuxtで実行してみるといったことをやってみました。

今回は取得だけですが、次回は登録・更新・削除をやってみたいと思います。

それでは今回はここまでです。

お疲れ様でした。

関連記事

関連記事