与我们一起参加10月8日至10日在纽约市的活动,了解最新的技巧、趋势和有关 GraphQL 联盟和 API 平台工程的消息。加入我们,在纽约市参加 GraphQL Summit 2024
文档
免费开始

自定义标量


The规范包括默认的类型Int, Float, String, BooleanID。虽然这些涵盖了大多数用例,但某些应用程序需要支持其他原子数据类型(例如Date)或在现有类型中添加验证。为此,您可以为标量类型定义自定义类型。

定义自定义标量

要定义一个自定义标量,您可以将其添加到您的模式中,如下所示:

scalar MyCustomScalar

现在您可以在任何可以使用默认标量的地方使用MyCustomScalar(例如:作为对象、输入类型字段或)的类型。

包含标量规范

更新于2021年10月的GraphQL规范版本中,您可以将@specifiedBy指令用作元数据,供模式消费者了解标量使用的格式。此指令不提供自动验证,但可以为人阅读模式提供有用的上下文。

scalar MyCustomScalar @specifiedBy(url: "https://specs.example.com/rfc111")

然而,仍然需要知道如何交互和生成此新标量类型的值。

定义自定义标量逻辑

在你定义了一个自定义标量类型之后,你需要定义Apollo Server如何与之交互。特别是,你需要定义:

  • 标量的值在您的后端如何表示
    • 这通常是后端数据存储驱动程序使用的表示。
  • 如何将值的后端表示序列化到兼容JSON的类型
  • 如何将兼容JSON的表示反序列化到后端表示

你定义这些交互在一个GraphQLScalarType类的实例中。

有关graphql库的的更多信息,请参阅官方文档

示例:Date标量

注意

下面的代码块默认使用TypeScript。您可以通过每个代码块上方的下拉菜单将其切换到JavaScript。

如果您使用的是JavaScript,请使用.js.jsx文件

以下GraphQLScalarType对象定义了一个表示日期的自定义标量(这是最常见的自定义标量之一)的交互。它假定我们的后端使用JavaScript对象Date表示日期。

import { GraphQLScalarType, Kind } from 'graphql';
const dateScalar = new GraphQLScalarType({
name: 'Date',
description: 'Date custom scalar type',
serialize(value) {
if (value instanceof Date) {
return value.getTime(); // Convert outgoing Date to integer for JSON
}
throw Error('GraphQL Date Scalar serializer expected a `Date` object');
},
parseValue(value) {
if (typeof value === 'number') {
return new Date(value); // Convert incoming integer to Date
}
throw new Error('GraphQL Date Scalar parser expected a `number`');
},
parseLiteral(ast) {
if (ast.kind === Kind.INT) {
// Convert hard-coded AST string to integer and then to Date
return new Date(parseInt(ast.value, 10));
}
// Invalid hard-coded value (not an integer)
return null;
},
});
import { GraphQLScalarType, Kind } from 'graphql';
const dateScalar = new GraphQLScalarType({
name: 'Date',
description: 'Date custom scalar type',
serialize(value) {
if (value instanceof Date) {
return value.getTime(); // Convert outgoing Date to integer for JSON
}
throw Error('GraphQL Date Scalar serializer expected a `Date` object');
},
parseValue(value) {
if (typeof value === 'number') {
return new Date(value); // Convert incoming integer to Date
}
throw new Error('GraphQL Date Scalar parser expected a `number`');
},
parseLiteral(ast) {
if (ast.kind === Kind.INT) {
// Convert hard-coded AST string to integer and then to Date
return new Date(parseInt(ast.value, 10));
}
// Invalid hard-coded value (not an integer)
return null;
},
});

这个初始化定义了以下方法:

  • serialize
  • parseValue
  • parseLiteral

这些方法共同描述了 Apollo Server 在每个场景中如何与标量交互。

serialize

serialize 方法将标量的后端表示形式转换为 JSON 兼容格式,以便 Apollo Server 可以将其包含在操作响应中。

在上面的示例中,日期标量在后端通过 JavaScript 对象代表。当我们发送 GraphQL 响应中的日期标量时,我们将其序列化为 JavaScript Date 对象的 getTime() 函数返回的整数值。

请注意,Apollo Client 不能自动解析自定义标量(参见问题),因此您的客户端必须定义自定义逻辑来按需反序列化此值。

parseValue

parseValue 方法在将其添加到解析器的 args 之前将标量的 JSON 值转换为后端表示形式。

当客户端通过 GraphQL 变量GraphQL variable提供标量(scalar)时,Apollo Server 会调用这个方法。

parseLiteral

当传入的 字符串将标量作为硬编码的 值时,该值将是查询 的抽象语法树(AST)的一部分。Apollo Server 会调用 parseLiteral 方法将值的 AST 表示形式转换为标量的后端表示形式。

在上面的例子中,parseLiteral 将从字符串到整数的 AST 值进行转换,然后转换为 Date 对象以匹配 parseValue 的结果。

为 Apollo Server 提供自定义标量

注意

