rozszerz istniejący interfejs API o niestandardowe punkty końcowe

12

Tworzę interfejs API dla wielu klientów. Podstawowe punkty końcowe, takie jak, /userssą używane przez każdego klienta, ale niektóre punkty końcowe zależą od indywidualnego dostosowania. Być może więc użytkownik A chce specjalnego punktu końcowego /groupsi żaden inny klient nie będzie miał tej funkcji. Podobnie jak sidenote , każdy klient używałby również własnego schematu bazy danych z powodu tych dodatkowych funkcji.

Osobiście używam NestJs (Express pod maską). Tak więc app.moduleobecnie rejestruje wszystkie moje podstawowe moduły (z własnymi punktami końcowymi itp)

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

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

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

Myślę, że ten problem nie jest związany z NestJ, więc jak poradziłbyś sobie z tym w teorii?

Zasadniczo potrzebuję infrastruktury, która jest w stanie zapewnić podstawowy system. Nie ma już podstawowych punktów końcowych, ponieważ każde rozszerzenie jest unikalne i /usersmożliwe może być wiele implementacji. Podczas opracowywania nowej funkcji nie należy dotykać podstawowej aplikacji. Rozszerzenia powinny się integrować lub powinny zostać zintegrowane podczas uruchamiania. System podstawowy jest dostarczany bez punktów końcowych, ale zostanie rozszerzony z tych plików zewnętrznych.

Niektóre pomysły przychodzą mi na myśl


Pierwsze podejście:

Każde rozszerzenie reprezentuje nowe repozytorium. Zdefiniuj ścieżkę do niestandardowego folderu zewnętrznego zawierającego wszystkie projekty rozszerzeń. Ten niestandardowy katalog zawierałby folder groupsz plikiemgroups.module

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

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

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

Mój interfejs API może przechodzić przez ten katalog i próbować importować każdy plik modułu.

  • plusy:

    1. Kod niestandardowy jest trzymany z dala od głównego repozytorium
  • Cons:

    1. NestJs używa Typescript, więc najpierw muszę skompilować kod. Jak zarządzałbym kompilacją API i kompilacjami z niestandardowych aplikacji? (System plug and play)

    2. Niestandardowe rozszerzenia są bardzo luźne, ponieważ zawierają tylko niektóre pliki maszynopisu. Ponieważ nie mają dostępu do katalogu node_modules API, mój edytor pokaże mi błędy, ponieważ nie może rozwiązać zależności zewnętrznych pakietów.

    3. Niektóre rozszerzenia mogą pobierać dane z innego rozszerzenia. Może usługa grup musi mieć dostęp do usługi użytkowników. Tutaj może być trudniej.


Drugie podejście: przechowuj każde rozszerzenie w podfolderze folderu src interfejsu API. Ale dodaj ten podfolder do pliku .gitignore. Teraz możesz przechowywać rozszerzenia w interfejsie API.

  • plusy:

    1. Twój edytor jest w stanie rozwiązać zależności

    2. Przed wdrożeniem kodu możesz uruchomić polecenie kompilacji i mieć jedną dystrybucję

    3. Możesz łatwo uzyskać dostęp do innych usług ( /groupsmusi znaleźć użytkownika według identyfikatora)

  • Cons:

    1. Podczas programowania musisz skopiować pliki repozytorium w tym podfolderze. Po zmianie czegoś musisz skopiować te pliki z powrotem i zastąpić pliki repozytorium zaktualizowanymi plikami.

Trzecie podejście:

W zewnętrznym folderze niestandardowym wszystkie rozszerzenia są pełnoprawnymi samodzielnymi interfejsami API. Twój główny interfejs API po prostu zapewnia uwierzytelnianie i może działać jako serwer proxy, aby przekierowywać przychodzące żądania do docelowego interfejsu API.

  • plusy:

    1. Nowe rozszerzenia można łatwo opracowywać i testować
  • Cons:

    1. Wdrożenie będzie trudne. Będziesz miał główny interfejs API oraz API rozszerzeń n, rozpoczynający własny proces i nasłuchujący na porcie.

    2. System proxy może być trudny. Jeśli klient zażąda, /usersaby serwer proxy wiedział, które rozszerzenie API nasłuchuje dla tego punktu końcowego, wywołuje ten interfejs API i przekazuje tę odpowiedź z powrotem do klienta.

    3. Aby chronić interfejsy API rozszerzeń (uwierzytelnianie jest obsługiwane przez główny interfejs API), serwer proxy musi udostępnić klucz tajny tym interfejsom API. Zatem interfejs API rozszerzenia będzie przekazywał przychodzące żądania tylko wtedy, gdy ten pasujący klucz tajny zostanie dostarczony z serwera proxy.


