estendere l'API esistente con endpoint personalizzati


12

Sto creando un'API per più clienti. Gli endpoint di base come /usersvengono utilizzati da ogni cliente, ma alcuni endpoint si basano sulla personalizzazione individuale. Quindi potrebbe essere che l' utente A desideri un endpoint speciale /groupse nessun altro cliente avrà quella funzionalità. Proprio come un sidenote , ogni cliente userebbe anche il proprio schema di database a causa di quelle funzionalità extra.

Personalmente uso NestJs (Express sotto il cofano). Quindi app.moduleattualmente registra tutti i miei moduli core (con i loro endpoint ecc.)

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

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

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

Penso che questo problema non sia correlato a NestJs, quindi come lo gestiresti in teoria?

Fondamentalmente ho bisogno di un'infrastruttura in grado di fornire un sistema di base. Non ci sono più endpoint principali perché ogni estensione è unica e /userspotrebbero essere possibili più implementazioni. Quando si sviluppa una nuova funzionalità, l'applicazione principale non deve essere toccata. Le estensioni dovrebbero integrarsi o dovrebbero essere integrate all'avvio. Il sistema principale viene fornito senza endpoint ma verrà esteso da quei file esterni.

Alcune idee mi vengono in mente


Primo approccio:

Ogni estensione rappresenta un nuovo repository. Definire un percorso per una cartella esterna personalizzata contenente tutti i progetti di estensione. Questa directory personalizzata conterrebbe una cartella groupscon agroups.module

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

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

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

La mia API potrebbe scorrere attraverso quella directory e provare a importare ciascun file del modulo.

  • professionisti:

    1. Il codice personalizzato è tenuto lontano dal repository principale
  • contro:

    1. NestJs usa Typescript, quindi devo prima compilare il codice. Come gestirò la build dell'API e le build dalle app personalizzate? (Sistema plug and play)

    2. Le estensioni personalizzate sono molto lente perché contengono solo alcuni file dattiloscritti. A causa del fatto che non hanno accesso alla directory node_modules dell'API, il mio editor mi mostrerà errori perché non è in grado di risolvere le dipendenze dei pacchetti esterni.

    3. Alcune estensioni potrebbero recuperare dati da un'altra estensione. Forse il servizio gruppi deve accedere al servizio utenti. Le cose potrebbero diventare complicate qui.


Secondo approccio: mantenere ogni estensione all'interno di una sottocartella della cartella src dell'API. Ma aggiungi questa sottocartella al file .gitignore. Ora puoi mantenere le tue estensioni all'interno dell'API.

  • professionisti:

    1. Il tuo editor è in grado di risolvere le dipendenze

    2. Prima di distribuire il codice è possibile eseguire il comando build e disporre di un'unica distribuzione

    3. Puoi accedere facilmente ad altri servizi (devi /groupstrovare un utente per ID)

  • contro:

    1. Durante lo sviluppo è necessario copiare i file del repository all'interno di quella sottocartella. Dopo aver modificato qualcosa, è necessario copiare nuovamente questi file e sovrascrivere i file del repository con quelli aggiornati.

Terzo approccio:

All'interno di una cartella personalizzata esterna, tutte le estensioni sono API standalone complete. L'API principale fornirebbe semplicemente le informazioni di autenticazione e potrebbe fungere da proxy per reindirizzare le richieste in arrivo all'API di destinazione.

  • professionisti:

    1. Le nuove estensioni possono essere sviluppate e testate facilmente
  • contro:

    1. La distribuzione sarà complicata. Avrai un'API principale e n API di estensione che iniziano il loro processo e ascoltano una porta.

    2. Il sistema proxy potrebbe essere complicato. Se il client richiede /usersal proxy di sapere quale API dell'estensione è in ascolto per quell'endpoint, richiama tale API e inoltra la risposta al client.

    3. Per proteggere le API di estensione (l'autenticazione è gestita dall'API principale) il proxy deve condividere un segreto con tali API. Quindi l'API di estensione passerà le richieste in arrivo solo se quel proxy corrispondente viene fornito dal proxy.


Quarto approccio:

I microservizi potrebbero aiutare. Ho preso una guida da qui https://docs.nestjs.com/microservices/basics

Potrei avere un microservizio per la gestione degli utenti, la gestione dei gruppi ecc. E consumare quei servizi creando un piccolo API / gateway / proxy che chiama quei microservizi.

  • professionisti:

    1. Le nuove estensioni possono essere sviluppate e testate facilmente

    2. Preoccupazioni separate

  • contro:

    1. La distribuzione sarà complicata. Avrai un'API principale e n microservizi che iniziano il loro processo e ascoltano una porta.

    2. Sembra che dovrei creare un nuovo gateway API per ogni cliente se voglio averlo personalizzabile. Quindi, invece di estendere un'applicazione, dovrei creare ogni volta un'API di comsuming personalizzata. Ciò non risolverebbe il problema.

    3. Per proteggere le API di estensione (l'autenticazione è gestita dall'API principale) il proxy deve condividere un segreto con tali API. Quindi l'API di estensione passerà le richieste in arrivo solo se quel proxy corrispondente viene fornito dal proxy.


2
questo potrebbe aiutare github.com/nestjs/nest/issues/3277
Question3r

Grazie per il link Ma non credo che dovrei avere le estensioni personalizzate nel mio codice. Verificherò
hrp8sfH4xQ4

Penso che il tuo problema sia legato all'autorizzazione piuttosto che al riposo.
adnanmuttaleb,

