如何创建安全的Node.js GraphQL API

本文概述

  • 也就是说, 什么是GraphQL API?
  • 什么是GraphQL查询?
  • 使用GraphQL API的目的是什么?
  • GraphQL是否比REST更好?
  • 为什么我们使用Node.js?
  • 动手GraphQL教程
  • 让我们编码吧!
  • 总结和最终想法
在本文中, 我们旨在提供有关如何创建安全的Node.js GraphQL API的快速指南。
可能想到的一些问题可能是:
  • 使用GraphQL API的目的是什么?
  • 什么是GraphQL API?
  • 什么是GraphQL查询?
  • GraphQL有什么好处?
  • GraphQL是否比REST更好?
  • 为什么我们使用Node.js?
所有这些都是有效的问题, 但是在回答问题之前, 我们应该深入了解Web开发的当前状态:
  • 你今天发现的几乎每个解决方案都使用某种应用程序编程接口(API)。
  • 即使仅使用Facebook或Instagram之类的社交网络, 你仍将连接到使用API??的前端。
  • 如果你感到好奇, 你会发现几乎所有的在线娱乐服务都使用不同类型的API, 包括Netflix, Spotify和YouTube等服务。
在几乎每种情况下, 你都可以找到不需要详细了解的API, 例如, 你不需要了解它们的构建方式, 也不需要使用以前使用的相同技术。能够将其集成到你自己的系统中。提供的API使你能够提供一种在服务之间进行通信的通用标准, 服务和客户端都可以在不依赖特定技术堆栈的情况下进行通信。
借助结构良好的API, 可以拥有可以为多种客户端和前端应用程序提供服务的可靠, 可维护和可扩展的API。
也就是说, 什么是GraphQL API? GraphQL是一种API的查询语言, 专为在Facebook内部使用而开发, 并于2015年发布以供公众使用。它支持读取, 编写和实时更新。它也是开源的, 通常与REST和其他体系结构进行比较。概括地说, 它基于:
  • GraphQL查询-这允许客户端读取和操纵应如何接收数据。
  • GraphQL突变-这是如何在服务器上写入数据。这是关于如何将数据写入系统的GraphQL约定。
尽管本文旨在演示一个简单而真实的场景, 说明如何构建和使用GraphQL API, 但我们将不对GraphQL进行详细介绍。原因很简单, 因为GraphQL团队提供了全面的文档, 并在GraphQL简介中列出了一些最佳实践。
什么是GraphQL查询? 如前所述, 查询是客户端可以从API读取和操作数据的方式。你可以传递对象的类型, 然后选择要接收的字段类型。一个简单的查询如下所示:
query{ users{ firstName, lastName } }

在此查询中, 我们尝试从用户的架构访问所有用户, 但只接收firstName和lastName。该查询的结果如下所示:
{ "data": { "users": [ { "firstName": "Marcos", "lastName": "Silva" }, { "firstName": "Paulo", "lastName": "Silva" } ] } }

