【AICon】 如何构建高效的 RAG 系统?RAG 技术在实际应用中遇到的挑战及应对策略?>>> 了解详情
写点什么

Ballerina 面向全栈开发人员:创建后端 API 的指南

作者:Imesha Sudasingha

  • 2022-04-22
  • 本文字数:11574 字

    阅读完需:约 38 分钟

Ballerina 面向全栈开发人员:创建后端 API 的指南

背景

一个最简单的 Web 应用程序由 3 层构成,即客户端(前端)、服务器端(后端)和持久层(数据库)。全栈开发指的是针对 Web 应用程序的全部 3 层的实践。


  • 前端开发涉及 HTML、CSS 和原生 JavaScript, 或者一到多个 JavaScript 框架/库(如 JQuery 或 React.js)。在过去几年中,可用的工具、框架和库呈指数级增长,因此,前端开发本身就是一个非常广泛的课题。

  • 后端开发通常涉及服务器端脚本语言(如 PHP 或 JSP)又或者前端使用的服务。随着单页Web 应用程序 (SPA) 的出现,开发人员已经摆脱了传统的服务器端脚本语言,转而采用 API(REST、GraphQL、WebSocket 等)作为后端。

  • 持久层包括一个或多个 SQL 或者 NoSQL 数据库。但随着“大数据”的出现,数据存储不再局限于传统的数据存储。

热门的栈


MERN 栈- 图片来源


在上面提到的每一层使用的所有技术中,有一些技术比其他技术更受欢迎。当这些覆盖所有三层的技术结合在一起时,我们称之为“栈”,近些年来,我们已经看到了几个流行的栈。


  • LAMP / LEMP — JavaScript 用于前端。Linux 用于托管服务,Apache/Nginx 作为 Web 服务器,MySQL 作为持久层,PHP 作为后端(或服务器端)语言。像 Laravel 和 Symphony 这样的框架在这个栈下很流行。

  • MEAN — MongoDB 用于数据持久层,Express 作为后端框架,AngularJS 作为前端 JavaScript 框架,NodeJS 作为服务器运行时。

  • MERN — 与 MEAN 栈相同,只是选择了 ReactJS 而不是 Angular。

  • Ruby on Rails — 用 Ruby 编写的 Web 应用程序框架。

  • Django — JavaScript 用于前端,Python Django 框架用于后端,以及相应的 SQL 或 NoSQL 数据库层。然而,栈不再那么简单了。对于给定的一层,可用的替代技术成倍增加,在给定的层中,我们可以用来组合开发最终产品的不同技术的数量也在成倍增加。

现代全栈开发

在全栈开发方面,SPA(单页应用程序)和 PWA(渐进式 Web 应用程序)正在成为规范,并且出现了 SSR(服务器端渲染)等概念来解决它们的局限性。这些 Web 应用程序(前端)应该与后端 API(REST、GraphQL 等)一起工作,以便为终端用户提供最终功能。随之出现了诸如 BFF服务于前端的后端)之类的概念,以使后端 API 与前端用户体验 (UX) 保持一致。

BFF — 服务于前端的后端

一个组织可以有多个微服务,这些服务被不同的使用方使用,如移动应用程序、Web 应用程序、其他服务/API 和外部使用方。然而,现代 Web 应用程序需要一个紧密耦合的 API 来与前端 UX 紧密配合, 因此,BFF 充当了前端和微服务之间的接口。


一个 BFF 调用多个下游服务在前端构造一个视图。下游 API 可以是不同的类型(REST、GraphQL gRPC 等)。阅读模式:服务于前端的后端来深入了解 BFF 架构模式。


记住上面的概念,让我们进一步讨论一下现代 Web 开发。

全栈开发背景下的后端开发

开发后端 API 可能意味着两件事:


  1. 开发 BFF — 充当前端 UX 和后端微服务之间的适配器。

  2. 开发微服务 — 开发前端直接或间接(通过 BFF)使用的单个微服务。在全栈开发的背景下,我们只需考虑由前端直接调用的后端 API。这些 API 可以编写为 BFF API 或单独的微服务。

选择栈

如今,开发人员不会因为一个栈很流行就去使用它,他们选择最合适的前端技术来匹配他们希望实现的 UI/UX。然后他们选择后端技术时会考虑几个因素,包括其上市时间、可维护性和开发人员的经验。


