Help us understand the problem. What is going on with this article?

Nuxt.js+TypescriptでQiitaAPIを叩いて画面に表示する

More than 1 year has passed since last update.

とある勉強会のネタで使うのでHowToがてら記事にしておこうかとおもいます。

やってみること

  • Node.jsの環境構築

  • qiitaAPIを使う準備

  • Nuxt.jsの導入

  • Nuxt.jsのTypescript設定

  • Nuxt.jsからqiitaAPI叩いて画面作ってみる

Node.jsの環境構築

私はフルスタックなんですが、いろんな言語を使うので、XXenvをまとめて管理をできるanyenvなる便利なものを使ってます。
せっかくなので、それを入れてみます。

# anyenvのsetup
$ git clone https://github.com/anyenv/anyenv ~/.anyenv
$ echo 'export PATH="$HOME/.anyenv/bin:$PATH"' >> ~/.zshrc
$ ~/.anyenv/bin/anyenv init
$ anyenv install --init
$ anyenv install nodenv
$ exec $SHELL -l

# 各envのsetup
$ nodenv install -l
$ nodenv install x.x.x # なるべく最新のを入れる
$ nodenv global x.x.x

# 記事の内容には使わないけど私のメモがてら他のenvも
$ anyenv install goenv
$ anyenv install tfenv
$ anyenv install rbenv
$ anyenv install pyenv
$ exec $SHELL -l

$ goenv install -l
$ goenv install x.x.x
$ goenv global x.x.x

$ tfenv list-remote
$ tfenv install x.x.x
$ tfenv global x.x.x

$ rbenv install -l
$ rbenv install x.x.x
$ rbenv global x.x.x

$ pyenv install -l
$ pyenv install x.x.x
$ pyenv global x.x.x

# 全部のenvのversionが表示されるので便利
$ anyenv versions

qiitaAPIを使う準備

  • qiitaにログインする

  • アクセスTokenをここで発行する

  • APIリファレンスはこちら

Nuxt.jsの導入

# yarnをメインに使うので入れておく
$ brew install yarn 

# nuxtのインストール
$ npx create-nuxt-app nuxt-qiita-example

? Project name # そのままenter
? Project description #そのままenter
? Author name # そのままenter
? Choose the package manager
❯ Yarn # を選択してenter
? Choose UI framework
❯ None # 今回は使わないのでこれを選択してEnter
? Choose custom server framework
❯ Express # を選択してenter(BffAPI作るときに必要なので馴染みのあるのを入れておく)
? Choose Nuxt.js modules
❯◉ Axios # をSpaceキーで選択してenter
? Choose linting tools
 ◉ ESLint
❯◉ Prettier # 定番ツール使いたいのでこの2つをSpaceキーで選択してenter
? Choose test framework
❯ None # 今回は使わないのでこれを選択してEnter
? Choose rendering mode
❯ Universal (SSR) # を選択してenter

公式のInstall

Nuxt.jsのTypescript設定

$ yarn add -D @nuxt/typescript
$ yarn add ts-node
$ yarn add -D @typescript-eslint/eslint-plugin @typescript-eslint/parser
$ touch tsconfig.json
$ yarn run build # nuxtの起動(初回起動時にtsconfig.jsonに初期値設定値が入る)

nuxt.config.jsnuxt.config.tsにリネームして以下になるように書き換える

import NuxtConfiguration from '@nuxt/config'

const config: NuxtConfiguration = {
  mode: 'universal',
  /*
   ** Headers of the page
   */
  head: {
    title: process.env.npm_package_name || '',
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      {
        hid: 'description',
        name: 'description',
        content: process.env.npm_package_description || ''
      }
    ],
    link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }]
  },
  /*
   ** Customize the progress-bar color
   */
  loading: { color: '#fff' },
  /*
   ** Global CSS
   */
  css: [],
  /*
   ** Plugins to load before mounting the App
   */
  plugins: [],
  /*
   ** Nuxt.js dev-modules
   */
  devModules: [
    // Doc: https://github.com/nuxt-community/eslint-module
    '@nuxtjs/eslint-module'
  ],
  /*
   ** Nuxt.js modules
   */
  modules: [
    // Doc: https://axios.nuxtjs.org/usage
    '@nuxtjs/axios'
  ],
  /*
   ** Axios module configuration
   ** See https://axios.nuxtjs.org/options
   */
  axios: {},
  /*
   ** Build configuration
   */
  build: {
    /*
     ** You can extend webpack config here
     */
    extend(config, ctx) {}
  }
}