客户端使用非常简单。
使用GraphQL API的目的是什么? 创建API的目的是具有将软件即服务与其他外部服务集成的能力。即使你的应用程序由单个前端使用, 你也可以将此前端视为外部服务, 为此, 当通过API提供两者之间的通信时, 你将能够在不同的项目中工作。
如果你在大型团队中工作, 则可以将其拆分成一个前端团队和一个后端团队, 从而允许他们使用相同的技术并简化他们的工作。在设计API时, 重要的是要选择最适合该项目的方案, 这将使你更接近所需的解决方案。
在本文中, 我们将重点介绍构建使用GraphQL的API的框架。
GraphQL是否比REST更好? 可能有点解决问题, 但我无能为力:要看情况。
【如何创建安全的Node.js GraphQL API】GraphQL是一种非常适合多种方案的方法。 REST是一种架构方法, 也已在多种情况下得到证明。如今, 有成千上万的文章解释了为什么一个比另一个更好, 或者为什么只使用REST而不是GraphQL。而且, 你可以采用多种方式在内部使用GraphQL, 并且仍将API的端点维护为基于REST的体系结构。
最好的指导是了解每种方法的好处, 分析你正在创建的解决方案, 评估你的团队使用该解决方案的舒适度, 并评估你是否能够指导你的团队学习和起步。在选择方法之前要加快速度。
本文更多是实用指南, 而不是对GraphQL和REST的主观比较。如果你想阅读两者的详细比较, 建议你阅读我们的另一篇文章GraphQL与REST-GraphQL教程。
在今天的文章中, 我们将重点介绍使用Node.js创建GraphQL API。
为什么我们使用Node.js? GraphQL有几个可以使用的不同库。就本文而言, 由于它们的广泛使用以及Node.js允许开发人员将熟悉的前端语法用于服务器端开发的事实, 我们决定将JavaScript与Node.js一起使用。
与我们的基于REST的API的方法进行比较也很有用, 类似于另一篇srcmini Engineering博客文章:在Node.js中创建安全REST API所演示的方法。本文还展示了如何将Node.js与Express一起使用来开发骨架REST API, 这将使你能够比较这两种方法之间的一些差异。 Node.js还设计为具有可扩展的网络应用程序, 全球社区以及可以在npm网站上找到的几个开源库。
这次, 我们将展示如何使用GraphQL, Node.js和Express构建骨架API!
动手GraphQL教程 如前所述, 我们将为GraphQL API建立一个基本概念, 并且你需要先了解Node.js和Express的基础知识。此处提供了针对此GraphQL示例创建的项目的源代码。
我们将处理两种类型的资源:
  • 用户, 我们将为其处理基本的CRUD。
  • 产品, 我们将对其进行详细介绍以展示GraphQL的更多功能。
用户将包含以下结构:
  • id
  • 名字
  • 电子邮件
  • 密码
  • PermissionLevel
产品将包含以下结构:
  • id
  • 名称
  • 描述
  • 价钱
至于编码标准, 我们将在该项目中使用TypeScript。在源文件中, 你将能够配置所有内容以使用TypeScript开始编码。
让我们编码吧! 首先, 请确保你安装了最新的Node.js版本。在发布之时, 根据Nodejs.org, 当前版本为10.15.3。
初始化项目
让我们从一个可以命名为node-graphql的新文件夹开始。在这里, 我们可以打开终端或Git CLI控制台, 并使用以下命令启动魔术:npm init。
配置我们的依赖项和TypeScript
为了加快这一过程, 在我们的Git存储库中将package.json替换为以下内容应包含所有必要的依赖项:
{ "name": "node-graphql", "version": "1.0.0", "description": "", "main": "dist/index.js", "scripts": { "tsc": "tsc", "start": "npm run tsc & & node ./build/app.js" }, "author": "", "license": "ISC", "dependencies": { "@types/express": "^4.16.1", "@types/express-graphql": "^0.6.2", "@types/graphql": "^14.0.7", "express": "^4.16.4", "express-graphql": "^0.7.1", "graphql": "^14.1.1", "graphql-tools": "^4.0.4" }, "devDependencies": { "tslint": "^5.14.0", "typescript": "^3.3.4000" } }

使用更新的package.json, 再次在终端上使用:npm install。它将在Node.js和Express中安装运行此GraphQL API所需的所有依赖项。
下一部分是配置我们的TypeScript模式。我们需要在根文件夹中包含以下名称的文件tsconfig.json:
{ "compilerOptions": { "target": "ES2016", "module": "commonjs", "outDir": "./build", "strict": true, "esModuleInterop": true } }

此配置代码的逻辑将出现在app文件夹中。在这里我们可以创建一个app.ts文件, 并为进行基本测试在其中添加以下代码:
console.log('Hello Graphql Node API tutorial');

