使用自定义端点扩展现有API


12

我正在为多个客户创建一个API。像/users每个客户一样使用核心端点,但是某些端点依赖于单独的自定义。因此,可能是用户A需要一个特殊的终结点,/groups而其他客户将没有该功能。就像附带说明一样,由于这些额外功能,每个客户也将使用自己的数据库架构。

我个人使用NestJs(引擎盖下的Express)。因此,app.module当前注册了我所有的核心模块(带有自己的端点等)。

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

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

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

我认为这个问题与NestJs无关,因此您在理论上将如何处理?

我基本上需要一个能够提供基本系统的基础架构。由于每个扩展都是唯一的,/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构建以及来自自定义应用的构建?(即插即用系统)

    2. 自定义扩展非常宽松,因为它们仅包含一些打字稿文件。由于他们无权访问API的node_modules目录,因此我的编辑器将向我显示错误,因为它无法解析外部程序包依赖性。

    3. 某些扩展程序可能会从另一个扩展程序中获取数据。群组服务可能需要访问用户服务。这里的事情可能会变得棘手。


第二种方法: 将每个扩展名保留在API的src文件夹的子文件夹中。但是将此子文件夹添加到.gitignore文件中。现在,您可以将扩展保留在API中。

  • 优点:

    1. 您的编辑器能够解决依赖关系

    2. 在部署代码之前,您可以运行build命令,并且将具有单个分发

    3. 您可以轻松访问其他服务(/groups需要通过ID查找用户)

  • 缺点:

    1. 开发时,必须将存储库文件复制到该子文件夹中。更改某些内容后,您必须将这些文件复制回去,并用更新的文件覆盖您的存储库文件。

第三种方法:

在外部自定义文件夹中,所有扩展都是完全独立的API。您的主要API仅提供身份验证内容,并且可以充当代理,将传入的请求重定向到目标API。

  • 优点:

    1. 可以轻松开发和测试新扩展
  • 缺点:

    1. 部署将很棘手。您将有一个主API和n个扩展API,它们将启动各自的进程并监听端口。

    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
这可能会有所帮助github.com/nestjs/nest/issues/3277
Question3r

感谢您的链接。但是我认为我的代码中不应该包含自定义扩展名。我将检查微服务是否可以解决问题docs.nestjs.com/microservices/basics
hrp8sfH4xQ4

我认为您的问题与授权有关,而不是休息。
adnanmuttaleb

@ adnanmuttaleb您介意解释为什么=吗?
hrp8sfH4xQ4

Answers:


6

有几种解决方法。您需要做的是找出最适合您的团队,组织和客户的工作流程。

如果这取决于我,我将考虑每个模块使用一个存储库,并使用像NPM这样的程序包管理器以及私有或组织范围的程序包来处理配置。然后设置构建发布管道,以推送到新构建的软件包仓库。

这样,您所需要做的就是主文件和每个自定义安装的软件包清单文件。您可以独立开发和部署新版本,也可以在需要时在客户端上加载新版本。

为了增加平滑度,您可以使用配置文件将模块映射到路由,并编写通用的路由生成器脚本来执行大多数引导。

由于包可以是任何东西,因此包内的交叉依赖关系将很容易解决。您只需要在更改和版本管理方面受到约束。

在此处阅读有关私有软件包的更多信息: 私有软件包NPM

现在,私人NPM注册管理机构要花钱,但是如果这是一个问题,那么还有其他几种选择。请查看本文,了解一些其他选择-免费和付费。

拥有私人npm注册表的方法

现在,如果您想推出自己的经理,则可以编写一个简单的服务定位器,该服务定位器将使用一个包含必要信息的配置文件,以从存储库中提取代码,进行加载,然后提供某种方法来检索实例。

我已经为这种系统编写了一个简单的参考实现:

框架:运动服务定位器

检查回文的插件示例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”);

然后,它返回一个承诺,它将解决服务定位器对象,该对象具有locator方法来获取您的服务:

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

请注意,这只是参考实现,不足以用于严肃的应用程序。但是,该模式仍然有效,并显示了编写此类框架的要旨。

现在,这将需要扩展,以支持插件配置,初始化,错误检查,还可能添加对依赖项注入的支持等。


谢谢。出现两个问题,看来我们当时依赖npm并必须设置一个自托管注册表。第二件事是私有npm不再免费。我希望找到一个基本的技术解决方案。但是+1的想法:)
hrp8sfH4xQ4

1
为这种系统添加了基本解决方案的参考实现。
Espen

3

我会选择外部软件包。

您可以将应用程序结构化为具有packages文件夹。我将在该文件夹中使用UMD编译外部软件包的内部版本,以便您编译的打字稿不会对软件包造成任何问题。所有软件包index.js在每个软件包的根文件夹上都应有一个文件。

和你的应用程序可以运行通过包文件夹使用一个循环fsrequire所有软件包index.js到您的应用程序。

再一次,依赖项安装是您必须注意的事情。我认为每个软件包上的配置文件也可以解决该问题。您可以npm在主应用程序上具有一个自定义脚本,以在启动应用程序之前安装所有程序包依赖项。

这样,您可以通过将软件包复制粘贴到packages文件夹中并重新启动应用程序,从而将新软件包添加到您的应用程序中。您编译的打字稿文件不会被触碰,您也不必为自己的软件包使用私有npm。


感谢您的回复。我认为这听起来像@ Espen已经发布的解决方案,对吗?
hrp8sfH4xQ4

@ hrp8sfH4xQ4是的,在某种程度上。在阅读您对不想使用的评论后,我将其添加了npm。以上是您可以避免使用私人npm帐户的解决方案。而且,我相信您不需要添加组织外部人员创建的软件包。对?
Kalesh Kaladharan

顺便说一句 但它是真棒支持,太多......不知何故...
hrp8sfH4xQ4

如果您需要添加第三方软件包的选项,则npm可以使用此方法或任何其他软件包管理器。在这种情况下,我的解决方案是不够的。
Kalesh Kaladharan

如果您不介意,我想等待收集尽可能多的方法
hrp8sfH4xQ4
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.