@ adnanmuttaleb ti dispiacerebbe spiegare perché =?
hrp8sfH4xQ4

Risposte:


6

Esistono diversi approcci a questo. Quello che devi fare è capire quale flusso di lavoro è più adatto per il tuo team, organizzazione e clienti.

Se dipendesse da me, prenderei in considerazione l'utilizzo di un repository per modulo e utilizzerei un gestore di pacchetti come NPM con pacchetti con ambito privato o aziendale per gestire la configurazione. Quindi impostare pipeline di rilascio build che inviano al repository di pacchetti su nuove build.

In questo modo è sufficiente il file principale e un file manifest del pacchetto per installazione personalizzata. È possibile sviluppare e distribuire in modo indipendente nuove versioni e caricare nuove versioni quando è necessario sul lato client.

Per maggiore fluidità, è possibile utilizzare un file di configurazione per mappare i moduli ai percorsi e scrivere uno script generico del generatore di percorsi per eseguire la maggior parte del bootstrap.

Poiché un pacchetto può essere qualsiasi cosa, le dipendenze incrociate all'interno dei pacchetti funzioneranno senza troppi problemi. Devi solo essere disciplinato quando si tratta di gestione delle modifiche e delle versioni.

Maggiori informazioni sui pacchetti privati ​​qui: Pacchetti privati ​​NPM

Ora i registri NPM privati ​​costano denaro, ma se questo è un problema ci sono anche molte altre opzioni. Leggi questo articolo per alcune alternative, sia gratuite che a pagamento.

Modi per avere il tuo registro privato npm

Ora, se vuoi spostare il tuo manager, potresti scrivere un semplice localizzatore di servizi, che contiene un file di configurazione contenente le informazioni necessarie per estrarre il codice dal repository, caricarlo e quindi fornire una sorta di metodo per recuperare un istanza ad esso.

Ho scritto una semplice implementazione di riferimento per un tale sistema:

Il framework: localizzatore di servizi di locomozione

Un plug-in di esempio che controlla i palindromi: esempio di plug-in di locomozione

Un'applicazione che utilizza il framework per individuare plug-in: esempio di app locomotion

Puoi giocare con questo ottenendolo da npm usando npm install -s locomotiondovrai specificare un plugins.jsonfile con il seguente schema:

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

esempio:

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

caricalo in questo modo: const loco = Require ("locomozione");

Restituisce quindi una promessa che risolverà l'oggetto localizzatore di servizio, che ha il metodo locator per ottenere una sospensione dei tuoi servizi:

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

Si noti che questa è solo un'implementazione di riferimento e non è abbastanza solida per un'applicazione seria. Tuttavia, il modello è ancora valido e mostra l'essenza di scrivere questo tipo di framework.

Ora, questo dovrebbe essere esteso con il supporto per la configurazione del plug-in, le inizializzazioni, il controllo degli errori, forse aggiungere il supporto per l'iniezione delle dipendenze e così via.


Grazie. Sorgono due problemi, sembra che ci affidiamo a npm e dobbiamo impostare un registro self-hosted. La seconda cosa è che npm privato non è più gratuito. Speravo di trovare una soluzione tecnica di base. Ma +1 per l'idea :)
hrp8sfH4xQ4

1
aggiunta un'implementazione di riferimento di una soluzione rudimentale per questo tipo di sistema.
Espen,

3

Vorrei andare per l'opzione di pacchetti esterni.

Puoi strutturare la tua app in modo che abbia una packagescartella. Avrei compilato UMD build di pacchetti esterni in quella cartella in modo che il tuo dattiloscritto compilato non avesse problemi con i pacchetti. Tutti i pacchetti dovrebbero avere un index.jsfile nella cartella principale di ciascun pacchetto.

E la tua app può eseguire un ciclo attraverso la cartella dei pacchetti utilizzando fse requiretutti i pacchetti index.jsnella tua app.

Quindi di nuovo l'installazione delle dipendenze è qualcosa di cui devi occuparti. Penso che anche un file di configurazione su ciascun pacchetto possa risolverlo. Puoi avere uno npmscript personalizzato sull'app principale per installare tutte le dipendenze del pacchetto prima di avviare l'applicazione.

In questo modo, puoi semplicemente aggiungere nuovi pacchetti alla tua app copiando il pacchetto nella cartella dei pacchetti e riavviando l'app. I tuoi file dattiloscritti compilati non verranno toccati e non dovrai usare npm privato per i tuoi pacchetti.


grazie per la tua risposta. Penso che suona come la soluzione @ Espen già pubblicata, no?
hrp8sfH4xQ4

@ hrp8sfH4xQ4 Sì, fino a un certo punto. L'ho aggiunto dopo aver letto il tuo commento sul non voler usare npm. Sopra è una soluzione che puoi fare per evitare l'account npm privato. Inoltre, credo che non sia necessario aggiungere pacchetti creati da qualcuno al di fuori dell'organizzazione. Giusto?
Kalesh Kaladharan,

btw. ma sarebbe fantastico supportare anche questo ... in qualche modo ...
hrp8sfH4xQ4

Se hai bisogno dell'opzione per aggiungere pacchetti di terze parti, allora npmè il modo di farlo o qualsiasi altro gestore di pacchetti. In questi casi, la mia soluzione non sarà sufficiente.
Kalesh Kaladharan,

Se non ti dispiace, vorrei aspettare per raccogliere il maggior numero possibile di approcci
hrp8sfH4xQ4
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.