通过我们的配置, 我们现在可以运行npm start并等待构建, 并且能够测试一切正常。在终端控制台中, 你应该看到我们的” Hello GraphQL Node API教程” 。在后台, 配置基本上将TypeScript代码编译为纯JavaScript, 然后在build文件夹中执行构建。
现在, 我们为GraphQL API配置一个基本框架。为了开始我们的项目, 我们将添加三个基本导入:
  • 表现
  • 表达图
  • 图形工具
让我们开始将它们放在一起:
import express from 'express'; import graphqlHTTP from 'express-graphql'; import {makeExecutableSchema} from 'graphql-tools';

现在我们应该可以开始编写一些代码了。下一步是处理Express中的应用程序和基本的GraphQL配置, 例如:
import express from 'express'; import graphqlHTTP from 'express-graphql'; import {makeExecutableSchema} from 'graphql-tools'; const app: express.Application = express(); const port = 3000; let typeDefs: any = [` type Query { hello: String }type Mutation { hello(message: String) : String } `]; let helloMessage: String = 'World!'; let resolvers = { Query: { hello: () => helloMessage }, Mutation: { hello: (_: any, helloData: any) => { helloMessage = helloData.message; return helloMessage; } } }; app.use( '/graphql', graphqlHTTP({ schema: makeExecutableSchema({typeDefs, resolvers}), graphiql: true }) ); app.listen(port, () => console.log(`Node Graphql API listening on port ${port}!`));

我们正在做的是:
  • 为我们的Express服务器应用程序启用端口3000。
  • 定义我们想用作快速示例的查询和变异。
  • 定义查询和变异的工作方式。
好的, 但是typeDef和解析器又发生了什么, 以及与查询和变异的关系?
  • typeDefs-关于查询和变异的期望的架构定义。
  • 解析器-在这里, 我们定义查询和变异应如何工作的功能和行为, 而不是字段或所需参数的期望。
  • 查询-我们要从服务器读取的” 获取” 。
  • 突变-我们的请求将影响我们在自己服务器上拥有的任何数据。
现在, 让我们再次运行npm start来看看那里有什么。我们希望该应用程序运行时将显示以下消息:Node Graphql API正在侦听端口3000!
现在, 我们可以尝试通过以下方式在我们自己的服务器上查询和测试GraphQL API:http:// localhost:3000 / graphql
如何创建安全的Node.js GraphQL API

文章图片
太好了, 现在我们可以编写第一个自己的查询, 定义为” 你好” 。
如何创建安全的Node.js GraphQL API

文章图片
请注意, 我们在typeDefs上定义它的方式可以帮助我们构建查询。
很好, 但是我们如何改变价值呢?变异!
现在, 让我们看看通过突变更改内存值时会发生什么:
如何创建安全的Node.js GraphQL API

文章图片
现在, 我们可以使用GraphQL Node.js API进行基本的CRUD操作。现在让我们继续进行代码。
产品展示
对于产品, 我们将使用称为产品的模块。为了简化本文, 我们将仅使用内存数据库进行演示。我们将定义模型和服务来管理产品。
我们的模型将基于以下内容:
export class Product { private id: Number = 0; private name: String = ''; private description: String = ''; private price: Number = 0; constructor(productId: Number, productName: String, productDescription: String, price: Number) { this.id = productId; this.name = productName; this.description = productDescription; this.price = price; }}

将与GraphQL通信的服务将定义为:
export class ProductsService {public products: any = []; configTypeDefs() { let typeDefs = ` type Product { name: String, description: String, id: Int, price: Int } `; typeDefs += ` extend type Query { products: [Product] } `; typeDefs += ` extend type Mutation { product(name:String, id:Int, description: String, price: Int): Product! }`; return typeDefs; }configResolvers(resolvers: any) { resolvers.Query.products = () => { return this.products; }; resolvers.Mutation.product = (_: any, product: any) => { this.products.push(product); return product; }; }}

