创建安全Node.js GraphQL API的快速指南
本文的目標是提供關于如何創建安全的 Node.js GraphQL API 的快速指南。【視頻教程推薦:nodejs視頻教程 】
你可能會想到一些問題:
使用 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團隊提供了全面的文檔,并在Introduction to GraphQL中列出了幾個最佳實踐。
什么是GraphQL查詢?
如上所述,查詢是客戶端從API讀取和操作數據的一種方式。你可以傳遞對象的類型,并選擇要接收的字段類型。下面是一個簡單的查詢:
query{
users{
firstName,
lastName
}
}
我們嘗試從用戶庫中查詢所有用戶,但只接收firstName和lastName。此查詢的結果將類似于:
{
"data": {
"users": [
{
"firstName": "Marcos",
"lastName": "Silva"
},
{
"firstName": "Paulo",
"lastName": "Silva"
}
]
}
}
客戶端的使用非常簡單。
使用GraphQL API的目的是什么?
創建API的目的是使自己的軟件具有可以被其他外部服務集成的能力。即使你的程序被單個前端程序所使用,也可以將此前端視為外部服務,為此,當通過API為兩者之間提供通信時,你能夠在不同的項目中工作。
如果你在一個大型團隊中工作,可以將其拆分為創建前端和后端團隊,從而允許他們使用相同的技術,并使他們的工作更輕松。
在本文中,我們將重點介紹怎樣構建使用GraphQL API的框架。
GraphQL比REST更好嗎?
GraphQL是一種適合多種情況的方法。 REST是一種體系結構方法。如今,有大量的文章可以解釋為什么一個比另一個好,或者為什么你應該只使用REST而不是GraphQL。另外你可以通過多種方式在內部使用GraphQL,并將API的端點維護為基于REST的架構。
你應該做的是了解每種方法的好處,分析自己正在創建的解決方案,評估你的團隊使用解決方案的舒適程度,并評估你是否能夠指導你的團隊快速掌握這些技術。
本文更偏重于實用指南,而不是GraphQL和REST的主觀比較。如果你想查看這兩者的詳細比較,我建議你查看我們的另一篇文章,為什么GraphQL是API的未來。
在今天的文章中,我們將專注于怎樣用Node.js創建GraphQL API。
為什么要使用Node.js?
GraphQL有好幾個不同的支持庫可供使用。出于本文的目的,我們決定使用Node.js環境下的庫,因為它的應用非常廣泛,并且Node.js允許開發人員使用他們熟悉的前端語法進行服務器端開發。
掌握GraphQL
我們將為自己的 GraphQL API 設計一個構思的框架,在開始之前,你需要了解Node.js和Express的基礎知識。這個GraphQL示例項目的源代碼可以在這里找到(https://github.com/makinhs/no...)。
我們將會處理兩種類型的資源:
Users ,處理基本的CRUD。Products, 我們對它的介紹會詳細一點,以展示GraphQL更多的功能。
Users 包含以下字段:
idfirstnamelastnameemailpasswordpermissionLevel
Products 包含以下字段:
idnamedescriptionprice
至于編碼標準,我們將在這個項目中使用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。
接著是配置我們的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配置一個基本框架。為了開始我們的項目,將添加三個基本的導入:
ExpressExpress-graphqlGraphql-tools
把它們放在一起:
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。定義我們想要用作快速示例的查詢和修改。定義查詢和修改的工作方式。
好的,但是typeDefs和resolvers中發生了什么,它們與查詢和修改的關系又是怎樣的呢?
typeDefs - 我們可以從查詢和修改中獲得的模式的定義。Resolvers - 在這里我們定義了查詢和修改的功能和行為,而不是想要的字段或參數。Queries - 我們想要從服務器讀取的“獲取方式”。Mutations - 我們的請求將會影響在自己的服務器上的數據。
現在讓我們再次運行npm start,看看我們能得到些什么。我們希望該程序運行后產生這種效果:Graphql API 偵聽3000端口。
我們現在可以試著通過訪問 http://localhost:3000/graphql 查詢和測試GraphQL API:
好了,現在可以編寫第一個自己的查詢了,先定義為“hello”。
請注意,我們在typeDefs中定義它的方式,頁面可以幫助我們構建查詢。
這很好,但我們怎樣才能改變值呢?當然是mutation!
現在,讓我們看看當我們用mutation對值進行改變時會發生什么:
現在我們可以用GraphQL Node.js API進行基本的CRUD操作了。接下來開始使用這些代碼。
Products
對于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
對于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還需要很多步驟,但現在已經設置好了基本的核心功能。
總結和最后的想法
讓我們回顧一下本文的內容:
在Node.js下可以通過Express和GraphQL庫來構建GraphQL API;基本的GraphQL使用;查詢和修改的基本用法;為項目創建模塊的基本方法;測試我們的GraphQL API;
為了集中精力關注GraphQL API本身,我們忽略了幾個重要的步驟,可簡要總結如下:
新項目的驗證;使用通用的錯誤服務正確處理異常;驗證用戶可以在每個請求中使用的字段;添加JWT攔截器以保護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的快速指南的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何使用yum安装指定版本的php
- 下一篇: 塞尔达传说赌博技巧(为什么游戏《塞尔达)