在这篇文章中,我将介绍一个新的并且很有前景的后端开发候选技术,Ballerina。在将来,当你在为全栈开发做技术选型时,可以考虑一下它。

什么是 Ballerina?


Ballerina 编程语言徽标


Ballerina 是一种开源的云原生编程语言,旨在简化网络服务的使用、组合和创建。Ballerina Swan Lake 是 Ballerina 语言于今年发布的下一个主要版本,它在所有方面都进行了重大改进,包括改进的类型系统、增强的类 SQL 语言集成查询、增强/直观的服务声明等等。


Ballerina 背后的主要动机是**让开发人员能够专注于业务逻辑,同时减少集成云原生技术所需的时间。**使用 Ballerina 的内置网络原语直接在云上定义和运行服务的方式在这场变革中发挥了关键作用。灵活的类型系统、面向数据的语言集成查询、增强和直观的并发处理以及内置的可观察性和跟踪支持,使 Ballerina 成为云时代的首选语言之一。


在开发前端直接调用的 API 时,我们有几个常用的选择:


  • REST API

  • GraphQL API

  • WebSocket API 一旦选择了合适的 API 类型,接下来我们必须考虑以下因素:

  • 安全通信

  • 验证

  • 授权

  • 监控、可观测性和日志记录除了上述因素外,我们在开发 API 时还必须考虑可维护性和开发者体验。让我们看看 Ballerina 如何为开发上述 API 类型提供支持,以及是什么让 Ballerina 成为后端 API 开发领域中一个有前景的候选者。

Ballerina 与网络交互

Ballerina 编程语言的主要目标之一是简化网络交互代码的编写。考虑到这一点,Ballerina 在语言中内置了网络原语。当其他主流编程语言都将网络视为另一种 I/O 资源时,Ballerina 为网络交互提供了更为优秀的支持。为了实现这一目标,Ballerina 采用了以下优秀的组件设计:


  • 侦听器 — 充当网络层和服务层之间的接口。侦听器代表底层传输,如 HTTP、gRPC、TCP 或 WebSocket。

  • 服务—表示向最终用户公开组织功能的服务。HTTP、GraphQL、gRPC 和 WebSocket 是此类服务的一些例子。

  • 资源方法— 表示服务中的一个功能单元。例如,如果我们考虑使用一个简单的 CRUD 服务来管理库存,添加库存由一个单独的资源方法表示,而删除库存操作则由另一个资源方法表示。

  • 客户端—如今编写服务通常包括调用一个或多个外部或内部服务。例如:


  1. 在你的一项服务中,你可能想要发送一封电子邮件。为此,你需要一个电子邮件客户端。2. 同一个服务可能需要调用一个或多个内部 gRPC 服务。为此,你需要 gRPC 客户端。同样,编写服务需要调用外部服务。为此,Ballerina 有一个丰富的概念,称为客户端,外部调用由远程方法表示。在 Ballerina 运行时中调用远程方法是异步的(非阻塞,同时不需要显式回调或侦听器)。这些语言内置的网络原语与其他语言特性(如显式错误处理、内置 json/xml 支持和灵活的类型)相结合,可帮助开发人员更快地编写直观且可维护的网络交互,这反过来又使开发人员和组织能够更多地关注创新。


Ballerina 特性一览图


让我们探索一下如何使用 Ballerina 对 REST 和 GraphQL API 的支持来编写直观且有意义的后端 API。请按照入门指南安装和设置 Ballerina。


设置 Ballerina

开发 REST API

让我们看看如何使用 Ballerina 编写 REST API。


说 "Hello World!"


用 Ballerina 编写的 hello world REST API 如下所示:


import ballerina/http; service / on new http:Listener(8080) {    resource function get greeting() returns string {       return "Hello!";   }}
复制代码


让我们在这里解码语法的每个部分:


  • import ballerina/http;- 导入 ballerina/http 包。

  • service / on new http:Listener(8080)- 在 HTTP 侦听器上创建一个上下文路径为“/”的服务,该侦听器侦听 8080 端口,服务的类型由附加的侦听器的类型决定。在当前实例中,由于我们的侦听器是 HTTP 类型的,所以它就是一个 HTTP 服务。

  • resource function get greeting() returns string- 表示可以通过此 HTTP 服务执行单个操作。“get”是“资源访问器”。简单来说,它代表 HTTP 方法(get、post、delete 等)。“greeting”是函数名,函数名作为路径,这意味着资源路径“/greeting”由该资源方法处理。“returns string”表示此服务返回一个字符串,我们也可以在这里返回复杂的对象。

  • return "Hello World!";- 表示资源方法的返回值。这里是字符串“Hello World”。下图显示了 Ballerina HTTP 服务语法的概述:


