DockerでNuxt.jsとExpressの環境構築を行うよ

## はじめに
今回はNuxt.jsとExpressの環境をDockerで作成した記事です。

## 環境
今回は、Nuxt.jsに`TypeScript`を導入し、`Composition-API`の書き方を採用します。
また、バックエンドにはORMとして`Prisma`を採用します。

Prismaの導入に関してものすごく手間取りました。
Qiitaで質問にお答えいただいた2名の方に感謝申し上げます。

https://qiita.com/Ryo9597/questions/17fbcac39569b580e677

### Dockerfile
<ul>
<li>MySQL (5.7)</li>
<li>Node.js （【Express】14.15.3-alpine）</li>
<li>Node.js （【Nuxt.js】14.15.3-alpine）</li>
</ul>

### Frontend
採用する技術は以下の通りです。

<ul>
<li>Nuxt.js</li>
<li>TypeScript</li>
<li>Vue3.0（Composition-API）</li>
</ul>

Composition-APIに関して全く知識がない方は以下の記事をご覧ください。

https://qiita.com/Ryo9597/items/6e3d31bddcfb60cd3d8c

### Backend
採用する技術は以下の通りです。

<ul>
<li>Express</li>
<li>TypeScript</li>
<li>Prisma</li>
</ul>

## Github
コードはこちらです。

https://github.com/ssk9597/Docker-Nuxt-TypeScript-Express-Prisma

READMEに環境構築の方法を書いてますが、Qiitaにまとめてみます。

## それでは環境を作りましょう〜！
### ①クローンしてディレクトリを移動する
```terminal:ターミナル
$ git clone git@github.com:ssk9597/Docker-Nuxt-TypeScript-Express-Prisma.git

$ cd Docker-Nuxt-TypeScript-Express-Prisma
```

### ②作成するテーブルを記載する
`api/prisma/schema.prisma`に現在デフォルトで以下が記載されています。

```:api/prisma/schema.prisma
model User {
  id    Int    @id @default(autoincrement())
  name  String
  email String @unique
}
```

上記の意味は、以下の通りです
①`User`テーブルを作成
②カラムは3つ
③`id`というカラムは数値型で、オートインクリメントになっている
④`name`というカラムは文字列型になっている
⑤`email`というカラムは文字列型でユニークになっている

リレーションなどもできるので詳しくは公式サイトを確認しましょう。

https://www.prisma.io/docs/concepts/components/prisma-schema/data-model

### ③`make nuxt`を実行して、`Nuxt.js`の作成とDockerの起動を行う
`Nuxt.js`の作成では、必ず`TypeScript`を選択してください。

```terminal:ターミナル
$ make nuxt

> npx create-nuxt-app frontend

create-nuxt-app v3.6.0
✨  Generating Nuxt.js project in frontend
? Project name: nuxt-typescript
? Programming language: TypeScript
? Package manager: Npm
? UI framework: None
? Nuxt.js modules: (Press <space> to select, <a> to toggle all, <i> to invert selection)
? Linting tools: (Press <space> to select, <a> to toggle all, <i> to invert selection)
? Testing framework: None
? Rendering mode: Universal (SSR / SSG)
? Deployment target: Server (Node.js hosting)
? Development tools: (Press <space> to select, <a> to toggle all, <i> to invert selection)
? What is your GitHub username? XXXXX
? Version control system: Git
```

### ④`nuxt.config.js`と`.env`を修正する
#### `nuxt.config.js`
```javascript:frontend/nuxt.config.js
require('dotenv').config();
const { API_URL, API_URL_BROWSER } = process.env;

export default {
  head: {
    title: 'frontend',
    htmlAttrs: {
      lang: 'ja',
    },
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      { hid: 'description', name: 'description', content: '' },
    ],
    link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }],
  },

  css: [],

  plugins: [],

  components: true,

  buildModules: ['@nuxt/typescript-build'],

  watchers: {
    webpack: {
      poll: true,
    },
  },

  modules: ['@nuxtjs/axios', '@nuxtjs/proxy', '@nuxtjs/dotenv'],

  env: {
    API_URL,
    API_URL_BROWSER,
  },

  proxy: {
    '/api': process.env.API_URL,
  },

  axios: {
    baseURL: process.env.API_URL,
    browserBaseURL: process.env.API_URL_BROWSER,
  },

  build: {},
};
```

#### `.env`

```:frontend/.env
API_URL = "http://app:18080"
API_URL_BROWSER = "http://localhost:18080"
```