用户数
对于用户, 我们将遵循与产品模块相同的结构。我们将为用户提供模型和服务。该模型将定义为:
export class User { private id: Number = 0; private firstName: String = ''; private lastName: String = ''; private email: String = ''; private password: String = ''; private permissionLevel: Number = 1; constructor(id: Number, firstName: String, lastName: String, email: String, password: String, permissionLevel: Number) { this.id = id; this.firstName = firstName; this.lastName = lastName; this.email = email; this.password = password; this.permissionLevel = permissionLevel; }}

同时, 我们的服务将像:
const crypto = require('crypto'); export class UsersService {public users: any = []; configTypeDefs() { let typeDefs = ` type User { firstName: String, lastName: String, id: Int, password: String, permissionLevel: Int, email: String } `; typeDefs += ` extend type Query { users: [User] } `; typeDefs += ` extend type Mutation { user(firstName:String, lastName: String, password: String, permissionLevel: Int, email: String, id:Int): User! }`; return typeDefs; }configResolvers(resolvers: any) { resolvers.Query.users = () => { return this.users; }; resolvers.Mutation.user = (_: any, user: any) => { let salt = crypto.randomBytes(16).toString('base64'); let hash = crypto.createHmac('sha512', salt).update(user.password).digest("base64"); user.password = hash; this.users.push(user); return user; }; }}

提醒一下, 可以从此链接使用源代码。
现在我们可以播放和测试我们的代码了。让我们运行npm start。我们将在端口3000上运行服务器。我们现在可以在http:// localhost:3000 / graphql上访问GraphQL进行测试。
让我们尝试一种将商品添加到我们的产品列表的变体:
如何创建安全的Node.js GraphQL API

文章图片
为了测试它是否有效, 我们现在将使用产品查询, 但只接收ID, 名称和价格:
query{ products{ id, name, price } }The response will be: { "data": { "products": [ { "id": 100, "name": "My amazing product", "price": 400 } ] } }

就是这样;产品运行正常。现在, 我们可以根据需要播放和切换字段。你可以尝试添加描述:
query{ products{ id, name, description, price } }

现在, 我们可以对我们的产品进行描述。让我们现在尝试用户。
mutation{ user(id:200, firstName:"Marcos", lastName:"Silva", password:"amaz1ingP4ss", permissionLevel:9, email:"[email  protected]") { id } }

一个查询将类似于:
query{ users{ id, firstName, lastName, password, email } }

响应如下:
{ "data": { "users": [ { "id": 200, "firstName": "Marcos", "lastName": "Silva", "password": "kpj6Mq0tGChGbZ+BT9Nw6RMCLReZEPPyBCaUS3X23lZwCCp1Ogb94/oqJlya0xOBdgEbUwqRSuZRjZGhCzLdeQ==", "email": "[email  protected]" } ] } }

现在我们的GraphQL骨架已经准备好了!从这里到实用的功能齐全的API尚有许多步骤, 但现在已设置了基本核心。
总结和最终想法 即使缩短技巧, 这篇文章也相当丰富, 其中包含有关GraphQL Node.js API开发的许多基本信息。
让我们回顾一下到目前为止所涵盖的内容:
  • 将Node.js与Express和GraphQL结合使用以构建GraphQL API;
  • GraphQL的基本用法;
  • 查询和变异的基本用法;
  • 为你的项目创建模块的基本方法;
  • 测试我们的GraphQL API;
为了更专注于事物的开发方面, 我们避免了几个重要的项目, 可以将其简要总结如下:
  • 新项目验证;
  • 使用通用错误服务正确处理错误;
  • 验证用户可以在每个请求中使用通用服务的字段;
  • 添加JWT拦截器以保护API;
  • 使用更有效的方法处理密码哈希;
  • 添加单元和集成测试;
请记住, 我们在此Git链接中具有完整的源代码。随意使用, 分叉, 打开问题, 发出拉动请求并进行操作!请注意, 本文提出的所有标准和建议并非一成不变。
这只是可用于开始设计自己的GraphQL API的众多方法之一。此外, 请确保更详细地阅读和探索GraphQL, 了解它所提供的功能以及如何使你的API更好。

    推荐阅读