个性化阅读
专注于IT技术分析

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

本文概述

在本文中, 我们旨在提供有关如何创建安全的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更好?

可能有点解决问题, 但我无能为力:要看情况。

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

GraphQL教程:服务器测试

太好了, 现在我们可以编写第一个自己的查询, 定义为”你好”。

GraphQL教程:第一个查询

请注意, 我们在typeDefs上定义它的方式可以帮助我们构建查询。

很好, 但是我们如何改变价值呢?变异!

现在, 让我们看看通过突变更改内存值时会发生什么:

GraphQL教程:突变演示

现在, 我们可以使用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突变演示

为了测试它是否有效, 我们现在将使用产品查询, 但只接收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更好。

赞(0)
未经允许不得转载:srcmini » 如何创建安全的Node.js GraphQL API

评论 抢沙发

评论前必须登录!