前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >尝试使用官方教程学习 GraphQL

尝试使用官方教程学习 GraphQL

原创
作者头像
種法龍
发布2023-12-31 15:43:05
1340
发布2023-12-31 15:43:05
举报
文章被收录于专栏:未知未知
  • 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:字符串型 → string
  • Int:整数型 → number
  • Float:浮点数型 → number
  • Boolean:布尔型 → boolean
  • ID:唯一标识符 → string
    • GraphQL 客户端似乎会根据此ID执行缓存等操作

所有基本类型都是可空的。

如果不允许为 Null,则在末尾添加 !(例如,String!)。

对于列表类型,使用 [] 括起来(例如,String)。

代码语言:ts
复制
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');

简要尝试了一下教程。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • GraphQL 概要
  • 官方教程(JavaScript)
    • 入门指南
      • Running an Express GraphQL Server
        • GraphQL Clients
          • Basic Types
            • Passing Arguments
              • Object Type
                • Mutations and Input Types
                  • Authentication and Express Middleware
                  相关产品与服务
                  消息队列 TDMQ
                  消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档