本文的目標是提供如何建立安全性的 Node.js GraphQL API 的快速指南。 【影片教學推薦:node js教學 】
你可能會想到一些問題:
這些問題都是有意義的,但在回答之前,我們應該深入了解目前Web 開發的狀態:
你會發現幾乎在每種情況下都會有一個不需要你去詳細了解的API,例如你不需要知道它們是怎樣構建的,並且不需要使用與他們相同的技術就能夠將其整合到你自己的系統中。 API允許你提供一種可以在伺服器和客戶端通訊之間進行通用標準通訊的方式,而不必依賴特定的技術堆疊。
透過結構良好的API,可以擁有可靠、可維護且可擴展的API,可以為多種客戶端和前端應用提供服務。
GraphQL 是一種 API 所使用的查詢語言,由Facebook開發並用於其內部項目,並於2015年公開發布。它支援讀取、寫入和即時更新等操作。同時它也是開源的,通常會與REST和其他架構放在一起比較。簡而言之,它是基於:
#雖然本文應該要展示一個關於如何建置和使用GraphQL API的簡單但真實的場景,但我們不會去詳細介紹GraphQL。因為GraphQL團隊提供了全面的文檔,並在Introduction to GraphQL中列出了幾個最佳實踐。
如上所述,查詢是客戶端從API讀取和操作資料的一種方式。你可以傳遞物件的類型,並選擇要接收的欄位類型。以下是一個簡單的查詢:
query{ users{ firstName, lastName } }
我們嘗試從用戶庫中查詢所有用戶,但只接收firstName
和lastName
。此查詢的結果將類似於:
{ "data": { "users": [ { "firstName": "Marcos", "lastName": "Silva" }, { "firstName": "Paulo", "lastName": "Silva" } ] } }
客戶端的使用非常簡單。
創建API的目的是使自己的軟體具有可以被其他外部服務整合的能力。即使你的程式被單一前端程式所使用,也可以將此前端視為外部服務,為此,當透過API為兩者之間提供通訊時,你能夠在不同的專案中運作。
如果你在一個大型團隊中工作,可以將其拆分為創建前端和後端團隊,從而允許他們使用相同的技術,並使他們的工作更輕鬆。
在本文中,我們將重點放在如何建構使用GraphQL API的框架。
GraphQL是一種適合多種情況的方法。 REST是一種體系結構方法。如今,有大量的文章可以解釋為什麼一個比另一個好,或者為什麼你應該只使用REST而不是GraphQL。另外你可以透過多種方式在內部使用GraphQL,並將API的端點維護為基於REST的架構。
你應該做的是了解每種方法的好處,分析自己正在創建的解決方案,評估你的團隊使用解決方案的舒適程度,並評估你是否能夠指導你的團隊快速掌握這些技術。
本文更偏重於實用指南,而不是GraphQL和REST的主觀比較。如果你想查看這兩者的詳細比較,我建議你查看我們的另一篇文章,為什麼GraphQL是API的未來。
在今天的文章中,我們將專注於如何用Node.js建立GraphQL API。
GraphQL有好幾個不同的支援庫可供使用。出於本文的目的,我們決定使用Node.js環境下的函式庫,因為它的應用非常廣泛,而且Node.js允許開發人員使用他們熟悉的前端語法進行伺服器端開發。
我们将为自己的 GraphQL API 设计一个构思的框架,在开始之前,你需要了解Node.js和Express的基础知识。这个GraphQL示例项目的源代码可以在这里找到(https://github.com/makinhs/no...)。
我们将会处理两种类型的资源:
Users 包含以下字段:
Products 包含以下字段:
至于编码标准,我们将在这个项目中使用TypeScript。
首先,要确保安装了最新的Node.js版本。在本文发布时,在Nodejs.org上当前版本为10.15.3。
让我们创建一个名为node-graphql
的新文件夹,并在终端或Git CLI控制台下使用以下命令:npm init
。
为了节约时间,在我们的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
。
接着是配置我们的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 tutorial”。在后台场景中,我们的配置会将 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}!`));
我们正在做的是:
好的,但是typeDefs和resolvers中发生了什么,它们与查询和修改的关系又是怎样的呢?
现在让我们再次运行npm start,看看我们能得到些什么。我们希望该程序运行后产生这种效果:Graphql API 侦听3000端口。
我们现在可以试着通过访问 http://localhost:3000/graphql 查询和测试GraphQL API:
好了,现在可以编写第一个自己的查询了,先定义为“hello”。
请注意,我们在typeDefs
中定义它的方式,页面可以帮助我们构建查询。
这很好,但我们怎样才能改变值呢?当然是mutation!
现在,让我们看看当我们用mutation对值进行改变时会发生什么:
现在我们可以用GraphQL Node.js API进行基本的CRUD操作了。接下来开始使用这些代码。
对于Products,我们将使用名为products的模块。为了是本文不那么啰嗦,我们将用内存数据库进行演示。先定义一个模型和服务来管理Products。
我们的模型将基于以下内容:
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; }; } }
对于users,我们将遵循与products模块相同的结构。我们将为用户提供模型和服务。该模型将定义为:
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; }; } }
提醒一下,源代码可以在 https://github.com/makinhs/no... 找到。
现在运行并测试我们的代码。运行npm start
,将在端口3000上运行服务器。我们现在可以通过访问http://localhost:3000/graphql来测试自己的GraphQL
尝试一个mutation,将一个项目添加到我们的product列表中:
为了测试它是否有效,我们现在使用查询,但只接收id
,name
和price
:
query{ products{ id, name, price } } 将会返回: { "data": { "products": [ { "id": 100, "name": "My amazing product", "price": 400 } ] } }
很好,按照预期工作了。现在可以根据需要获取字段了。你可以试着添加一些描述:
query{ products{ id, name, description, price } }
现在我们可以对product进行描述。接下来试试user吧。
mutation{ user(id:200, firstName:"Marcos", lastName:"Silva", password:"amaz1ingP4ss", permissionLevel:9, email:"marcos.henrique@toptal.com") { id } }
查询如下:
query{ users{ id, firstName, lastName, password, email } }
返回内容如下:
{ "data": { "users": [ { "id": 200, "firstName": "Marcos", "lastName": "Silva", "password": "kpj6Mq0tGChGbZ+BT9Nw6RMCLReZEPPyBCaUS3X23lZwCCp1Ogb94/ oqJlya0xOBdgEbUwqRSuZRjZGhCzLdeQ==", "email": "marcos.henrique@toptal.com" } ] } }
到此为止,我们的GraphQL骨架完成!虽然离实现一个有用的、功能齐全的API还需要很多步骤,但现在已经设置好了基本的核心功能。
让我们回顾一下本文的内容:
为了集中精力关注GraphQL API本身,我们忽略了几个重要的步骤,可简要总结如下:
请记住,我们在Git (https://github.com/makinhs/node-graphql-tutorial)上有完整的源代码。可以随意使用、fork、提问、pull 并运行它!请注意,本文中提出的所有标准和建议并不是一成不变的。
这只是设计GraphQL API的众多方法之一。此外,请务必更详细地阅读和探索GraphQL文档,以了解它提供的内容以及怎样使你的API更好。
英文地址原文:https://www.toptal.com/graphql/graphql-nodejs-api
更多编程相关知识,可访问:编程教学!!
以上是建立安全Node.js GraphQL API的快速指南的詳細內容。更多資訊請關注PHP中文網其他相關文章!