export default config

.eslintrc.jsをtypescriptに対応させる

module.exports = {
  root: true,
  env: {
    browser: true,
    node: true
  },
  plugins: ['prettier', '@typescript-eslint'],
  parserOptions: {
    parser: '@typescript-eslint/parser'
  },
  extends: [
    '@nuxtjs',
    'plugin:nuxt/recommended',
    'plugin:prettier/recommended',
    'prettier',
    'prettier/vue',
    'prettier/@typescript-eslint'
  ],
  rules: {
    'nuxt/no-cjs-in-config': 'off',
    'no-unused-vars': 'off',
    '@typescript-eslint/no-unused-vars': 'error'
  }
}

公式のTSsupport

ここまでは、公式サポートに書いてあるものの抜粋ですが、Typescriptを使う場合でも通常のJavascriptを使う場合でもcreate-nuxt-appで初回インストールした人は、ここからがハマりどころ。

ハマりどころ その1. Packageが古い!

まず、公式のcreate-nuxt-appでinstallしたnuxt.jsですが、package.jsonをみると

  "dependencies": {
    "@nuxtjs/axios": "^5.3.6",
    "cross-env": "^5.2.0",
    "express": "^4.16.4",
    "nuxt": "^2.0.0",
    "ts-node": "^8.3.0"
  },

あれ、nuxtのversion古い。。。となります。
create-nuxt-appのテンプレートをアップデートしてほしいところではありますが、自分で最新版にあげてきます。

packageのversionを更新するのに
yarn にyarn upgradeInteractiveはありますが、package.jsonは書き換えてくれないので、手で書き換えるのは面倒なのでncuを使えるようにしてversionを上げます。

$ npm install -g npm-check-updates
# ターミナルを立ち上げ直して
$ ncu # 依存パッケージの最新versionを確認
$ ncu -u # 依存パッケージを最新にアップデート
$ yarn install # 最新の依存packageをインストール

ハマりどころ その2. scriptsの定義が古い

生成されたものはこれですが、nuxt-templateのソースコードを読んだところ、選択したフレームワークによって生成されるpackage.jsonの書き方が違うようで、選択したものによっては、nuxtコマンドの使い方が非常に古いです。そうなると初見だとハマりどころ。。。
公式documentのスクラッチから始めるなどをみると正しいコマンド書いてありますが、全部のっていないのとあちこちのdocumentに点在してるので正しくするとこうなります。ついでに対象をtsにしておきます。

修正前

  "scripts": {
    "lint": "eslint --ext .js,.vue --ignore-path .gitignore .",
    "dev": "cross-env NODE_ENV=development nodemon server/index.js --watch server",
    "build": "nuxt build",
    "start": "cross-env NODE_ENV=production node server/index.js",
    "generate": "nuxt generate"
  },

修正後

  "scripts": {
    "lint": "eslint --ext .ts,.vue --ignore-path .gitignore .",
    "dev": "nuxt",
    "build": "nuxt build",
    "start": "nuxt start",
    "generate": "nuxt generate"
  },

ハマりどころ その3. server/index.jsはいらない

その2の修正で、nodemonがなくなってると思いますが最新だとnuxtコマンドがnode-serverを立ててくれるため基本的にはserver/index.jsは不要なので一度消しちゃいます。
server/index.jsを使う場合は、nuxtの
serverMiddlewares
を利用するときですね。

以上で、Nuxt.js+Typescriptの初回環境構築は完了です。

Nuxt.jsからqiitaAPI叩いて画面作ってみる

準備

Typescriptで開発始める前に公式で推奨しているvue-property-decoratorを追加したいと思うのですが、これだとnuxtが用意しているasyncDataが使えません。あとでAPI呼び出しでasyncDataを使うため、nuxt-communityが出してるnuxt-property-decoratorを入れます。

$ yarn add nuxt-property-decorator

最低限必要なもの

Nuxt.jsはコードにルールを持たせるため、ディレクトリ構造も決まっています。
今回画面を作るうえで最低限必要なpagesディレクトリのみ使います。