### ⑤`make migrate`で`Prisma`のマイグレーションを行う
```terminal:ターミナル
$ make migrate
```

こちらの実行でマイグレーションとGUIでデータベース上のデータを閲覧・編集できる`Prisma studio`が使用できるようになります。

![スクリーンショット 2021-08-29 13.51.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/090208d1-35dc-337b-a4d2-9b5eb050c72c.png)

これがめちゃんこ便利です！

### ⑥`make typescript`でTypeScriptを導入する
まずはターミナルで必要なパッケージをインストールしましょう。

```terminal:ターミナル
$ make typescript
```

次に必要なファイルに追記を行います。

#### shims-vue.d.ts
```typescript:frontend/shims-vue.d.ts
declare module '*.vue' {
  import Vue from 'vue';
  export default Vue;
}
```

#### tsconfig.json
```json:frontend/tsconfig.json
{
  "compilerOptions": {
    "target": "ES2018",
    "module": "ESNext",
    "moduleResolution": "Node",
    "lib": ["ESNext", "ESNext.AsyncIterable", "DOM"],
    "esModuleInterop": true,
    "allowJs": true,
    "sourceMap": true,
    "strict": true,
    "noEmit": true,
    "experimentalDecorators": true,
    "baseUrl": ".",
    "paths": {
      "~/*": ["./*"],
      "@/*": ["./*"]
    },
    "types": ["@nuxt/types", "@types/node"]
  },
  "files": ["shims-vue.d.ts"],
  "include": [
    "components/**/*.ts",
    "components/**/*.vue",
    "layouts/**/*.ts",
    "layouts/**/*.vue",
    "pages/**/*.ts",
    "pages/**/*.vue"
  ],
  "exclude": ["node_modules", ".nuxt", "dist"]
}
```

これで`TypeScript`が使えます。

### ⑦`make composition-api`でComposition-APIを導入する
まずはターミナルで必要なパッケージをインストールしましょう。

```terminal:ターミナル
$ make composition-api
```

次に必要なファイルに追記を行います。

#### nuxt.config.js
```javascript:frontend/nuxt.config.js
require('dotenv').config();
const { API_URL, API_URL_BROWSER } = process.env;

export default {
  head: {
    title: 'frontend',
    htmlAttrs: {
      lang: 'ja',
    },
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      { hid: 'description', name: 'description', content: '' },
    ],
    link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }],
  },

  css: [],

  plugins: [],

  components: true,

  buildModules: ['@nuxt/typescript-build', '@nuxtjs/composition-api/module'],

  generate: {
    interval: 2000,
  },

  watchers: {
    webpack: {
      poll: true,
    },
  },

  modules: ['@nuxtjs/axios', '@nuxtjs/proxy', '@nuxtjs/dotenv'],

  env: {
    API_URL,
    API_URL_BROWSER,
  },

  proxy: {
    '/api': process.env.API_URL,
  },

  axios: {
    baseURL: process.env.API_URL,
    browserBaseURL: process.env.API_URL_BROWSER,
  },

  build: {},
};
```

### ⑧`pages/index.vue`を修正してバックエンドとの通信を図る
では、バックエンドとの通信ができているか確認しましょう。

```vue:frontend/pages/index.vue
<template>
  <div>
    <h1>
      {{ data }}
    </h1>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref, useAsync, useContext } from '@nuxtjs/composition-api';
import axios from '@nuxtjs/axios';

export default defineComponent({
  setup() {
    const data = ref({});
    const { $axios } = useContext();
    useAsync(async () => {
      const result = await $axios.$get('/api');
      data.value = result;
    });
    return { data };
  },
});
</script>
```

このように表示されていれば問題なく通信ができています！

![スクリーンショット 2021-08-29 14.04.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838372/aa1ddd9c-5515-91fa-34e9-bb892d9cb760.png)

また、Prismaのクエリに関してはこちらを確認してください。
基本のCRUDに関してはまとめてあるのでこれを使いこなせれば問題ないと思います。

https://www.prisma.io/docs/concepts/components/prisma-client/relation-queries

## ここからは任意です
UI開発用ツールのStorybookを使いたい方は以下も行うようにしましょう。

ちなみに、Storybookが何か分からない方は以下の記事を確認されてください。

https://qiita.com/Ryo9597/items/3dc7963407ec772c134b

コマンド1つでStorybookが実行できます。

```terminal:ターミナル
$ make storybook

// 再起動したいとき
$ make re-storybook
```

## 終わりに
ここまでできればNuxt.jsとExpressの環境構築はばっちりです👍
あとは作りたいものを作っていきましょう！