Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
17
Help us understand the problem. What is going on with this article?
@lucaspoppy

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

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設計に悩み続けて色々探した結果
この設計に出会った初めての記事を共有しておきます。

参考

17
Help us understand the problem. What is going on with this article?
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
lucaspoppy
新卒2年目のWEBエンジニアです。 AWS/Nuxt/Vue/Laravel
lifull
日本最大級の不動産・住宅情報サイト「LIFULL HOME'S」を始め、人々の生活に寄り添う様々な情報サービス事業を展開しています。

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
17
Help us understand the problem. What is going on with this article?