25
23

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 3 years have passed since last update.

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

Last updated at Posted at 2020-08-28

はじめに

今回は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の設定ができるので今回はこちらに書いていきます。

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の場合はこんな感じ

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を叩きたい場合はどんどんここに追加していくイメージです。

src/repositories/user-repository.js

const resourse = 'api/users';

export default ($axios) => ({
  findAll() {
    return $axios.$get(`${resourse}`);
  },
  findByPk($id) {
    return $axios.$get(`${resourse}/${id}`);
  },
});
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が取得できるようにしています。

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してあげます。

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を追加

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

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

storeから使用してみる

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を使って共通化してあげます。

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を追加

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);
  }
});
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を引っ張ってこないといけなかったり色々辛かったです。

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

25
23
0

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
25
23

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?