概要
GraphqlのdirectiveについてやCustom directiveの導入方法のまとめ
ディレクティブとは
GraphQLのtype, field, 引数などに付与するデコレータで、必要に応じてカスタムロジックを実行できます。
例えばGraphQLには@deprecated
というディレクティブが組み込まれていて、装飾した箇所が非推奨だと知らせることができます。
type User {
name: String
age: Int
gender: String @deprecated(reason: "このようにコメントを付与できます")
}
type Query {
user: User
}
他には@skip
や@include
などがある
Playground
ディレクディブ | 内容 |
---|---|
@deprecated | 型の非推奨フィールドや非推奨の列挙値など |
@skip | 条件付きでクエリに含めないことができます。 |
@include | 条件付きでクエリに含めることができます。 |
カスタムディレクティブ
ディレクティブを独自に定義し、付与できます。
使用するライブラリーごとに実装方法が少し違っているので注意が必要です。
Apolloによるスキーマファーストでの実装例
nestjsの例
TypeGraphQLの例
- https://typegraphql.com/docs/directives.html#providing-the-implementation
- https://typegraphql.com/docs/emit-schema.html#emit-schema-with-custom-directives
サンプル
@auth
というリクエストのトークンを取得しトークン認証をするカスタムディレクティブ
authDirective.ts
const authDirectiveTransformer = (
directiveName: string
) => {
return {
authDirectiveTypeDefs: `directive @${directiveName} on FIELD_DEFINITION`,
authDirective: (schema: GraphQLSchema) => mapSchema(schema, {
[MapperKind.OBJECT_FIELD]: (fieldConfig) => {
const directive = getDirective(
schema,
fieldConfig,
directiveName
)?.[0];
if (directive) {
const originalResolve = fieldConfig.resolve || defaultFieldResolver;
fieldConfig.resolve = async function (
source,
args,
context,
info
) {
// contextの情報が取得できる
const token = context.token;
// トークンチェックなど...
const isAuthorized = false
if (!isAuthorized) {
throw new Error("Unauthrized");
}
return originalResolve(source, args, context, info)
};
return fieldConfig;
}
}
})
}
};
index.ts
const typeDefs = gql`
type Address {
region: String
city: String
}
type User {
name: String
age: Int
gender: String @deprecated(reason: "このようにコメントを付与できます")
address: Address
}
type Query {
user: User @auth
}
`;
const resolvers = {
Query: {
user: () => ({
name: "taro",
age: 25,
gender: "male",
address: {
region: "tokyo",
city: "shibuya"
}
})
},
};
const { authDirective, authDirectiveTypeDefs } = authDirectiveTransformer("auth")
const executableSchema = makeExecutableSchema({
typeDefs: [
authDirectiveTypeDefs,
typeDefs
],
resolvers,
});
const schema = authDirective(executableSchema)
const server = new ApolloServer({
schema,
context: ({ req }) => {
return {
token: req.headers["authentication-token"] || "",
};
},
plugins: [ApolloServerPluginLandingPageGraphQLPlayground()]
});
おわりに
今回は認証しかカスタムディレクティブを使用しませんでしたが、他にこういったことで使うといいよってのがありましたらぜひ教えていただきたいです!