Czwarte podejście:

Mikrousługi mogą pomóc. Wziąłem stąd przewodnik https://docs.nestjs.com/microservices/basics

Mogę mieć mikrousługę do zarządzania użytkownikami, zarządzania grupami itp. I korzystać z tych usług, tworząc mały interfejs API / bramę / proxy, który wywołuje te mikrousługi.

  • plusy:

    1. Nowe rozszerzenia można łatwo opracowywać i testować

    2. Oddzielne obawy

  • Cons:

    1. Wdrożenie będzie trudne. Trzeba będzie głównym API i n microservices rozpoczęciem własnego procesu i słuchać do portu.

    2. Wygląda na to, że musiałbym utworzyć nowy interfejs API bramy dla każdego klienta, jeśli chcę mieć możliwość dostosowania. Zamiast rozszerzać aplikację, musiałbym za każdym razem tworzyć niestandardowy interfejs API do zbierania danych. To nie rozwiązałoby problemu.

    3. Aby chronić interfejsy API rozszerzeń (uwierzytelnianie jest obsługiwane przez główny interfejs API), serwer proxy musi udostępnić klucz tajny tym interfejsom API. Zatem interfejs API rozszerzenia będzie przekazywał przychodzące żądania tylko wtedy, gdy ten pasujący klucz tajny zostanie dostarczony z serwera proxy.

hrp8sfH4xQ4
źródło
2
to może pomóc github.com/nestjs/nest/issues/3277
Question3r
Dzięki za link. Ale nie sądzę, że powinienem mieć niestandardowe rozszerzenia w moim kodzie. Sprawdzę,
hrp8sfH4xQ4
Myślę, że twój problem dotyczy raczej autoryzacji niż odpoczynku.
adnanmuttaleb
@ adnanmuttaleb, czy mógłbyś wyjaśnić dlaczego =?
hrp8sfH4xQ4

Odpowiedzi:

6

Istnieje kilka podejść do tego. Musisz dowiedzieć się, jaki przepływ pracy najlepiej pasuje do Twojego zespołu, organizacji i klientów.

Gdyby to zależało ode mnie, rozważyłbym użycie jednego repozytorium na moduł i użycie menedżera pakietów, takiego jak NPM, z pakietami prywatnymi lub o zasięgu organizacji do obsługi konfiguracji. Następnie skonfiguruj potoki wydania kompilacji, które wypychają repozytorium pakietów na nowe kompilacje.

W ten sposób wystarczy plik główny i plik manifestu pakietu dla każdej instalacji niestandardowej. Możesz samodzielnie opracowywać i wdrażać nowe wersje oraz ładować nowe wersje, kiedy jest to konieczne po stronie klienta.

Aby zwiększyć płynność, można użyć pliku konfiguracyjnego do mapowania modułów na trasy i napisania ogólnego skryptu generatora tras, aby wykonać większość ładowania początkowego.

Ponieważ pakiet może być dowolny, wzajemne zależności w pakietach będą działać bez większych problemów. Musisz być zdyscyplinowany, jeśli chodzi o zarządzanie zmianami i wersjami.

Przeczytaj więcej o pakietach prywatnych tutaj: Pakiety prywatne NPM

Teraz prywatne rejestry NPM kosztują pieniądze, ale jeśli jest to problem, istnieje kilka innych opcji. Zapoznaj się z tym artykułem, aby uzyskać informacje na temat alternatyw - zarówno bezpłatnych, jak i płatnych.

Sposoby posiadania prywatnego rejestru npm

