- Apollo Client
- Relay
官方教程(JavaScript)
入门指南
试用的存储库在此处
准备软件包
代码语言:bash复制npm init -y && npm i ts-node graphql
在 GraphQL 中获取数据需要定义查询类型(Query type)的模式以及实际处理数据的被称为 Resolver 的函数的实现。需要注意的是,在 Query 类型中定义了用于获取数据的 API。除了 Query 类型,还有用于添加、修改、删除数据的 Mutation 类型,以及用于订阅事件的 Subscription 类型。
以下是一个返回 Hello World 的 Query 类型 API 的实现示例。
代码语言:ts复制import { graphql, buildSchema } from 'graphql';
// 在 Query 类型中定义模式
// 定义返回字符串的名为 hello 的 API
const schema = buildSchema(`
type Query {
hello: String
}
`);
// 定义处理查询的 Resolver
const rootValue = {
hello: () => {
return 'Hello world!';
},
};
const main = async () => {
const query = '{ hello }';
// 执行查询
const response = await graphql({
schema,
source: query,
rootValue,
});
console.log(response);
};
main();
执行
代码语言:sh复制npx ts-node server
{ data: Object: null prototype { hello: 'Hello world!' } }
Running an Express GraphQL Server
可以使用 Express 将 GraphQL 服务器挂载到 HTTP 终端点上
安装包
代码语言:bash复制npm init && npm i ts-node graphql express-graphql express @types/express
使用 Express 将 GraphQL 服务器映射到 HTTP 终端点的实现示例
代码语言:ts复制import express from 'express';
import { graphqlHTTP } from 'express-graphql';
import { buildSchema } from 'graphql';
const schema = buildSchema(`
type Query {
hello: String
}
`);
const root = {
hello: () => {
return 'hello world';
},
};
const app = express();
app.use(
'/graphql', // 将 GraphQL 服务器映射到 /graphql
graphqlHTTP({
schema,
rootValue: root,
graphiql: true, // 启用 GraphQL 的 Web 客户端 GraphiQL。
})
);
app.listen(4000);
console.log('Running a GraphQL API server at http://localhost:4000/graphql');
运行graphql服务器
代码语言:sh复制npx ts-node server
在浏览器中访问以下 URL,即可访问 GraphiQL
,通过 Web UI 执行 GraphQL 查询。
https://localhost:4000/graphql
在屏幕的左侧输入以下查询并执行,将会返回结果。
代码语言:graphql复制{
hello
}
GraphQL Clients
虽然存在 GraphQL 客户端库,但也可以通过简单的 HTTP POST 请求轻松发出查询。在 REST API 中,根据用途使用 GET/DELETE/POST/PUT 等不同的请求方法,但在 GraphQL 中,所有查询都使用 POST。
对于使用 Express GraphQL Server 创建的 GraphQL 服务器,可以通过执行以下 curl 命令返回 JSON 格式的数据。
代码语言:sh复制curl -X POST -H "Content-Type: Application/json" -d '{"Query": "{hello}"}' http://localhost:4000/graphql
此外,GraphQL 也允许向 API 端点传递参数。
通过在查询中指定以 $ 为前缀的关键字,并在变量中传递具有相应关键字属性的对象,可以自动转义值并发出查询。
代码语言:ts复制const dice = 3;
const sides = 7;
// 为了传递 dice 和 sides 作为变量,
// 通过指定 $dice 和 $sides 来创建查询。
const query = `query RollDice($dice: Int!, $sides: Int) {
rollDice(numDice: $dice, numSides: $sides)
}`;
const result = await fetch("/graphql2", {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify({
query,
variables: { dice, sides },// 传递包含 dice 和 sides 的对象
}),
});
Basic Types
在 GraphQL 中的基本类型及其对应的 JavaScript 类型如下:
String
:字符串型 → stringInt
:整数型 → numberFloat
:浮点数型 → numberBoolean
:布尔型 → booleanID
:唯一标识符 → string- GraphQL 客户端似乎会根据此ID执行缓存等操作
所有基本类型都是可空的。
如果不允许为 Null,则在末尾添加 !
(例如,String!)。
对于列表类型,使用 []
括起来(例如,String)。
import express from 'express';
import { graphqlHTTP } from 'express-graphql';
import { buildSchema } from 'graphql';
const schema = buildSchema(`
type Query {
quoteOfTheDay: String
random: Float!
rollThreeDice: [Int]
}
`);
const root = {
quoteOfTheDay: () => {
return Math.random() < 0.5 ? 'Take it easy' : 'Salvation lies within';
},
random: () => {
return Math.random();
},
rollThreeDice: () => {
return [1, 2, 3].map((_) => 1 Math.floor(Math.random() * 6));
},
};
const app = express();
app.use(
'/graphql',
graphqlHTTP({
schema,
rootValue: root,
graphiql: true,
})
);
app.listen(4000);
console.log('Running a GraphQL API server at localhost:4000/graphql');
Passing Arguments
在 GraphQL 中,也可以向端点传递参数。
参数需要指定名称和类型。
代码语言:graphql复制type Query {
rollDice(numDice: Int!, numSides: Int): [Int]
}
由于 numDice 使用 !
保证非空,因此可以省略服务器的验证。
在带有参数的 API 中,参数将作为对象传递给解析器的第一个参数。
代码语言:ts复制import express from 'express';
import { graphqlHTTP } from 'express-graphql';
import { buildSchema } from 'graphql';
const schema = buildSchema(`
type Query {
rollDice(numDice: Int!, numSides: Int): [Int]
}
`);
// RollDice 的参数
interface RollDiceArgs {
numDice: number;
numSides: number;
}
const root = {
//解析器的第一个参数作为对象传递了参数。
rollDice: ({ numDice, numSides }: RollDiceArgs) => {
const output: number[] = [];
for (let i = 0; i < numDice; i ) {
output.push(1 Math.floor(Math.random() * (numSides || 6)));
}
return output;
},
};
const app = express();
app.use(express.static('public'));
app.use(
'/graphql',
graphqlHTTP({
schema,
rootValue: root,
graphiql: true,
})
);
app.listen(4000);
console.log('Running a GraphQL API server at localhost:4000/graphql');
Object Type
在 GraphQL 的模式中,可以定义具有自定义行为的对象。
代码语言:ts复制// 定义具有自定义行为的 RandomDie
type RandomDie {
roll(numRolls: Int!): [Int]
}
type Query {
// 返回 RandomDie 的 API
getDie(numSides: Int): RandomDie
}
getDie 和 RandomDie 的实现示例
代码语言:ts复制import express from 'express';
import { graphqlHTTP } from 'express-graphql';
import { buildSchema } from 'graphql';
// 由于可以访问 RandomDie 的公共成员,
// 因此也要为 numSides 和 rollOnce 定义模式
const schema = buildSchema(`
type RandomDie {
numSides: Int!
rollOnce: Int!
roll(numRolls: Int!): [Int!]!
}
type Query {
getDie(numSides: Int): RandomDie
}
`);
class RandomDie {
constructor(private _numSides: number) {}
get numSides() {
return this._numSides;
}
rollOnce() {
return 1 Math.floor(Math.random() * this._numSides);
}
// 参数以对象形式传递,因此使用解构赋值接收它们
roll({ numRolls }: { numRolls: number }) {
const output: number[] = [];
for (let i = 0; i < numRolls; i ) {
output.push(this.rollOnce());
}
return output;
}
}
interface GetDieArg {
numSides: number;
}
const root = {
getDie: ({ numSides }: GetDieArg) => {
return new RandomDie(numSides || 6);
},
};
const app = express();
app.use(express.static('public'));
app.use(
'/graphql',
graphqlHTTP({
schema,
rootValue: root,
graphiql: true,
})
);
app.listen(4000);
console.log('Running a GraphQL API server at localhost:4000/graphql');
对于通过 getDie 获取的 RandomDie,执行每个方法的结果将返回。
代码语言:graphql复制{
getDie(numSides: 6) {
rollOnce
roll(numRolls: 3)
numSides
}
}
↓
代码语言:json复制{
"data": {
"getDie": {
"rollOnce": 5,
"roll": [
5,
4,
5
],
"numSides": 6
}
}
}
Mutations and Input Types
在执行诸如数据插入或数据修改等的 API 时,应将其定义为 Mutation 而不是 Query。
更新和获取消息的 API 模式
代码语言:graphql复制type Mutation {
setMessage(message: String): String
}
type Query {
getMessage(message: String): String
}
更新和获取消息的 API 实现示例
代码语言:ts复制import express from 'express';
import { graphqlHTTP } from 'express-graphql';
import { buildSchema } from 'graphql';
const schema = buildSchema(`
type Mutation {
setMessage(message:String!): String!
}
type Query {
getMessage: String!
}
`);
const inmemoryDB = {
message: 'default',
};
const root = {
setMessage: ({ message }: { message: string }) => {
inmemoryDB.message = message;
return inmemoryDB.message;
},
getMessage: () => {
return inmemoryDB.message;
},
};
const app = express();
app.use(
'/graphql',
graphqlHTTP({
schema,
rootValue: root,
graphiql: true,
})
);
app.listen(4000);
console.log('Running a GraphQL API server at localhost:4000/graphql');
获取初始消息
代码语言:tsx复制{
getMessage
}
↓
代码语言:json复制{
"getMessage": "default"
}
更新消息
代码语言:tsx复制mutation {
setMessage(message: "hello")
}
↓
代码语言:json复制{
"setMessage": "hello"
}
获取新消息
代码语言:graphql复制{
getMessage
}
↓
代码语言:json复制{
"getMessage": "hello"
}
此外,当多个 API 使用相同的输入参数等情况时,可以使用 input 关键字将它们汇总为输入类型。
代码语言:graphql复制input MessageInput {
content: String
author: String
# message: Message -> 对象类型不允许
}
type Message {
id: ID!
content: String
author: String
}
type Query {
getMessage(id: ID!): Message
}
type Mutation {
createMessage(input: MessageInput): Message
updateMessage(id: ID!, input: MessageInput): Message
}
输入类型可以作为字段具有基本类型、列表类型和输入类型。
不允许具有对象类型。
代码语言:ts复制import express from 'express';
import { graphqlHTTP } from 'express-graphql';
import { buildSchema } from 'graphql';
import crypto from 'crypto';
const schema = buildSchema(`
input MessageInput {
content: String
author: String
}
type Message {
id: ID!
content: String
author: String
}
type Query {
getMessage(id: ID!): Message
}
type Mutation {
createMessage(input: MessageInput): Message
updateMessage(id: ID!, input: MessageInput): Message
}
`);
interface MessageInput {
content: string;
author: string;
}
class Message {
private _content: string;
private _author: string;
constructor(private _id: string, { content, author }: MessageInput) {
this._content = content;
this._author = author;
}
get id() {
return this._id;
}
get content() {
return this._content;
}
get author() {
return this._author;
}
}
type MessageInfo = {
content: string;
author: string;
};
const inmemoryDB: {
[key: string]: MessageInfo;
} = {};
const root = {
getMessage: ({ id }: { id: string }) => {
if (!inmemoryDB[id]) {
throw new Error('no message exists with id ' id);
}
return new Message(id, inmemoryDB[id]);
},
createMessage: ({ input }: { input: MessageInfo }) => {
const id = crypto.randomUUID();
inmemoryDB[id] = input;
return new Message(id, input);
},
updateMessage: ({ id, input }: { id: string; input: MessageInput }) => {
if (!inmemoryDB[id]) {
throw new Error('no message exists with id ' id);
}
inmemoryDB[id] = input;
return new Message(id, input);
},
};
const app = express();
app.use(
'/graphql',
graphqlHTTP({
schema,
rootValue: root,
graphiql: true,
})
);
app.listen(4001, () => {
console.log('Running a GraphQL API server2 at localhost:4001/graphql');
});
Authentication and Express Middleware
结合 express-graphql,可以轻松地使用 Express 中间件。
此外,在解析器中,可以通过第二个参数访问请求(request)。。
代码语言:ts复制import express from 'express';
import { graphqlHTTP } from 'express-graphql';
import { buildSchema } from 'graphql';
var schema = buildSchema(`
type Query {
ip: String
}
`);
const loggingMiddleware = (
req: express.Request,
res: express.Response,
next: express.NextFunction
) => {
console.log('ip:', req.ip);
next();
};
const root = {
ip: (_args: any, context: express.Request) => {
// 由于没有指定 graphqlHTTP 的 context 选项
// express.Request 的值将传递给第二个参数
return context.ip;
},
};
const app = express();
app.use(loggingMiddleware);
app.use(
'/graphql',
graphqlHTTP({
schema,
rootValue: root,
graphiql: true,
// 传递给解析器的第二个参数。如果不指定,则传递 request 对象
// context: { hoge: 'context' },
})
);
app.listen(4000);
console.log('Running a GraphQL API server at localhost:4000/graphql');
简要尝试了一下教程。