他ディレクトリの説明は公式のディレクトリ構造を参照してみてください。

pagesは簡単にいうと
https://qiita.com/list
というURLがあったとして、https://qiita.com/
/以降のURLの画面を表しています。

上のURLの場合、

pages
 ┠ list
 ┃  ┗ index.vue  # https://qiita.com/list のページです。
 ┗ index.vue # https://qiita.com/ のdomainの直下のページです。

というディレクトリ構造になり、ディレクトリ名がそのままURLになります。
index.vueというファイル場合にすると、htmlでよくあるindex.htmlと同じようにURL省略できます。
(https://qiita.com/list/index としなくていい。https://qiita.com/listと省略可)

他にも表現方法はありますが、ここでは割愛します。詳しくみたい方は公式の
ルーティングの基礎
を参照してください。

それでは実際にページを作っていきます。
まずpagesの下にlistディレクトリを作り、その下に、index.vueファイルをつくります。

まずはhello world

作ったindex.vueのファイルに以下を記述し画面を表示してみます。

<template>
  <h1 class="red">Hello {{ name }}!</h1>
</template>

<style>
.red {
  color: red;
}
</style>

<script lang="ts">
import { Component, Vue } from 'nuxt-property-decorator'

@Component({})
export default class List extends Vue {
  data() {
    return {
      name: 'World'
    }
  }
}
</script>

Nuxtのサーバーを起動します。

$ yarn run dev

ブラウザでhttp://localhost:3000/listアクセスしてみると、赤文字のHello Worldが表示できたかと思います。

vueファイルについてちょっと解説します。

vueファイルの作りについて

主にこの3つのディレクティブ(タグみたいなところ)からできています。

・template: HTMLを書くところ
・style: CSS書くところ
・script: 画面で使う変数の定義や、ボタンクリック時の処理、API呼び出してデータ取得するなど、いろいろな処理を書きます

vueファイルは画面が主なので、templateは必須ですが、CSSの記載が不要ならstyleは不要ですし、変数やイベントを持たない静的なページについては、scriptも不要です。

dataメソッドについて

dataメソッドの戻り値がそのままtemplateで呼び出せます。サンプルだと
returnで name を返してると思いますが、templateでは{{ name }}で変数の値を表示することができます。dataメソッドは同期呼び出しのみなので、apiから返却された値を使いたい場合は、非同期呼び出しになるのでasyncDataを使います。

APIの呼び出し

APIの呼び出しはasyncDataaxiosを呼び出して利用します。公式だとimportでaxiosを呼び出してますが、すでにnuxtのcontextにaxiosがいるのでそれを利用します。

記事の一覧を呼び出し、returnでAPIのレスポンスを返します。

  async asyncData(context) {
    const res = await context.$axios.get(
      'https://qiita.com/api/v2/items',
      {
        headers: {
          Authorization: 'Bearer 準備で発行した自分のトークン'
        }
      }
    )
    return {
      data: res.data
    }
  }

記事の一覧APIはArrayで返ってくるので、templateで一覧表示をしてみます。
templateはvue.jsの構文をそのまま使えるので
v-forを使って記事一覧のタイトルを出してみます。ついでにstyleも少し整えます。

<template>
  <ul class="list">
    <li v-for="item in data" :key="item.id" class="listItem">
      {{ item.title }}
    </li>
  </ul>
</template>

<style>
.list {
  padding: 0;
}

.listItem {
  list-style: none;
  border-bottom: 1px solid #999;
  padding: 1rem 16px;
  box-sizing: border-box;
}
</style>

APIから取ってきた記事一覧のタイトルが画面に表示されるかと思います。
これで基本的なAPIの呼び出しから画面の表示までの流れができました。

いかがでしたでしょうか?

今回のサンプルはgithubに上げておきます。

次回の記事を書くとしたら、serverMiddlewaresの機能利用して、APIを作って、tokenを隠してみたり、ページングなどを実装してみたりとか書けたらと思います。

tanaka-yui
広く深く行くフルスタックエンジニア。最近はReact、Vueでフロント書き、Terraform でインフラ作りつkotlin とgolang書く何でも屋。座右の銘は「為せば(大抵何とか)成る」
https://twitter.com/yuit1552
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away