Ballerina HTTP 服务结构(来源


要深入了解 Ballerina HTTP 服务语法,尤其是如何使用查询和路径参数、payload 数据绑定等,请参考以下文章:


HTTP Deep-Dive with Ballerina: Services

集成示例 — 货币转换 API

以下是一个稍微复杂一点的 REST API。给定基准货币、目标货币和金额,此 API 将返回转换后的金额。此 API 使用外部服务来获取最新汇率。


import ballerina/log;import ballerina/http; configurable int port = 8080; type ConversionResponse record {   boolean success;   string base;   map<decimal> rates;}; service / on new http:Listener(port) {    resource function get convert/[string baseCurrency]/to/[string targetCurrency](decimal amount) returns decimal|error {       http:Client exchangeEP = check new ("https://api.exchangerate.host");       ConversionResponse response = check exchangeEP->get(string `/latest?base=${baseCurrency}`);        if !response.success {           return error("Exchange rates couldn't be obtained");       }        decimal? rate = response.rates[targetCurrency];       if rate is () {           return error("Couldn't determine exchange rate for target currency", targetCurrency = targetCurrency);       }        log:printInfo("converting currency", baseCurrency = baseCurrency, targetCurrency = targetCurrency, amount = amount);        return rate * amount;   }}
复制代码


与 hello world 示例相比,这个示例展示了 Ballerina 一些更有趣的功能。


  • service / on new http:Listener(port) - 基础路径现在是 /,端口是可配置的,这意味着它可以在运行时进行配置,正如 configurable int port = 8080 中所定义的,端口的默认值为 8080,并且是可配置的,可配置变量也是一个值得注意的特性。

  • resource function get convert/[string baseCurrency]/to/[string targetCurrency](decimal amount) returns decimal|error - 在这种情况下,资源路径是 /convert/{baseCurrency}/to/{targetCurrency} 并且现在需要一个名为 amount 的查询参数。此资源方法会返回一个十进制值(转换速率)或一个错误(映射到 500 - 内部服务器错误)。

  • ConversionResponse response = check dccClient->get(string /latest?base=${baseCurrency}) - 调用外部汇率 API,并将响应转换为公开的记录 ConversionResponse。此调用是非阻塞的。响应有效载荷被无缝地转换为了一个公开的记录,这体现了 Ballerina 的默认开放原则。运行后,如下所示的 curl 请求会将 100 美元转换为英镑。


curl http://localhost:8080/convert/USD/to/GBP?amount=100
复制代码

红利:低代码开发

Ballerina 具有无泄漏的图形表示。也就是说,您可以同时编辑源代码和低代码视图(图形表示)。下图是上述 API 的低代码视图:


上述货币转换 API 的低代码视图


尽管我们不会在 Ballerina 的低代码方向做过多探索,但它对于非技术或技术水平较低的人来说,这有助于他们理解和编写代码,所以也试一试吧。


无泄漏 — 任何东西都可以用代码编程,代码中的一切都是可视的。

一个简单的 CRUD 服务

下面是一个用 Ballerina 编写的 CRUD 服务示例,它操作一组保存在内存中的产品。


import ballerina/http;import ballerina/log;import ballerina/uuid; # 表示一种产品public type Product record {|   # Product ID   string id?;   # Name of the product   string name;   # Product description   string description;   # Product price   Price price;|}; # 表示货币的枚举public enum Currency {   USD,   LKR,   SGD,   GBP} # 表示价格public type Price record {|   # Currency   Currency currency;   # Amount   decimal amount;|}; # 表示错误public type Error record {|   # Error code   string code;   # Error message   string message;|}; # 错误响应public type ErrorResponse record {|   # Error   Error 'error;|}; # 错误的请求响应public type ValidationError record {|   *http:BadRequest;   # Error response.   ErrorResponse body;|}; # 表示已创建响应的标头public type LocationHeader record {|   # Location header. A link to the created product.   string location;|}; # 产品创建响应public type ProductCreated record {|   *http:Created;   # Location header representing a link to the created product.   LocationHeader headers;|}; # 产品更新响应public type ProductUpdated record {|   *http:Ok;|}; # 产品服务service / on new http:Listener(8080) {    private map<Product> products = {};    # 列出所有产品   # + return - List of products   resource function get products() returns Product[] {       return self.products.toArray();   }    # 添加一个新产品   #   # + product - Product to be added   # + return - product created response or validation error   resource function post products(@http:Payload Product product) returns ProductCreated|ValidationError {       if product.name.length() == 0 || product.description.length() == 0 {           log:printWarn("Product name or description is not present", product = product);           return <ValidationError>{               body: {                   'error: {                       code: "INVALID_NAME",                       message: "Product name and description are required"                   }               }           };       }        if product.price.amount < 0d {           log:printWarn("Product price cannot be negative", product = product);           return <ValidationError>{               body: {                   'error: {                       code: "INVALID_PRICE",                       message: "Product price cannot be negative"                   }               }           };       }        log:printDebug("Adding new product", product = product);       product.id = uuid:createType1AsString();       self.products[<string>product.id] = product;       log:printInfo("Added new product", product = product);        string productUrl = string `/products/${<string>product.id}`;       return <ProductCreated>{           headers: {               location: productUrl           }       };   }    # 更新一个产品   #   # + product - Updated product   # + return - A product updated response or an error if product is invalid   resource function put product(@http:Payload Product product) returns ProductUpdated|ValidationError {       if product.id is () || !self.products.hasKey(<string>product.id) {           log:printWarn("Invalid product provided for update", product = product);           return <ValidationError>{               body: {                   'error: {                       code: "INVALID_PRODUCT",                       message: "Invalid product"                   }               }           };       }        log:printInfo("Updating product", product = product);       self.products[<string>product.id] = product;       return <ProductUpdated>{};   }    # 删除一个产品   #   # + id - Product ID   # + return - Deleted product or a validation error   resource function delete products/[string id]() returns Product|ValidationError {       if !self.products.hasKey(<string>id) {           log:printWarn("Invalid product ID to be deleted", id = id);           return {               body: {                   'error: {                       code: "INVALID_ID",                       message: "Invalud product id"                   }               }           };       }        log:printDebug("Deleting product", id = id);       Product removed = self.products.remove(id);       log:printDebug("Deleted product", product = removed);       return removed;   }}
复制代码


大部分语法是不言自明的,该服务有 4 种资源方法:


  • 列出所有产品 — GET /products

  • 添加新产品 — POST /products

  • 更新产品 — PUT /product

  • 删除产品 — DELETE /products/{id}请注意如何定义类型来表示产品、价格和货币。然后,我们在需要实现所需模式的地方定义了响应类型,ProductCreated 表示添加产品的响应,ValidationError 表示验证中的错误。


# 错误的请求响应public type ValidationError record {|   *http:BadRequest;   # Error response.   ErrorResponse body;|}; # 产品创建响应public type ProductCreated record {|   *http:Created;   # Location header representing a link to the created product.   LocationHeader headers;|};
复制代码


拥有这样的模式有助于开发人员轻松理解代码。只需查看资源方法定义,开发人员就可以清楚地了解资源方法。什么是资源路径,需要什么查询/路径参数,有效负载是什么,以及可能的返回类型是什么。


resource function post products(@http:Payload Product product) returns ProductCreated|ValidationError { }
复制代码


这是一个 POST 请求,发送到 /products(通过查看资源方法派生),需要 Product 类型的有效负载,并返回验证错误 (400) 或带有位置标头的 HTTP CREATED 响应 (201)。

生成 OpenAPI 规范

一旦我们用 Ballerina 编写了服务,只需指向源文件即可生成 OpenAPI 规范。通过查看源代码,它将输出带有相应状态代码和模式的 OpenAPI 规范。


您可以在 OpenAPI 部分阅读更多内容:

Ballerina OpenAPI 工具

生成完整的 OpenAPI 规范可帮助您生成所需的客户端。在我们的例子中,生成 JavaScript 客户端并将我们的前端与后端轻松集成。

保护服务

您可以通过将 HTTP 侦听器更新为 HTTPS 侦听器来保护您的服务,如下所示。


http:ListenerSecureSocket secureSocket = {   key: {       certFile: "../resource/path/to/public.crt",       keyFile: "../resource/path/to/private.key"   }};service /hello on new http:Listener(8080, secureSocket = secureSocket) {   resource function get world() returns string {       return "Hello World!";   }}
复制代码


您也可以启用双向 SSL 并进行高级配置。更多信息请参阅有关 HTTP 服务安全性的 Ballerina 示例。

验证

Ballerina 内置了对 3 种身份验证机制的支持。

JWT

您可以提供证书文件或授权服务器的 JWK 端点 URL 并启用 JWT 签名验证。例如,如果我们要使用像 Asgardeo 这样的 IDaaS(身份即服务)来保护我们的服务,我们只需在服务中添加以下注解:


@http:ServiceConfig {   auth: [       {           jwtValidatorConfig: {               signatureConfig: {                   jwksConfig: {                       url: "https://api.asgardeo.io/t/imeshaorg/oauth2/jwks"                   }               }           }       }   ]}
复制代码


此外,


  • 您可以保护整个服务或仅保护资源路径的子集。

  • 您可以在 JWT 中验证颁发者或受众。

  • 您可以根据声明执行授权(稍后解释)。请参阅 Ballerina 示例中的 REST API 安全部分以了解更多信息。

OAuth2

与 JWT 类似,您可以使用 OAuth2 保护您的服务。有关更多详细信息,请参阅服务 - OAuth2 示例。

基本认证

对于基本身份验证,有 2 个用户存储选项可用;文件和 LDAP。请参考以下示例以了解它是如何完成的:


Authorization

使用 OAuth2 和 JWT,您可以验证每个服务或每个资源的作用域。在这两种情况下,您都可以指定自定义范围键,默认值为 scope。


对于 JWT,您可以使用包含用户角色(基于角色的访问控制 — RBAC)或权限(细粒度访问控制)的自定义声明来授权单个操作。


import ballerina/http; public type Product record {|   string id?;   string name;   string description;|}; Product[] products = []; @http:ServiceConfig {   auth: [       {           jwtValidatorConfig: {               issuer: "wso2",               audience: "example.com",               scopeKey: "permissions",               signatureConfig: {                   jwksConfig: {                       url: "https://api.asgardeo.io/t/imeshaorg/oauth2/jwks"                   }               }           }       }   ]}service /products on new http:Listener(8080) {    @http:ResourceConfig {       auth: {           scopes: "product:view"       }   }   resource function get .() returns Product[] {       return products;   }    @http:ResourceConfig {       auth: {           scopes: "product:create"       }   }   resource function post .(@http:Payload Product product) returns error? {       products.push(product);   }}
复制代码


如上所示,/products 服务验证传入的 JWT 是否具有列出产品的 product:view 权限和创建产品的 product:create 权限。scopeKey 设置为 permissions,这是要在 JWT 中进行验证的声明的名称。此外,它还验证了发行者和受众。

客户端

显然,在编写后端 API 时,您必须与外部服务进行通信,至少你需要一个数据库客户端,无论是 DB 客户端、HTTP 客户端还是 gRPC 客户端,Ballerina 都能很好地满足您的需求。最重要的是,Ballerina 中的客户端调用是非阻塞的,开发人员无需添加任何回调或侦听器。


看看 Ballerina 的客户端有多方便:


GraphQL APIs

为了使这篇文章简短,我不会深入讨论如何编写 GraphQL API,但是,与 REST API 类似,Ballerina 对 GraphQL 服务具有相同级别的支持。请参阅以下链接以了解更多信息:


WebSocket APIs

我也不会深入讨论这个问题。有关编写 WebSocket 服务的更多详细信息,请参阅 Ballerina 网站的示例参考部分下的 WebSockets 和 WebSocket 安全部分。

可观察性

可观察性是该语言内置的关键特性之一。使用 Ballerina,您可以开箱即用地执行分布式跟踪和指标监控。

追踪

分布式追踪可通过 JaegerChoreo 实现。为了将追踪发布到 Jaeger(或 Choreo),您只需在代码中添加一行导入。在运行时,您的服务将使用 Open Telemetry 标准将追踪发布到 Jaeger(或 Choreo)。


使用 Jaeger 和 Ballerina 进行分布式跟踪

日志记录

Ballerina 提供的日志框架非常适合使用 logstash 和类似的日志分析器进行日志分析。在编写代码时,您可以将额外的键值对传递到日志行。


log:printInfo("This is an example log line", key1 = "key1", payload = { id: 123});
复制代码


上述日志行的输出如下所示:


time = 2022-01-26T16:19:38.662+05:30 level = INFO module = "" message = "This is an example log line" key1 = "key1" payload = {"id":123}
复制代码

指标

可以使用 Prometheusgrafana 监控实时指标。此外,还可以使用 Choreo 监控实时指标..


Choreo 中的可观察性视图(来源


与分布式跟踪类似,只需向源代码中添加一行导入,并导入一个现成的 grafana 仪表板,即可发布和监控实时指标。


Ballerina 的实时指标 Grafana 仪表板


从以下链接阅读有关 Ballerina 可观察性功能的更多信息:


持久层

后端开发的下一个主要方面是持久层。Ballerina 拥有丰富的 SQL 和 NoSQL DB 客户端。


通过客户端进行的 DB 调用在 Ballerina 中是非阻塞的

SQL

截至到目前,以下客户端是可用的:



mysqlClient->execute(`insert into products (product_name, price, currency) values (${product.productName}, ${product.price}, ${product.currency})`); mysqlClient->execute(`update products set product_name = ${product.productName}, price = ${product.price}, currency=${product.currency} where id=${product?.id}`);
复制代码


在上面的示例中,${<variableName>} 表示绑定到查询的变量,上面的代码在运行时作为准备好的语句执行。

Data Binding and Streams

类似地,我们可以使用如下的选择查询将数据作为用户定义类型的流来获取。假设我们有如下的产品记录:


type Product record {|   int id?;   string productName;   float price;   string currency;|};
复制代码


产品表定义如下:


CREATE TABLE `products` ( `id` int NOT NULL AUTO_INCREMENT, `product_name` varchar(255) NOT NULL, `price` float DEFAULT NULL, `currency` varchar(5) DEFAULT NULL, PRIMARY KEY (`id`))
复制代码


您可以将数据作为产品记录流获取,如下所示:


stream<Product, error?> productStream = mysqlClient->query(`select id, product_name as productName, price, currency from products`);
复制代码


请注意,记录字段名称和提取的列名称是相似的。

NoSQL

  • MongoDB

  • CosmosDB

  • AWS DynamoDB

  • Redis除了 NoSQL 的常见优势之外,Ballerina 内置的 JSON 和开放记录类型在处理非结构化或半结构化数据时会派上用场。


注意:Ballerina 的生态系统仍在完善过程中。因此,还没有功能齐全的 ORM 可用。

其它值得注意的功能

内置 JSON/XML 支持

正如我们已经看到的,Ballerina 内置了对主流传输格式 JSON 和 XML 的支持。使用 Ballerina,您可以在用户定义的类型和 JSON 之间无缝转换,正如我们在 HTTP 服务和 DB 示例中看到的那样。

结构类型系统

Ballerina 不使用名义类型(如 Java/Kotlin),而是依赖结构类型(如 Go/TypeScript)来确定子类型。这允许开发人员在用户定义的类型之间以及在用户定义的类型和 JSON 之间无缝转换。

静态类型

Ballerina 是静态类型的。这使得 Ballerina 能够提供一组丰富的工具来编写可靠且可维护的代码。同时,Ballerina 遵循“默认开放”的原则,您只需定义您感兴趣的内容,开放记录就是这种用法的一个例子。

显式错误处理

应显式处理错误。正如我们在示例中看到的,客户端调用返回结果和错误的并集。开发人员应该键入 check 检查错误,并显式地处理它们。

空安全

Ballerina 是空安全的。

可维护

结合以上所有方面,Ballerina 成为一种可维护且可靠的网络编程语言。

图形化

正如我们简要看到的,Ballerina 有一个非常好的、无泄漏的低代码特性。Ballerina 使用序列图来可视化网络交互,这对于技术水平较低和非技术人员编写程序和理解程序非常有用。

总结

在本文中,我想简单而全面地概述一下 Ballerina 编程语言对编写后端 API 的支持。我们介绍了如何深入编写 REST API,我们还研究了如何保护服务、如何执行身份验证和授权以及生成 OpenAPI 规范。


接下来,我们简要介绍了如何编写 GraphQL 和 WebSocket 服务,然后我们研究了可观察性特性和持久性层支持(针对 SQL 和 NoSQL 数据库),最后,我们看了一些值得注意的 Ballerina 特性。


我认为所讨论的内容将有助于您更多地探索 Ballerina 编程语言,并了解它在后端 API 开发环境中的重要性。我希望我能够确定 Ballerina 是一种真正的云原生编程语言,它将网络原语视为一等公民。我邀请你进一步探索 Ballerina 编程语言。


感谢您关注本文,随时分享您的意见和建议。此外,如果您还有其他问题,请联系我或 Ballerina 社区。


作者介绍:


Imesha Sudasingha 是 WSO2 的高级软件工程师,他还是 Apache 软件基金会的成员,现任 Apache OODT 项目管理委员会主席,他一直是开源的持续贡献者和推动者,在多个领域拥有工作经验,包括企业集成、支付和 DevOps,他目前参与了 Ballerina 项目,通过 IDE 工具参与改善 Ballerina 用户的开发人员体验。


原文链接:


https://www.infoq.com/articles/ballerina-fullstack-rest-api

2022-04-22 16:124883

评论

发布
暂无评论
发现更多内容

被忽略的一点:Docker的单进程模型

董哥的黑板报

Docker Kubernetes 容器 云原生 进程

如何应对核心员工提离职?

石云升

员工离职 职场经验 8月月更

一文带你打通Node流的"任督二脉"

战场小包

前端 Node 签约计划第三季

史上最全的Java并发系列之Java多线程(二)

自然

多线程 并发 8月月更

selenium脚本编写注意点(二)

Xd

如何成为一名合格的文案:需要具备哪些技能

石头IT视角

极狐 GitLab 冷知识:使用 Email 也可以创建 Issue?

郭旭东

极狐GitLab JIHULAB 101

自智网络简介

俞凡

网络 自智网络

开源一夏 | 使用 JavaScript 和 CSS 的随机颜色生成器

海拥(haiyong.site)

开源 8月月更

投研报告 -野心勃勃的meme项目 Lovely Inu($ lovely)

鳄鱼视界

史上最全的Java并发系列之Java多线程

自然

多线程 并发 8月月更

苏彤,你的 Python Flask 编写生成二维码接口写完了

梦想橡皮擦

Python 爬虫 8月月更

一篇文章和你从Java1聊到Java18

瀛洲骇客

Java core

构建在Findora上的Forlend,具备隐私特性的借贷协议

小哈区块

RxJS实现“搜索”功能

掘金安东尼

前端 函数式编程 8月月更

C++继承的基本语法与三种继承方式

CtrlX

c c++ 面向对象 继承 8月月更

史上最全的Java并发系列之Java内存模型

自然

多线程 并发 8月月更

Redis 多机

武师叔

8月月更

手把手带你实战 AGP 7.x ASM 字节码插桩

如浴春风

android asm Gradle 签约计划第三季

每日一R「06」内存管理

Samson

8月月更 ​Rust

极狐 GitLab 冷知识:Quick Actions 快速操作 Issue

郭旭东

极狐GitLab JIHULAB 101

深入了解JAVA篇之内存分析

邱学喆

JVM 堆内存 内存结构 MAT

【云原生】Docker入门 -- 阿里云服务器环境下安装Docker

Bug终结者

Docker 阿里云 云原生 服务器 8月月更

构建在Findora上的Forlend,具备隐私特性的借贷协议

西柚子

计算后缀表达式-算法与数据结构-栈的运用-C++语言实现

清风莫追

算法 数据结构, 8月月更

开源一夏 | JS超集对TypeScript的Map对象以及联合类型的深入实战

恒山其若陋兮

开源 8月月更

构建在Findora上的Forlend,具备隐私特性的借贷协议

BlockChain先知

【LeetCode】分割字符串的最大得分Java题解

Albert

LeetCode 8月月更

开源雨林企业开源治理与贡献论坛| ChinaOSC

CCF开源发展委员会

一张图进阶 RocketMQ - 消费者这个大冤种

三此君

RocketMQ 消息队列 消费者consumer 签约计划第三季

types-paddle: 为Paddle增加Tensor类型注释特性

吴京

Python paddle machine-learning

Ballerina 面向全栈开发人员:创建后端 API 的指南_语言 & 开发_InfoQ精选文章