在下面的例子中,我们使用顶层 await 调用来异步启动我们的服务器。如果您想查看我们如何设置,请查看 入门指南的详细信息。

在定义您的 GraphQLScalarType 实例后,您可以将其包含在同一个 解析器映射 中,该映射包含了您的模式中其他类型和 的解析器:

import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
import { GraphQLScalarType, Kind } from 'graphql';
const typeDefs = `#graphql
/* highlight-line */ scalar Date
type Event {
id: ID!
date: Date!
}
type Query {
events: [Event!]
}
`;
const dateScalar = new GraphQLScalarType({
// See definition above
});
const resolvers = {
Date: dateScalar,
// ...other resolver definitions...
};
const server = new ApolloServer({
typeDefs,
resolvers,
});
const { url } = await startStandaloneServer(server);
console.log(`🚀 Server listening at: ${url}`);
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
import { GraphQLScalarType } from 'graphql';
const typeDefs = `#graphql
/* highlight-line */ scalar Date
type Event {
id: ID!
date: Date!
}
type Query {
events: [Event!]
}
`;
const dateScalar = new GraphQLScalarType({
// See definition above
});
const resolvers = {
Date: dateScalar,
// ...other resolver definitions...
};
const server = new ApolloServer({
typeDefs,
resolvers,
});
const { url } = await startStandaloneServer(server);
console.log(`🚀 Server listening at: ${url}`);

示例:将整数限制为奇数

在此示例中,我们创建了一个名为 Odd 的自定义标量,它只能包含奇数整数:

index.ts
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
import { GraphQLScalarType, Kind, GraphQLError } from 'graphql';
// Basic schema
const typeDefs = `#graphql
scalar Odd
type Query {
# Echoes the provided odd integer
echoOdd(odd: Odd!): Odd!
}
`;
// Validation function for checking "oddness"
function oddValue(value: unknown) {
if (typeof value === 'number' && Number.isInteger(value) && value % 2 !== 0) {
return value;
}
throw new GraphQLError('Provided value is not an odd integer', {
extensions: { code: 'BAD_USER_INPUT' },
});
}
const resolvers = {
Odd: new GraphQLScalarType({
name: 'Odd',
description: 'Odd custom scalar type',
parseValue: oddValue,
serialize: oddValue,
parseLiteral(ast) {
if (ast.kind === Kind.INT) {
return oddValue(parseInt(ast.value, 10));
}
throw new GraphQLError('Provided value is not an odd integer', {
extensions: { code: 'BAD_USER_INPUT' },
});
},
}),
Query: {
echoOdd(_, { odd }) {
return odd;
},
},
};
const server = new ApolloServer({
typeDefs,
resolvers,
});
const { url } = await startStandaloneServer(server);
console.log(`🚀 Server listening at: ${url}`);
index.js
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
import { GraphQLScalarType, Kind, GraphQLError } from 'graphql';
// Basic schema
const typeDefs = `#graphql
scalar Odd
type Query {
# Echoes the provided odd integer
echoOdd(odd: Odd!): Odd!
}
`;
// Validation function for checking "oddness"
function oddValue(value) {
if (typeof value === 'number' && Number.isInteger(value) && value % 2 !== 0) {
return value;
}
throw new GraphQLError('Provided value is not an odd integer', {
extensions: { code: 'BAD_USER_INPUT' },
});
}
const resolvers = {
Odd: new GraphQLScalarType({
name: 'Odd',
description: 'Odd custom scalar type',
parseValue: oddValue,
serialize: oddValue,
parseLiteral(ast) {
if (ast.kind === Kind.INT) {
return oddValue(parseInt(ast.value, 10));
}
throw new GraphQLError('Provided value is not an odd integer', {
extensions: { code: 'BAD_USER_INPUT' },
});
},
}),
Query: {
echoOdd(_, { odd }) {
return odd;
},
},
};
const server = new ApolloServer({
typeDefs,
resolvers,
});
const { url } = await startStandaloneServer(server);
console.log(`🚀 Server listening at: ${url}`);

导入第三方自定义标量

如果其他库定义了自定义标量,您可以按与其他符号一样的方式导入并使用它。

例如,graphql-type-json库定义了GraphQLJSON对象,它是GraphQLScalarType的一个实例。您可以使用该对象定义一个接受任何有效JSON值的JSON标量。

首先,安装库

$ npm install graphql-type-json

然后导入GraphQLJSON对象并将其添加到解析器映射中,就像平常一样:

import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
import GraphQLJSON from 'graphql-type-json';
const typeDefs = `#graphql
scalar JSON
type MyObject {
myField: JSON
}
type Query {
objects: [MyObject]
}
`;
const resolvers = {
JSON: GraphQLJSON,
// ...other resolvers...
};
const server = new ApolloServer({
typeDefs,
resolvers,
});
const { url } = await startStandaloneServer(server);
console.log(`🚀 Server listening at: ${url}`);
上一页
联合与接口
下一页
指令
评分文章评分在GitHub上编辑编辑论坛Discord

©2024Apollo Graph Inc., Apollo GraphQL有限公司。

隐私政策

公司