Chapter 08

Mutationのリゾルバを実装する

Eringi_V3
Eringi_V3
2021.09.16に更新
このチャプターの目次

ここまでで認証 + ユーザーの作成処理ができました。
次に、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;