Как подключить мультидоменность к приложению на Vue (Nuxt.js 2)

Как подключить мультидоменность к приложению на Vue (Nuxt.js 2)
Просмотров (339)

На одном из проекте, я столкнулся с задачей, где нужно было адаптировать приложение на Vue.js для использования одного и тот же интерфейса, но на разных доменах и с подтягиванием разных данных (SaaS на минималках). Казалось бы, проблема легко решаемая, ведь в Nuxt 3 уже есть хук useRequestURL для таких случаев. Однако проект уже был написан на Vue 2 / Nuxt 2, и переход на новую версию только ради поддержки мультидоменности был нецелесообразен для клиента.

Перейдём сразу к реализации мультидоменности на Vue (Nuxt.js 2), используя только возможности фреймворка. Нам нужно определить домен ещё до первого запроса к API. Я перепробовал несколько подходов, включая использование middleware, но наилучший результат показало получение домена непосредственно при инициализации API.

Домен необходимо обрабатывать в двух режимах:

  • Из заголовков сервера в SSR-режиме.
  • При навигации между страницами через state приложения.

Настройка плагина API

Все запросы к API обрабатываются через единственный скрипт api.js. Это удобно, когда все запросы находятся в одном месте. Советую использовать такой подход и в ваших приложениях.

Подключаем наш api.js в nuxt.config.js как плагин:

plugins: [
    { src: '@/plugins/api.js' },
]
//plugins/api.js

class Api {
    constructor(client, domain, store) {
        // Создаем экземпляр axios с заголовками
        this.instance = client.create({
            headers: {
                'Access-Control-Allow-Origin': true,
            },
        });
        this.store = store;
        
        this.version = "v1";

        // Определяем базовый URL для API
        if (domain !== "") {
            this.domain = `https://${domain}/api/${this.version}`;
        } else {
            this.domain = `/api/${this.version}`;
        }
    }

    // Пример метода
    async getBannersList() {

        let res = {};
        let errorMessageText = '';
        const params = {};

        await this.instance.get(`${this.domain}/banners`, params)
            .then(response => {
                if (response.data.success) {
                    res = response.data.result;
                }
            }).catch(error => {
                console.log(error);
            });

        return res;

    }

}

export default ({ app, store, req }, inject) => {
    let domain = "";

    // Получаем домен из заголовков сервера или из store
    if (typeof req !== 'undefined') {
        domain = req.headers.host;
    } else {
        domain = store.state.domain;
    }

    const api = new Api(app.$axios, domain, store);
    inject('api', api);
};

Настройка Store

Сохраняем в стору домен, будем его получать, при навигации по сайту без перезагрузки страницы (когда не используется SSR):

// store/index.js

export const state = () => ({
    domain: '',
});

export const mutations = {
    SET_DOMAIN(state, domain) {
        state.domain = domain;
    },
};

export const actions = {
    nuxtServerInit({ commit }, context) {
        // Устанавливаем домен при инициализации сервера
        if (context.req && context.req.headers.host) {
            commit('SET_DOMAIN', context.req.headers.host);
        }
    },
};

Использование API в компонентах

Теперь, когда наш API настроен, давайте посмотрим, как использовать его в компонентах:

<script>
export default {
	  async fetch() {
        await this.$api.getBannersList()
        .then(response => {
            this.banners = response;
        });
    }
}
</script>

С клиентской частью разобрались, но также нам нужно настроить и серверную часть приложения. Чтобы наше API работало с разных доменов, нужно дополнительно прописать заголовок Access-Control-Allow-Origin для поддержки CORS. Реализация зависит от того на чём разработано REST API.

Кстати, если вы ещё не знаете, что такое REST API и как его проектируют, то вам обязательно нужно заглянуть вот на эту статью.