расширить существующий API с помощью пользовательских конечных точек - PullRequest
12 голосов
/ 30 октября 2019

Я создаю API для нескольких клиентов. Основные конечные точки, такие как /users, используются каждым клиентом, но некоторые конечные точки зависят от индивидуальных настроек. Так что, возможно, Пользователь A хочет иметь специальную конечную точку /groups, и никакой другой клиент не будет иметь этой функции. Так же, как sidenote , каждый клиент также использовал бы свою собственную схему базы данных из-за этих дополнительных функций.

Я лично использую NestJ (Express под капотом). Таким образом, app.module в настоящее время регистрирует все мои основные модули (с их собственными конечными точками и т. Д.)

import { Module } from '@nestjs/common';

import { UsersModule } from './users/users.module'; // core module

@Module({
  imports: [UsersModule]
})
export class AppModule {}

Я думаю, что эта проблема не связана с NestJ, так как бы вы справились с этим в теории?

Мне в основном нужна инфраструктура, способная обеспечить базовую систему. Больше нет конечных точек ядра, потому что каждое расширение уникально и возможно несколько реализаций /users. При разработке новой функции основное приложение не должно касаться. Расширения должны интегрироваться сами или интегрироваться при запуске. Базовая система поставляется без конечных точек, но будет расширена из этих внешних файлов.

Мне приходят в голову некоторые идеи


Первый подход:

Каждое расширение представляет новый репозиторий. Определите путь к пользовательской внешней папке, содержащей все проекты расширений. Этот пользовательский каталог будет содержать папку groups с groups.module

import { Module } from '@nestjs/common';

import { GroupsController } from './groups.controller';

@Module({
  controllers: [GroupsController],
})
export class GroupsModule {}

Мой API может циклически проходить по этому каталогу и пытаться импортировать каждый файл модуля.

  • плюсы:

    1. Пользовательский код хранится вдали от основного хранилища
  • минусы:

    1. NestJs использует Typescript, поэтому я должен сначала скомпилировать код. Как бы я управлял сборкой API и сборками из пользовательских приложений? (Система Plug and Play)

    2. Пользовательские расширения очень свободны, потому что они просто содержат некоторые машинописные файлы. Из-за того, что они не имеют доступа к каталогу API node_modules, мой редактор покажет мне ошибки, потому что он не может разрешить внешние зависимости пакета.

    3. Некоторые расширения могут быть загруженыданные из другого расширения. Возможно, службе групп необходимо получить доступ к службе пользователей. Здесь все может быть сложно.


Второй подход: Держите каждое расширение внутри подпапки папки src API. Но добавьте эту подпапку в файл .gitignore. Теперь вы можете хранить свои расширения внутри API.

  • плюсы:

    1. Ваш редактор может разрешать зависимости

    2. Перед развертыванием вашего кода вы можете запустить команду сборки, и у вас будет один дистрибутив

    3. Вы можете легко получить доступ к другим службам (/groups необходимо найти пользователя поid)

  • минусы:

    1. При разработке вам необходимо скопировать файлы репозитория в эту подпапку. После изменения чего-либо вы должны скопировать эти файлы обратно и переопределить ваши файлы репозитория обновленными.

Третий подход:

Внутри внешней пользовательской папки все расширения являются полноценными автономными API. Ваш основной API просто обеспечит аутентификацию и может выступать в качестве прокси-сервера для перенаправления входящих запросов на целевой API.

  • профи:

    1. Новые расширенияможет быть легко разработан и протестирован
  • минусы:

    1. Развертывание будет сложным. У вас будет основной API и API расширения n , запускающие собственный процесс и прослушивающие порт.

    2. Прокси-система может быть сложной. Если клиент запрашивает /users, прокси-сервер должен знать, какой API-интерфейс расширения прослушивает эту конечную точку, вызывает этот API-интерфейс и перенаправляет этот ответ клиенту.

    3. Чтобы защитить API расширений (аутентификация обрабатывается основным API), прокси должен поделиться секретом с этими API. Таким образом, API расширения будет принимать входящие запросы только в том случае, если соответствующий секретный ключ предоставлен прокси-сервером.


Четвертый подход:

Могут помочь микросервисы. Отсюда я взял руководство https://docs.nestjs.com/microservices/basics

Я мог бы иметь микросервис для управления пользователями, управления группами и т. Д. И использовать эти сервисы, создав небольшой API / шлюз / прокси, который вызывает эти микросервисы.

  • плюсы:

    1. Новые расширения можно легко разрабатывать и тестировать

    2. Отдельные вопросы

  • минусы:

    1. Развертывание будет сложным. У вас будет основной API и n микросервисы, запускающие собственный процесс и прослушивающие порт.

    2. Похоже, мне нужно будет создать новый API шлюза длякаждый клиент, если я хочу, чтобы это настраивалось. Поэтому вместо расширения приложения мне придется каждый раз создавать настраиваемый API-интерфейс. Это не решило бы проблему.

    3. Чтобы защитить API расширений (аутентификация обрабатывается основным API), прокси должен поделиться секретом с этими API. Поэтому API расширения будет принимать входящие запросы только в том случае, если соответствующий секретный ключ будет предоставлен прокси-сервером.

