Nuxt.js×Axios×RepositoryFactoryのオレオレベストプラクティス

# はじめに

今回はNuxtのAPI設計をRepositoryFactoryパターンを適応して実装して
昔と比べてかなりスッキリした設計にできたかなと満足できたので、そちらの構成の紹介をしていこうと思います。

## ディレクトリ設計

```
project
├ src
|　 ├ repositories
│ 　│　 ├ user-repository.js
│ 　│ 　└ post-repository.js
│　 ├ factories
│ 　│ 　└ api-repository-factory.js 
│　 ├ store
│ 　│ 　└ post.js 
│ 　└ plugins
│      ├ axios.js
│　　　 └ api.js
│ 　  　
└ nuxt.config.js
```

## Repositoryの作成
まず大本となるRepositoryをVue.jsなどであれば作成するのですが、
Nuxtの場合は`@nuxtjs/axios`を使いたいということと
`nuxt.config.js`でAxiosの設定ができるので今回はこちらに書いていきます。

```js:nuxt.config.json
export default {
  env: {
    baseUrl: process.env.BASE_URL || 'http://localhost:3000/',
  },
  srcDir: './src',
  modules: [
    // Doc: https://axios.nuxtjs.org/usage
    '@nuxtjs/axios',
  ],
  /*
   ** Axios module configuration
   ** See https://axios.nuxtjs.org/options
   */
  axios: {
    baseURL: process.env.baseUrl,
    timeout: 3000,
  },
```

参考までにVueの場合はこんな感じ

```js:src/repositories/Repository.js
import axios from 'axios';

const baseURL = process.env.baseUrl;

export const Axios = {
  createAxios(auth) {
    const options = {
      baseURL,
      timeout: 3000,
    };
    if (auth) {
      options.headers = {};
      options.headers.Authorization = `Bearer ${auth}`;
    }
    return axios.create(options);
  },
};
```


## リソースごとのRepositoryを作成する

次にリソースごとにRepositoryを作成し、axiosのインスタンスを返してあげるようなメソッドを作成してあげます。
新しくAPIを叩きたい場合はどんどんここに追加していくイメージです。

```js:src/repositories/user-repository.js

const resourse = 'api/users';

export default ($axios) => ({
  findAll() {
    return $axios.$get(`${resourse}`);
  },
  findByPk($id) {
    return $axios.$get(`${resourse}/${id}`);
  },
});
```

```js:src/repositories/post-repository.js

const resourse = 'api/posts';

export default ($axios) => ({
  get(params) {
    return $axios.$get(`${resourse}`, {
      params: {
        ...params,
      },
    });
  },
  store(payload, authToken) {
    return $axios.$post(`${resourse}/`, {
      headers: {
        Authorization: `Bearer ${authToken}`,
      }
    }, payload);
  },
  update(id, payload, authToken) {
    return $axios.$post(`${resourse}/${id}`,{
      headers: {
        Authorization: `Bearer ${authToken}`,
      }
    }, payload);
  }
});
```

## Factory の作成
使用するRepositoryをrepositoriesに格納していき、
getでrepositoryが取得できるようにしています。

```js:src/factories/api-repository-factory.js
import UserRepository from '@/repositories/api/user-repository';
import PostRepository from '@/repositories/api/post-repository';

const repositories = {
  user: UserRepository,
  post: PostRepository,
};

export const ApiRepositoryFactory = {
  get: (name) => repositories[name],
};

```

## Vueインスタンスへのコンテキストの注入
repositoryには通常の`axios`ではなく、`$axios`を渡して使いたいというのと
`this.$repositories`から呼び出して使えると便利なので
pluginsでinjectしてあげます。


```js:src/plugins/api.js
import { ApiRepositoryFactory } from '@/factories/ApiRepositoryFactory';

export default ({ app }, inject) => {
  const repositories = (name) => {
    return ApiRepositoryFactory.get(name)(app.$axios);
  };
  inject('repositories', repositories);
};

```

nuxt.config.jsにpluginsを追加

```js:nuxt.config.js
  plugins: [
    { src: '~/plugins/api.js' },
  ],
```

以上でAPI呼び出しの準備は完了です。

## storeから使用してみる

```js:store/post.js
export const actions = {
  async fetchPosts({ commit }, query) {
    const posts = await this.$repositories('post').get(query);
    commit('setPosts', posts);
  },
  async sendPost({ commit, rootState }, post) {
    const payload = { ...post };
    const posts = await this.$repositories('post').post(payload, rootState.auth.token);
    commit('setPosts', posts);
  },
}
```

## AxiosのonRequestを使いこなす

現状だと認証が必要なAPIを使用するたびにAuth用のtokenを送っていて、Repository側でも毎回その記述をしなければいけないので辛いです。
なのでAxiosのonRequestを使って共通化してあげます。

```js:src/plugins/axios.js
export default function ({ $axios, store }) {
  $axios.onRequest((config) => {
    const token = store.auth.token;
    if (token) {
      config.headers.common.Authorization = `Bearer ${token}`;
    }
  });
}
```
nuxt.config.jsにpluginsを追加

```js:nuxt.config.js
  plugins: [
    { src: '~/plugins/api.js' },
    { src: '~/plugins/axios.js' },
  ],
```

いらない実装を消していきます。

```src/repositories/post-repository.js

const resourse = 'api/posts';

export default ($axios) => ({
  get(params) {
    return $axios.$get(`${resourse}`, {
      params: {
        ...params,
      },
    });
  },
  store(payload) {
    return $axios.$post(`${resourse}/`, payload);
  },
  update(id, payload) {
    return $axios.$post(`${resourse}/${id}`, payload);
  }
});
```

```js:store/post.js
export const actions = {
  async fetchPosts({ commit }, query) {
    const posts = await this.$repositories('post').get(query);
    commit('setPosts', posts);
  },
  async sendPost({ commit }, post) {
    const payload = { ...post };
    const post = await this.$repositories('post').post(payload);
    commit('addPost', post);
  },
}
```

これでだいぶスッキリしました。


## 最後に
以前何も考えずにAPIを利用していた時は、↓のようにひたすら書いていたのですが、APIのエンドポイントを変更した場合や、rootStateからtokenを引っ張ってこないといけなかったり色々辛かったです。

```js:sample
  async fetchPost({ commit }) {
    const posts = await axios.get(`${baseUrl}/api/posts/`);
    commit('setPosts', posts);
  },
  async sendPost({ commit, rootState }, post) {
    const result = await axios.post(`${baseUrl}/api/posts/`, {
      headers: {
        Authorization: `Bearer ${rootState.auth.token}`,
      },
      post
    });
    commit('addPost', result);
  },
```
今回RepositoryFactoryパターンを使用してみることで、だいぶ保守しやすいコードになったのではと思います。
この機会に是非RepositoryFactoryパターンを採用してみてください

ここはイケテナイ！ダメ！もっといい方ある！等あれば優しく突っ込んでください！！！

最後にAPI設計に悩み続けて色々探した結果
この設計に出会った初めての記事を共有しておきます。

##参考
https://qiita.com/07JP27/items/0923cbe3b6435c19d761