当前位置: 首页 > 科技观察

PrismaJavaScript类型安全简介_0

时间:2023-03-15 23:10:56 科技观察

【.com快译】如果你经常使用JavaScript,你可能会遇到与类型相关的问题。例如,您可能不小心将一个值从整数转换为字符串:console.log("User'scartvalue:","500"+100)[Log]User'scartvalue:"500100"看起来是一个非常简单的问题,但是,如果一个看似无害的错误存在于应用程序的有效代码中,那么它可能会成为一个真正的问题。这很可能在现实生活中发生,因为JavaScript越来越多地用于关键服务。幸运的是,像Prisma这样的数据库工具为JavaScript项目的数据库访问层提供了类型安全。在本文中,我们提供了输入JavaScript的背景知识,并强调了对实际项目的影响。然后,通过使用Prisma、Fastify和MySQL构建的示例应用程序实现自动检查以提高类型安全性,详见下文。静态vs.动态类型在不同的编程语言中,变量和值的类型检查可以在程序编译或执行的不同阶段进行。语言也可以允许或禁止某些操作,允许或禁止类型组合。根据何时进行类型检查,编程语言可以是静态类型的,也可以是动态类型的。静态类型语言,例如C++、Haskell和Java,通常会在编译时检查类型错误。动态类型语言(例如JavaScript)会在程序执行期间检查类型错误。类型错误在JavaScript中并不容易,因为这种语言中的变量没有类型。但是,如果我们尝试“不小心”将变量用作函数,则程序运行时会出现TypeError://types.jsnumBakers=2;console.log(numBakers());在我们的控制台中,错误看起来像这样:TypeError:numBakersisNotActionstrongvs.weaktyping类型检查的另一个关键点是强类型和弱类型。在这里,强弱之间的界限是模糊的,这取决于开发人员或社区的意见。有人说,如果一种语言允许隐式类型转换,那么它就是弱类型的。在JavaScript中,即使我们对整数和字符串求和,以下代码仍然有效:numBakers=1;numWaiters="many";totalStaff=numBakers+numWaiters;//1many这段代码执行没有错误,并且在值1被隐式转换为a计算totalStaff值时的字符串。基于这种行为,JavaScript可以被认为是一种弱类型语言。但弱类型到底意味着什么?对于许多开发人员来说,类型弱点会在编写JavaScript代码时带来不适和不确定性,尤其是在使用严格的系统(例如计费代码或会计数据)时。如果不检查变量中的意外类型,可能会导致混乱甚至造成真正的损害。这是一个面包店设备网站的示例代码,它实现了购买商业级搅拌机和一些替换零件的功能://types.jsmixerPrice="1870";mixerPartsPrice=100;console.log("Totalcartvalue:",mixerPrice+混合器部件价格);请注意,价格的定义方式存在类型不匹配。可能是类型发送到后端的时间早于正确的时间,因此信息被错误地存储在数据库中。如果我们执行这段代码会发生什么?让我们运行一个示例来说明结果$nodetypes.jsTotalcartvalue:1870100JavaScript缺乏类型安全性如何减慢可预测的行为。作为弱类型语言,JavaScript不提供类型安全。尽管如此,许多处理银行余额、保险金额和其他敏感数据的生产系统都是用JavaScript开发的。开发人员对意外行为持谨慎态度,尤其是因为它可能导致错误的交易金额。由于各种其他原因,JavaScript中缺乏类型安全可能会带来不便,例如:?生产力损失:如果您必须处理类型错误,可能需要很长时间来调试它们并考虑所有类型的可能性交互出错。?处理类型不匹配的样板代码:在类型敏感的操作中,开发人员通常需要添加代码来检查类型并协调任何可能的差异。此外,工程师必须编写许多测试来处理未定义的数据类型。添加与应用程序的业务价值不直接相关的额外代码对于保持代码库的可读性和清洁性来说并不是理想的选择。?缺乏明确的错误信息:有时类型不匹配会在错误的地方产生神秘的错误。在这种情况下,类型错误可能很难调试。?写入数据库时??出现意外问题:类型错误可能会导致写入数据库时??出现问题。例如,随着应用程序数据库的增长,开发人员经常需要添加新的数据库字段。在暂存环境中添加一个字段,但忘记将其推送到生产环境中,可能会在生产部署上线时导致意外的类型错误。由于应用程序数据库层中的类型错误会通过数据损坏造成很多危害,因此开发人员必须想出解决方案来解决因缺乏类型安全而引入的问题。在下一节中,我们将讨论引入像Prisma这样的工具如何帮助您解决JavaScript项目中的类型安全问题。使用Prisma进行类型安全的数据库访问尽管JavaScript本身不提供内置类型安全,但Prisma允许您在应用程序中选择类型安全检查。Prisma是一种新的ORM工具,由用于JavaScript和TypeScript的类型安全查询构建器(PrismaClient)、迁移系统(PrismaMigrate)和用于与数据库交互的GUI(PrismaStudio)组成。Prisma中类型检查的核心是Prisma模式,它是建模数据的唯一真实来源。这是最小模式的样子://prisma/schema.prismamodelBaker{idInt@id@default(autoincrement())emailString@uniquenameString?}在这个例子中,模式描述了一个Baker实体,其中每个实例都是一个面包师,有一个电子邮件(一个字符串),一个名称(也是一个字符串,可选)和一个自动递增的标识符(一个整数)。术语“模型”用于描述映射到后端数据库表的数据实体。在幕后,PrismaCLI从您的Prisma模式生成Prisma客户端。生成的代码使您可以方便地使用JavaScript访问您的数据库,并实施一系列检查和实用程序以使您的代码类型安全。Prisma模式中定义的每个模型都被转换为包含用于访问单个记录的函数的JavaScript类。通过在您的项目中使用像Prisma这样的工具,您可以在使用库(其对象关系映射层或ORM)生成的数据类型访问数据库中的记录时开始利用额外的类型检查。在Fastify应用程序中实现类型安全的示例让我们看一个在Fastify应用程序中使用Prisma模式的示例。Fastify是Node.js的Web框架,专注于性能和简单性。我们将使用prisma-fastify-bakery项目,它实现了一个简单的系统来跟踪面包店的运营。初步设置要运行该项目,我们需要在我们的开发机器上设置最新的Node.js版本。第一步是克隆repo并安装所有必需的依赖项:$gitpullhttps://github.com/chief-wizard/prisma-fastify-bakery.git$cdprisma-fastify-bakery$npminstall我们还需要确保我们有一个正在运行的MySQL服务器。如果您在安装和设置MySQL方面需要帮助,请查看Prisma关于该主题的指南。为了记录可以访问数据库的位置,我们将在存储库的根目录中创建一个.env文件:$touch.env现在我们可以将数据库URL添加到.env文件中。示例文件如下所示:DATABASE_URL='mysql://root:bakingbread@localhost/mydb?schema=public'设置完成后,让我们继续创建Prisma模式的步骤。为类型安全创建模式的第一步是添加模式。在我们的prisma/schema.prisma文件中,我们定义了我们的数据源,在本例中是我们的MySQL数据库。请注意,我们不是在模式文件中对数据库凭据进行硬编码,而是从.env文件中读取数据库URL。从环境中读取敏感数据在安全性方面更安全:datasourcedb{provider="mysql"url=env("DATABASE_URL")}然后我们定义与我们的应用程序相关的类型。在我们的示例中,让我们看一下我们将在面包店销售的产品的模型。我们想记录长棍面包和羊角面包等物品,并使追踪咖啡袋和果汁瓶等物品成为可能。项目将具有“蛋糕”、“面包”或“咖啡”等类型,以及“甜”或“咸”等类别(视情况而定)。我们还将存储每个产品的销售参考,以及产品的价格和成分。在Prisma模式文件中,我们首先将产品模型命名为:modelProduct{...}我们可以添加一个id属性-这将帮助我们快速识别产品表中的每条记录并将用作索引:modelProduct{...idInt@id@default(autoincrement())...}然后我们可以添加我们希望每个项目包含的任何其他属性。在这里,我们希望每个项目的名称都是唯一的,每个产品只给我们一个条目。为了引用成分和销售额,我们使用我们分别定义的成分和销售类型:modelProduct{...nameString@uniquetypeStringcategoryStringingredientsIngredient[]salesSale[]priceFloat...}现在,我们在Prisma模式中有了完整的产品模型。这是prisma.schema文件的样子,包括Ingredient和Sale模型:))nameString@uniqueallergenBooleanveganBooleanvegetarianBooleanproductsProduct?@relation(fields:[products_id],re$npxprismamigratedev--nameinitferences:[id])products_idInt?}modelSale{idInt@id@default(autoincrement())dateDateTime@default(现在())itemProduct?@relation(fields:[item_id],references:[id])item_idInt?}为了将我们的模型转换为实时数据库表,我们指示Prisma运行迁移。迁移包含用于在数据库中创建表、索引和外键的SQL代码。我们还传递了此迁移所需的名称init,它代表“初始迁移”:$npxprismamigratedev--nameinit我们看到以下输出表明数据库已根据我们的模式创建:MySQL数据库mydb创建于localhost:3306Thefollowingmigration(s)havebeenapplied:migrations/└─20210619135805_init/└─migration.sql...您的数据库现在与您的架构同步。?生成的Prisma客户端(2.25.0)到./node_modules/@prisma/clientin468ms此时我们已准备好在我们的应用程序中使用我们的模式定义的对象。创建使用Prisma模式的RESTAPI在本节中,我们将开始使用Prisma模式中的类型,从而为类型安全奠定基础。如果您想查看类型安全检查的实际效果,请直接跳到下一节。由于我们在示例中使用了Fastify,因此我们在fastify/routes目录中创建了一个product.js文件。我们从Prisma模式中添加Products模型,如下所示:const{PrismaClient}=require("@prisma/client")const{products}=newPrismaClient()然后我们可以定义一个Fastify路由,它使用模型上提供的Prisma查找许多功能。我们将参数take:100传递给查询以将结果限制为最多100个项目以避免我们的API过载:asyncfunctionroutes(fastify,options){fastify.get('/products',async(req,res)=>{constlist=awaitproduct.findMany({take:100,})res.send(list)})...当我们尝试为烘焙产品添加创建端点时,类型安全的真正价值开始发挥作用。通常,我们需要检查每个输入的类型。但在我们的示例中,我们可以完全跳过检查,因为PrismaClient将首先通过模式运行它们:...//createfastify.post('/product/create',async(req,res)=>{letaddProduct=req.body;constproductExists=awaitproduct.findUnique({where:{name:addProduct.name}})if(!productExists){letnewProduct=awaitproduct.create({data:{name:addProduct.name,type:addProduct.type,category:addProduct.category,sales:addProduct.sales,price:addProduct.price,},})res.send(newProduct);}else{res.code(400).send({message:'recordalreadyexists'})}})...在上面的示例中,我们在/product/create端点中执行以下步骤:?将请求的文本分配给变量addProduct。此变量包含请求中提供的所有详细信息。?使用findUnique函数查明我们是否已有同名产品。where子句允许我们过滤结果以仅包含具有我们提供的名称的产品。如果在运行此查询后productExists变量不为空,那么我们已经有一个同名的现有产品。?如果产品不存在:?我们使用请求中收到的所有字段创建它。我们通过使用product.create函数来做到这一点,新产品的详细信息位于数据部分下。?如果产品已经存在,我们返回一个错误。接下来,让我们使用cURL测试/product和/product/create端点。填充数据库并使用PrismaStudio测试我们的API我们可以通过运行以下命令启动我们的开发服务器:$npmrundev让我们打开PrismaStudio并查看数据库中当前的内容。我们将运行以下命令来启动PrismaStudio:$npx一旦prismastudio启动,我们将在本地URLhttp://localhost:5555中看到应用程序中的不同模型以及每个模型的记录数:当前在产品型号下没有条目,所以让我们通过单击“添加新记录”按钮创建一些记录:添加这些数据点后,让我们使用以下cURL命令测试我们的产品端点:$curllocalhost:3000/products#output[{"id":1,"name":"baguette","type":"savory","category":"bread","price":3,"ingredients":[]},{"id":2,"name":"whitebreadroll","type":"savory","category":"bread","price":2,"ingredients":[]}]让我们创建另一个产品:$curl-XPOST-H'Content-Type:application/json'-d'{"name":"黑麦面包卷","type":"savory","category":"bread","price":2}'localhost:3000/product/create#output{"id":3,"name":"黑麦面包卷","type":"savory","category":"bread","price":2,"ingredients":[]}另一个项目已成功创建!接下来,让我们看看我们的示例在API中的类型安全方面的行为安全请记住,我们目前不检查产品创建端点上的请求内容。如果我们错误地使用字符串而不是浮点数指定价格会怎样?让我们看看:$curl-XPOST-H'Content-Type:application/json'-d'{"name":"wholewheatbreadroll","type":"savory","category":"bread","price":"1.50"}'localhost:3000/product/create#output{"statusCode":500,"error":"InternalServerError","message":"\n无效的`prisma.product.create()`调用:\n\n{\n数据:{\n名称:'全麦面包卷',\n类型:'咸味',\n类别:'面包',\n销售额:未定义,\n价格:'1.50',\n~~~~~~\n成分:{\nconnect:undefined\n}\n},\ninclude:{\n成分:true\n}\n}\n\nArgument价格:prisma.createOneProduct上的无效值“1.50”。提供的字符串,预期为浮点数。\n\n"}如您所见,Prisma检查可防止我们创建价格不正确的商品-我们不必特例添加任何显式检查!将类型安全添加到现有项目的技巧到现在为止,很明显值类型检查可以添加到JavaScript项目中。如果您想尝试将此类检查添加到现有项目,这里有一些帮助您入门的提示。内省数据库以生成初始模式使用Prisma时,数据库内省允许您查看数据库中表的当前布局,并根据已有信息生成新模式。如果您不想手动编写模式,此函数是一个有用的起点。尝试运行npxprismaintrospect,几秒钟内,一个新的schema.prisma文件将在您的项目目录中自动生成。VSCode中的类型检查如果VisualStudioCode是您选择的编程环境,您可以利用ts-check指令直接在代码中获取类型检查建议。在使用Prisma客户端的JavaScript文件中,在每个文件的顶部添加以下注释://@ts-check启用此检查时,会突出显示类型错误等类型错误,从而更容易及早发现与类型相关的问题。在这篇ProductiveDevelopmentwithPrisma文章中了解有关此功能的更多信息。持续集成环境中的类型检查上面使用@ts-check的技巧之所以有效,是因为VisualStudioCode通过TypeScript编译器运行JavaScript文件。您还可以直接运行TypeScript编译器,例如,在您的持续集成环境中。在逐个文件的基础上添加类型检查可能是开始类型安全工作的可行方法。要开始检查文件中的类型,请将TypeScript编译器添加为开发依赖项:发出警告。我们建议开始对一个或几个文件运行TypeScript检查:$npxtsc--noEmit--allowJs--checkJsfastify/routes/product.js上面的示例将在我们的Fastify产品路由文件上运行TypeScript编译器。了解有关在JavaScript中实现类型安全的更多信息准备好将一些类型安全融入您自己的代码库了吗?在prisma-fastify-bakery存储库中查看我们的完整代码示例,并尝试自己运行该项目。【翻译稿件,合作网站转载请注明原译者和出处.com】