Ответы [ 2 ]

4 голосов
/ 04 ноября 2019

Есть несколько подходов к этому. Что вам нужно сделать, это выяснить, какой рабочий процесс лучше всего подходит для вашей команды, организации и клиентов.

Если бы это зависело от меня, я бы подумал об использовании одного репозитория на модуль и использовании менеджера пакетов, такого как NPM, с частными или организационными пакетами для обработки конфигурации. Затем настройте конвейеры выпуска сборки, которые будут распространяться на репозиторий в новых сборках.

Таким образом, все, что вам нужно, - это основной файл и файл манифеста пакета для пользовательской установки. Вы можете самостоятельно разрабатывать и развертывать новые версии, а также загружать новые версии в случае необходимости на стороне клиента.

Для большей плавности вы можете использовать файл конфигурации, чтобы сопоставить модули с маршрутами и написать общий скрипт генератора маршрутов для выполнения большей части начальной загрузки.

Поскольку пакет может быть любым, перекрестные зависимостивнутри пакетов будет работать без особых хлопот. Вам просто нужно быть дисциплинированным, когда дело доходит до управления изменениями и версиями.

Подробнее о частных пакетах здесь: Частные пакеты NPM

Теперь частные реестры NPM стоят денег,но если это проблема, есть несколько других вариантов. Пожалуйста, просмотрите эту статью, чтобы узнать о некоторых альтернативах - как бесплатных, так и платных.

Способы создания личного реестра npm

Теперь, если вы хотите запустить своего собственного менеджера, вы можетенаписать простой сервисный локатор, который принимает файл конфигурации, содержащий необходимую информацию для извлечения кода из репозитория, загрузки его, а затем предоставляет какой-то метод для извлечения экземпляра в него.

У меня естьнаписал простую эталонную реализацию для такой системы:

Framework: locootion service locator

Пример проверки плагина для палиндромов: Пример плагина Locomotion

Приложение, использующее инфраструктуру для поиска плагинов: Пример приложения locomotion

Вы можете поэкспериментировать с этим, получив его из npm, используя npm install -s locomotion, вам нужно будет указатьфайл plugins.json со следующей схемой:

{
    "path": "relative path where plugins should be stored",
    "plugins": [
        { 
           "module":"name of service", 
           "dir":"location within plugin folder",
           "source":"link to git repository"
        }
    ]
}

пример:

{
    "path": "./plugins",
    "plugins": [
        {
            "module": "palindrome",
            "dir": "locomotion-plugin-example",
            "source": "https://github.com/drcircuit/locomotion-plugin-example.git"
        }
    ]
}

загрузить его так: const loco = require ("locomotion");

Затем он возвращает обещание, которое разрешит объект локатора службы, у которого есть метод локатора для удержания ваших служб:

loco.then((svc) => {
    let pal = svc.locate("palindrome"); //get the palindrome service
    if (pal) {
        console.log("Is: no X in Nixon! a palindrome? ", (pal.isPalindrome("no X in Nixon!")) ? "Yes" : "no"); // test if it works :)
    }
}).catch((err) => {
    console.error(err);
});

Обратите внимание, что это просто ссылкареализация, и не является достаточно надежным для серьезного применения. Тем не менее, шаблон по-прежнему действителен и показывает суть написания такого рода фреймворков.

Теперь его необходимо расширить за счет поддержки конфигурации плагинов, инициализации, проверки ошибок, возможно, добавить поддержку внедрения зависимостей искоро.

2 голосов
/ 04 ноября 2019

Я бы выбрал вариант внешних пакетов.

Вы можете структурировать свое приложение так, чтобы оно имело папку packages. Я хотел бы иметь скомпилированные сборки UMD внешних пакетов в этой папке, чтобы у вашей скомпилированной машинописи не было проблем с пакетами. Все пакеты должны иметь файл index.js в корневой папке каждого пакета.

И ваше приложение может запустить цикл по папке пакетов, используя fs и require все пакеты index.js в вашем приложении.

Опять же, установка зависимостей - это то, о чем вы должны позаботиться. Я думаю, что файл конфигурации в каждом пакете может решить эту проблему. У вас может быть собственный сценарий npm в главном приложении, чтобы установить все зависимости пакетов перед запуском приложения.

Таким образом, вы можете просто добавить новые пакеты в свое приложение, скопировав их в папку пакетов. и перезагрузка приложения. Ваши скомпилированные машинописные файлы не будут затронуты, и вам не придется использовать частный npm для ваших собственных пакетов.

...