Teraz, jeśli chcesz uruchomić własnego menedżera, możesz napisać prosty lokalizator usług, który pobiera plik konfiguracyjny zawierający informacje niezbędne do pobrania kodu z repozytorium, załadowania go, a następnie udostępnienia metody pobierania instancja do tego.

Napisałem prostą referencyjną implementację dla takiego systemu:

Ramy: lokalizator usługi lokomocji

Przykładowa wtyczka sprawdzająca palindromy: przykład wtyczki locomotion

Aplikacja korzystająca z frameworka do lokalizowania wtyczek: przykład aplikacji locomotion

Możesz się tym pobawić, pobierając go z npm przy użyciu npm install -s locomotion, musisz określić plugins.jsonplik o następującym schemacie:

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

przykład:

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

załaduj go w ten sposób: const loco = wymagana („locomotion”);

Następnie zwraca obietnicę, która rozwiąże obiekt lokalizatora usług, który ma metodę lokalizatora, aby uzyskać kontrolę nad twoimi usługami:

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);
});

Należy pamiętać, że jest to tylko implementacja referencyjna i nie jest wystarczająco odporna na poważne zastosowania. Jednak wzorzec jest nadal aktualny i pokazuje sedno pisania tego rodzaju frameworka.

Teraz trzeba by to rozszerzyć o obsługę konfiguracji wtyczek, inicjowanie, sprawdzanie błędów, może dodać obsługę wstrzykiwania zależności i tak dalej.

Espen
źródło
Dzięki. Pojawiają się dwa problemy, wydaje się, że wtedy polegamy na npm i musimy skonfigurować własny serwer. Drugą rzeczą jest to, że prywatny npm nie jest już wolny. Miałem nadzieję znaleźć podstawowe rozwiązanie techniczne. Ale +1 za pomysł :)
hrp8sfH4xQ4
1
dodano referencyjną implementację podstawowego rozwiązania dla tego rodzaju systemu.
Espen,
3

Wybrałbym opcję pakietów zewnętrznych.

Możesz zbudować aplikację tak, aby miała packagesfolder. Chciałbym, aby UMD skompilował kompilacje zewnętrznych pakietów w tym folderze, aby Twój skompilowany maszynopis nie miał żadnych problemów z pakietami. Wszystkie pakiety powinny mieć index.jsplik w folderze głównym każdego pakietu.

A aplikacja może uruchomić pętlę poprzez pakiety folderu za pomocą fsi requirewszystkich pakietów index.jsdo swojej aplikacji.

Z drugiej strony należy zająć się instalacją zależności. Myślę, że plik konfiguracyjny na każdym pakiecie może to rozwiązać. Możesz mieć niestandardowy npmskrypt w głównej aplikacji, aby zainstalować wszystkie zależności pakietu przed uruchomieniem aplikacji.

W ten sposób możesz po prostu dodać nowe pakiety do swojej aplikacji, kopiując wklejając pakiet do folderu paczek i ponownie uruchamiając aplikację. Twoje skompilowane pliki maszynopisu nie zostaną zmienione i nie musisz używać prywatnego npm dla własnych pakietów.

Kalesh Kaladharan
źródło
dzięki za odpowiedź. Myślę, że to brzmi jak rozwiązanie @ Espen już opublikowane, nie?
hrp8sfH4xQ4
@ hrp8sfH4xQ4 Tak, do pewnego stopnia. Dodałem go po przeczytaniu twojego komentarza o tym, że nie chcesz używać npm. Powyżej znajduje się rozwiązanie, które możesz zrobić, aby uniknąć prywatnego konta npm. Ponadto uważam, że nie trzeba dodawać pakietów utworzonych przez osobę spoza organizacji. Dobrze?
Kalesh Kaladharan
btw. ale byłoby wspaniale wesprzeć to też ... jakoś ...
hrp8sfH4xQ4
Jeśli potrzebujesz opcji dodawania pakietów stron trzecich, npmjest to sposób na zrobienie tego lub dowolnego innego menedżera pakietów. W takich przypadkach moje rozwiązanie nie wystarczy.
Kalesh Kaladharan
Jeśli nie masz nic przeciwko, chciałbym poczekać na zebranie jak największej liczby podejść
hrp8sfH4xQ4