使用Rust和Nextjs构建SaaS
我们也可以以类似的方式编写我们的登录功能;但是◆■★■,登录功能也将从axum_extra中采用一种PrivateCookieJar,这是一种抽象,您可以轻松地安全地处理cookie。让我们看看那会是什么样子:
我们可以使用create-shuttle-app开始编写我们的全栈应用程序,这是一个npm软件包■■■★,可以安装cargo-shuttle(用于部署管理的shuttle的CLI)★★◆★■,如果尚未安装,则使用货物的Rust,然后引导Next.js应用程序初始化以及包含我们开始的所有基础知识的后端文件夹。我们可以使用以下命令初始化应用程序◆★◆◆:
现在■◆,如果我们尝试使用相关表单数据向其发送POST请求,它应该会在我们的Stripe帐户上返回订阅■■★◆★!然后,我们可以将用户(在前端)重定向到成功页面或失败页面,具体取决于订阅尝试是否成功。
现在我们只需要运行npm run deploy,如果没有错误◆◆◆★★★,Shuttle将把我们的SaaS部署到实时服务器!您应该获得一个部署ID,以及一个数据库连接字符串和秘密列表以及您的应用程序使用的任何其他资源(在我们的案例中,公共文件夹)。
然后我们会想创建我们的端点!为了说明以下内容,我们创建了一个简短的功能,从数据库中获取给定用户的所有客户。让我们看看这个功能会是什么样子:
首先,我们希望定义一些结构★★★,这些结构将作为我们想要使用的响应或请求类型模型。如果请求类型或响应类型与模型不匹配,HTTP请求将自动失败,因此我们希望确保我们拥有所需的一切。见下文★◆◆◆:
使用Next.js,与webapp的其余部分相比,这部分非常简单◆◆◆■:我们需要做的就是将Next.js前端编译到Rust后端。为此◆■■◆,我们只需要确保我们的fig.js文件看起来像这样(在项目目录的根目录中)■■:
如果您坚持使用本文中的代码(或只是想查看最终结果),可以在这里找到带有最终代码的GitHub repo。本文使用前端模板,如果您决定使用它,则假设了解React/Next.js。您还可以在上找到此模板的实时部署。
获取销售记录将比获取客户稍微复杂一些,因为我们需要进行SQL连接才能为销售记录获取客户名称-客户名称不会存储在销售记录本身中,因为销售记录与客户记录相关联★★★■,因此我们可以轻松地从客户记录中获取它:
我们稍后会在我们的网络服务中使用API密钥和Mailgun域■■■◆★★,因此您需要找到一个安全的地方来记录您的API密钥和Mailgun域,这些密钥和其他人无法读取-我们稍后会使用这些★■★◆■。
我们希望通过访问Shuttle的网站登录,并通过GitHub登录★◆,然后在您最喜欢的终端上编写shuttle login,并按照说明进行操作。这将使我们能够部署到航天飞机★■。
您需要获取专用API密钥-每当我们想对Mailgun进行API调用时,我们都需要此密钥进行身份验证★★■◆,以便我们可以从自己的网络服务远程与服务进行交互◆■★◆★。
感谢您的阅读◆★■!我希望你能从头到尾拿走一些关于如何在Rust中构建自己的SaaS的想法■◆★◆◆,并用一个命令进行部署。如果您希望引导此Web应用程序★◆★,以便用自己的想法扩展它,您可以通过运行npx create-shuttle-app --fullstack-examples saas轻松完成。
对于付款★★■■◆◆,我们可以使用Stripe API使用async-stripe库轻松创建订阅。我们希望首先为我们的API端点初始化一个结构,以便能够获取付款信息:
首先,让我们进入带有cd backend的后端文件夹。我们希望从添加我们所有的依赖项开始,您可以使用这句简单的一句话来完成★★★■:
我们的用户需要能够创建使用客户的销售交易记录◆◆■■★,我们可以通过定义我们的请求和响应模型应该是什么样子来像上面一样创建它们,然后我们可以构建我们的端点■◆★★。
在本文结束时,我们将有一个以CRM为模型的易于扩展的SaaS入门包,该包具有以下内容:
您可能还想设置一个npm脚本来从npm运行后端,而不必每次都进入后端文件夹◆★★◆■◆,以及一个易于部署的脚本。我们可以这样设置:
带有API密钥的Mailgun帐户,以便我们可以远程订阅Mailgun邮件列表(您可以在这里注册Mailgun-您只需取消勾选“立即添加付款信息”,它就可以让您使用无限期免费试用◆★!)
像之前的其他示例一样,以下函数将采用状态和Json类型,并将返回在Axum中解析为响应的东西。让我们来看看:
您需要创建一个具有每月经常性成本的Stripe订阅项目,并将项目价格ID保存在我们稍后使用的某个地方。你可以在这里找到更多关于这个的信息★◆◆★◆◆。
登录后,您可以通过进入仪表板的底部找到Mailgun API密钥★◆■◆■◆,API密钥部分应该在那里:
我们需要在Stripe上创建客户和付款方式对象的功能。这部分非常简单-我们可以为各自的对象添加所需的任何参数,然后将其他所有内容称为默认值,如下所示★■◆★◆◆:
接下来,我们要为我们的订阅计划编写参数列表(您需要在Stripe上创建一个每月经常性价格的订阅计划,并获取产品ID,我们之前已经这样做了)■◆■◆★:
您还需要获取您的Mailgun域名-您可以通过单击“发送”,然后单击★■◆■■“域■■”来找到它,如下所示★■:
目前■◆★,我正在使用一个模板来充实我的前端■★★!它有用于登录和注册的页面■★■◆■★,基本仪表板和CRUD记录页面,以及分层定价页面和每月订阅结账。您可以在这里找到示例(确保插入后端,否则它将无法工作!)◆★。回购协议将假设您了解React/Next.js◆◆■■。
我们想从创建我们想要使用的结构开始。我们需要一个结构,作为用户注册详细信息的请求类型类型,然后是用户登录的类似结构:
现在我们可以合并我们所有的功能了◆★!我们最终可以将所有功能合并成一个最终的API路由器,我们可以像这样使用◆★:
接下来,我们希望安装SQLx命令行工具★◆,这样我们就可以毫不费力地进行迁移★★◆★★。谢天谢地■◆■,作为Rust板条箱★◆,安装起来非常简单◆★,所以我们可以使用一句话,然后继续编写我们的迁移:
您还需要安装Docker-您可以在此处找到有关如何安装Docker的更多信息。Docker是一个很棒的实用程序,主要用于使Shuttle能够运行自己的本地数据库实例,这意味着您可以避免自己设置任何东西■■■。
现在,您需要做的就是在您的终端中编写npm run build◆★★◆,它应该为您完成所有工作■■★!现在,您可以毫不费力地将前端与后端一起使用。您甚至可以在Rust后端运行时运行npm run build,它将为您重新编译资产,使您无需完全重新运行应用程序。
创建、编辑和删除销售记录并不像上述之前的查询那么困难!我们可以像为用户客户编写API端点一样为他们编写端点,因为我们不需要引用其他表中的任何数据◆★★■。
这将是一个POST请求,因为它更安全-客户端将发送用户的电子邮件以及他们的身份验证cookie,我们稍后会查看。我们还可以使用fetch_one()方法创建一个函数,该函数仅通过给定的ID返回一个特定客户,该方法仅返回1行,并将忽略任何其他行,如果出现问题,则返回错误:
现在让我们看看验证一个会话■◆◆。这并不难:我们试图用我们为会话声明的名称抓取cookie,并尝试将其与数据库中的内容进行匹配。如果匹配,那么我们允许用户继续-如果没有◆★■,我们返回403 Forbidden。
Rust是一种出色的语言,用于编写具有富有表现力的语法的内存安全程序,内存占用更少,可以降低您的开销,并让您以更低的价格部署更多,这对(潜在的)SaaS制造商来说非常好。
您还需要在Mailgun上创建一个邮件列表,并将您的mailgun域名、私钥和邮件列表名称保存在某个地方。您的mailgun域和密钥将私下存储在秘密文件中,但您可以在常规文件中使用邮件列表名称,因为这不需要是私有的(因为它可以从字面上命名为任何东西)。你可以在这里找到更多关于这个的信息。
如您所见◆◆★,尽管原始SQL查询比我们以前的端点更复杂一些,因为左连接,我们做了一个简单的选择,但它并不太复杂★■★★。我们正在从交易表中选择列,然后使用子查询从客户表中连接客户的名字和姓氏■◆■■,并将其添加到我们的查询结果中。
当我们需要编写SQLx迁移时,我们希望在项目根目录中运行sqlx migrate add name,它将创建一个迁移文件,以便我们可以添加迁移。这些将是我们将用于网络服务的迁移:
带有API密钥的Stripe帐户,以便我们可以远程付款(您可以在这里注册Stripe)
一些用户也希望能够退出登录。为此,我们需要做的就是编写一个函数来设置SQL查询,以便从会话数据库表中删除,其中会话ID是用户cookie的值,然后像这样返回cookie删除:
对于邮件■■■,我们将使用Mailgun,它有一个慷慨的免费计划,我们可以用它来保留邮件列表★■◆◆,并轻松地向新用户和我们的会员发送邮件。这部分将假设您有一个开始★■◆■,我们希望制作一个结构,该结构采用一个电子邮件地址◆◆■★,我们可以使用Serde反序列化到JSON:
一旦我们定义了结构★★◆■★★,我们就可以创建一个函数,为Mailgun API的POST请求制作参数的哈希图,最后是我们自己的API端点,以便能够将人们订阅到我们的Mailgun邮件列表:
我们将通过Shuttle进行部署,这是一个Rust原生云开发平台,旨在通过放弃复杂的配置文件并允许您使用代码注释(通过Rust宏)来使部署Rust Web服务尽可能简单。数据库和静态文件?没问题。只需将相关宏写入主函数中的参数,它即可工作■◆■■!没有供应商锁定,可以从您最喜欢的数据库管理工具(如pgAdmin)访问数据库。
最近★★◆◆■,用于Web开发的Rust已经走了很长的路;尽管生态系统可能不像JavaScript等流行语言那样庞大■★■,但其内存安全■■★◆、低内存占用★■◆、富有表现力的语法以及高度胜任的错误处理(当然还有速度!)的承诺■★。随着板条箱生态系统的扩大,变得更容易实现。支持Stripe★◆★■■、SMTP(通过lettre)、AWS-SES和其他邮件网络服务提供商、websockets和SSR、子域等。
同样,我们可以从数据库中获取单个销售记录■◆★◆,并通过为交易ID添加WHERE条件(从路径)将其返回到我们的端点:
如您所见,此函数采用“路径”类型。这基本上意味着◆◆★★■◆,例如,如果我们转到这个localhost:8000/api/customers/1,我们将能够看到它返回一个记录■★★◆■◆,如果存在,客户的ID为1。我们可以通过编写相关的SQL查询、绑定我们使用的变量并根据数据库连接运行它,以类似的方式创建、编辑和删除客户★■: