はじめに
今回は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の設定ができるので今回はこちらに書いていきます。
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の場合はこんな感じ
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を叩きたい場合はどんどんここに追加していくイメージです。
const resourse = 'api/users';
export default ($axios) => ({
findAll() {
return $axios.$get(`${resourse}`);
},
findByPk($id) {
return $axios.$get(`${resourse}/${id}`);
},
});
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が取得できるようにしています。
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してあげます。
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を追加
plugins: [
{ src: '~/plugins/api.js' },
],
以上でAPI呼び出しの準備は完了です。
storeから使用してみる
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を使って共通化してあげます。
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を追加
plugins: [
{ src: '~/plugins/api.js' },
{ src: '~/plugins/axios.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);
}
});
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を引っ張ってこないといけなかったり色々辛かったです。
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設計に悩み続けて色々探した結果
この設計に出会った初めての記事を共有しておきます。