このチャプターの目次
ここまでで認証 + ユーザーの作成処理ができました。
次に、Todoアプリに必要な残りのMutationを実装していきましょう。
AddTodo
認可処理は各リゾルバごとに実装していきます。
もっといいやり方(スキーマレベルで宣言的に認可を実装等)があるかもしれませんが、今回は一番シンプルだと思われるこのやり方で行きます。
contextのユーザーID(リクエストしてきたユーザーのID)が存在しない場合はエラーにします。
つまり、未ログインの場合はTodoを作成できないことになります。
ユーザーIDが存在する場合はuserIdに紐づけてTodoのレコードを作成します。
prismaクライアントではincludeプロパティでrelationしている値も取得することが可能です。
src/resolvers/mutation/addTodo.ts
import { prisma } from '../../lib/prisma';
import { MutationResolvers } from '../../types/generated/graphql';
export const addTodo: MutationResolvers['addTodo'] = async (
parent,
args,
context,
info
) => {
const userId = context.user?.id;
if (!userId) {
throw new Error('Authrization Error.');
}
const todo = await prisma.todo.create({
data: {
title: args.input.title,
status: 'pending',
userId: userId,
},
include: {
user: true,
},
});
return todo;
};
UpdateTodo
contextのuserIdと編集対象のTodoに紐づくuserIdが一致しない場合はエラーとし、本人以外は編集が不可能とします。
src/resolvers/mutation/updateTodo.ts
import { prisma } from '../../lib/prisma';
import { MutationResolvers } from '../../types/generated/graphql';
export const updateTodo: MutationResolvers['updateTodo'] = async (
parent,
args,
context,
info
) => {
const userId = context.user?.id;
if (!userId) {
throw new Error('Authentication Error.');
}
const targetTodo = await prisma.todo.findUnique({
where: {
id: args.id,
},
});
if (!targetTodo) {
throw new Error('Not Found Todo.');
}
if (targetTodo.userId !== userId) {
throw new Error('Authorization Error.');
}
const todo = await prisma.todo.update({
where: {
id: args.id,
},
data: {
title: args.input.title,
status: args.input.status,
},
include: {
user: true,
},
});
return todo;
};
DeleteTodo
こちらもUpdateTodoと同様に本人以外は削除が不可能なようにします。
src/resolvers/mutation/deleteTodo
import { prisma } from '../../lib/prisma';
import { MutationResolvers } from '../../types/generated/graphql';
export const deleteTodo: MutationResolvers['deleteTodo'] = async (
parent,
args,
context,
info
) => {
const userId = context.user?.id;
if (!userId) {
throw new Error('Authentication Error.');
}
const targetTodo = await prisma.todo.findUnique({
where: {
id: args.id,
},
});
if (!targetTodo) {
throw new Error('Not Found Todo.');
}
if (targetTodo.userId !== userId) {
throw new Error('Authorization Error.');
}
const todo = await prisma.todo.delete({
where: {
id: args.id,
},
include: {
user: true,
},
});
return todo;
};
UpdateUser
src/resolvers/mutation/updateUser
import { prisma } from '../../lib/prisma';
import { MutationResolvers } from '../../types/generated/graphql';
export const updateUser: MutationResolvers['updateUser'] = async (
parent,
args,
context,
info
) => {
const userId = context.user?.id;
if (!userId) {
throw new Error('Authentication Error.');
}
const user = await prisma.user.findUnique({
where: {
id: userId,
},
});
if (!user) {
throw new Error('Not Found Error.');
}
const updatedUser = await prisma.user.update({
where: {
id: userId,
},
data: {
name: args.input.name,
},
});
return updatedUser;
};
最後に各mutationをまとめて再exportしておきます。
src/resolvers/mutation/index.ts
export { addTodo } from './addTodo';
export { createUser } from './createUser';
export { deleteTodo } from './deleteTodo';
export { updateTodo } from './updateTodo';
export { updateUser } from './updateUser';
src/resolvers/index.ts
import { Resolvers } from '../types/generated/graphql';
import * as mutaiton from './mutation/';
import { dateScalar } from './scalar/date';
const resolvers: Resolvers = {
Query: {
getUser: () => null,
getTodos: () => [],
getTodoById: () => null,
},
Mutation: mutaiton,
Date: dateScalar,
};
export default resolvers;