ToDoアプリの作成（Task編）

<h2>はじめに</h2>
今回は，前回に続きタスクの取得、特定のIDのタスクを取得、タスクの新規作成、更新、削除機能を実装していきます。<br>

https://qiita.com/sawadashinji0526/items/7492f19b868833cab343

※動作確認にpgAdminとPostmanを使います。

<h2>リポジトリの実装</h2>
このコードは、タスク情報を扱うためのリポジトリインターフェースと、そのインターフェースを実装する具体的なリポジトリ構造体を定義しています。<br>

```repository/task_repository.go
package repository

import (
	"fmt"
	"go-rest-api/model"

	"gorm.io/gorm"
	"gorm.io/gorm/clause"
)

type ITaskRepository interface {
    //ユーザーのすべてのタスクを取得するメソッドです。
	GetAllTasks(tasks *[]model.Task, userId uint) error
    // 特定のIDのタスクを取得するメソッドです。
	GetTaskById(task *model.Task, userId uint, taskId uint) error
    // 新しいタスクを作成するメソッドです。
	CreateTask(task *model.Task) error
    //タスクを更新するメソッドです。
	UpdateTask(task *model.Task, userId uint, taskId uint) error
    //タスクを削除するメソッドです。
	DeleteTask(userId uint, taskId uint) error
}

type taskRepository struct {
    //このフィールドは、データベース接続 (`gorm.DB` オブジェクト) を保持します。
	db *gorm.DB
}

func NewTaskRepository(db *gorm.DB) ITaskRepository {
    //`taskRepository` インスタンスを作成し、データベース接続を `db` フィールドに設定して返します。
	return &taskRepository{db}
}

func (tr *taskRepository) GetAllTasks(tasks *[]model.Task, userId uint) error {
    //gorm.DB の Joins、Where、Order、Find、Error メソッドを使って、指定されたユーザーIDのタスクをデータベースから取得します。
    //Joins("User"): ユーザーとタスクの関係を定義します。
    //Where("user_id=?", userId): ユーザーIDを指定してタスクを絞り込みます。
    //Order("created_at"): 作成日時順にソートします。
    //Find(tasks): 取得したタスクを tasks スライスに格納します。
	if err := tr.db.Joins("User").Where("user_id=?", userId).Order("created_at").Find(tasks).Error; err != nil {
		return err
	}
	return nil
}

func (tr *taskRepository) GetTaskById(task *model.Task, userId uint, taskId uint) error {
    //gorm.DB の Joins、Where、First、Errorメソッドを使って、指定されたユーザーIDとタスクIDのタスクをデータベースから取得します。
    //First(task, taskId): 取得したタスクをtask構造体に格納します。
	if err := tr.db.Joins("User").Where("user_id=?", userId).First(task, taskId).Error; err != nil {
		return err
	}
	return nil
}

func (tr *taskRepository) CreateTask(task *model.Task) error {
    //新しいタスクを作成するメソッドの実装です。
    //gorm.DBのCreate メソッドを使って、新しいタスクレコードを作成します。
	if err := tr.db.Create(task).Error; err != nil {
		return err
	}
	return nil
}

func (tr *taskRepository) UpdateTask(task *model.Task, userId uint, taskId uint) error {
    //gorm.DBのModel、Clauses、Where、Update、Error メソッドを使って、指定されたIDとユーザーIDのタスクを更新します
    //Model(task): 更新対象のタスクを指定します。
    //Clauses(clause.Returning{}): 更新されたレコードの情報を取得します。
    //Where("id=? AND user_id=?", taskId, userId): 更新対象のタスクレコードを、taskIdとuserIdで絞り込みます。
    //Update("title", task.Title): task 構造体のTitleフィールドの値を、データベースのタスクレコードのtitle列に更新します。
	result := tr.db.Model(task).Clauses(clause.Returning{}).Where("id=? AND user_id=?", taskId, userId).Update("title", task.Title)
    //if result.Error != nil { return result.Error }: データベース更新処理中にエラーが発生した場合、エラーオブジェクトresult.Errorを返します。
	if result.Error != nil {
		return result.Error
	}
    //更新されたレコードがなかった場合、更新対象のレコードが存在しないことを示すエラーオブジェクトを返します。
	if result.RowsAffected < 1 {
		return fmt.Errorf("object does not exist")
	}
	return nil
}

func (tr *taskRepository) DeleteTask(userId uint, taskId uint) error {
    //データベースからタスクレコードを削除する処理を実行します。
    //gorm.DB の Where、Delete、Error メソッドを使って、指定されたIDとユーザーIDのタスクを削除します。
    //Where("id=? AND user_id=?", taskId, userId): 削除対象のタスクレコードを、taskIdとuserIdで絞り込みます。
    //Delete(&model.Task{}): gorm.DBのDeleteメソッドを使って、指定された条件に合致するタスクレコードを削除します。
	result := tr.db.Where("id=? AND user_id=?", taskId, userId).Delete(&model.Task{})
    //if result.Error != nil { return result.Error }: データベース削除処理中にエラーが発生した場合、エラーオブジェクト result.Error を返します。
	if result.Error != nil {
		return result.Error
	}
    //更新されたレコードがなかった場合、更新対象のレコードが存在しないことを示すエラーオブジェクトを返します。
	if result.RowsAffected < 1 {
		return fmt.Errorf("object does not exist")
	}
	return nil
}
```

<h2>ユースケースの実装</h2>
次はユースケースを作成していきます。<br>
タスクの取得、特定のIDのタスクを取得、タスクの新規作成、更新、削除を行うユースケースを実装したものです。

```usecase/task_usecase.go
package usecase

import (
	"go-rest-api/model"
	"go-rest-api/repository"
)

//タスクユースケースが実装すべきメソッドを定義するインターフェースです。
type ITaskUsecase interface {
    //ユーザーのすべてのタスクを取得するメソッドです。
	GetAllTasks(userId uint) ([]model.TaskResponse, error)
    //特定のIDのタスクを取得するメソッドです。
	GetTaskById(userId uint, taskId uint) (model.TaskResponse, error)
    //新しいタスクを作成するメソッドです。
	CreateTask(task model.Task) (model.TaskResponse, error)
    //タスクを更新するメソッドです。
	UpdateTask(task model.Task, userId uint, taskId uint) (model.TaskResponse, error)
    //タスクを削除するメソッドです。
	DeleteTask(userId uint, taskId uint) error
}

//タスクリポジトリへの依存関係を保持するフィールドです。
type taskUsecase struct {
	tr repository.ITaskRepository
}

//taskUsecase構造体のインスタンスを生成する関数です。
func NewTaskUsecase(tr repository.ITaskRepository) ITaskUsecase {
	return &taskUsecase{tr}
}

func (tu *taskUsecase) GetAllTasks(userId uint) ([]model.TaskResponse, error) {
    //tasks := []model.Task{} は、データベースから取得したタスクのリストを格納する model.Task 型のスライスtasksを空で初期化します。
	tasks := []model.Task{}
    //タスクリポジトリのGetAllTasksメソッドを呼び出して、ユーザーのすべてのタスクを取得します。
    //GetAllTasks(&tasks, userId): タスクリポジトリのGetAllTasksメソッドは、データベースからユーザーのすべてのタスクを取得し、tasksスライスに格納します。
	if err := tu.tr.GetAllTasks(&tasks, userId); err != nil {
		return nil, err
	}
    //resTasks を空で初期化。
	resTasks := []model.TaskResponse{}
    //取得したタスクのリストtasksをループ処理し、model.TaskResponse型のオブジェクトに変換します。
	for _, v := range tasks {
        //model.TaskResponse 型の新しいオブジェクトを作成し、取得したタスクの情報 (v.ID, v.Title, v.CreatedAt, v.UpdatedAt) を設定します。
		t := model.TaskResponse{
			ID:        v.ID,
			Title:     v.Title,
			CreatedAt: v.CreatedAt,
			UpdatedAt: v.UpdatedAt,
		}
        //resTasks = append(resTasks, t) は、変換されたmodel.TaskResponseオブジェクトを resTasks スライスに追加します。
		resTasks = append(resTasks, t)
	}
    //取得したタスクのリストresTasksとエラーオブジェクトnilを返します。
	return resTasks, nil
}

func (tu *taskUsecase) GetTaskById(userId uint, taskId uint) (model.TaskResponse, error) {
    //データベースから取得したタスクの情報を格納するmodel.Task型の変数taskを空で初期化します。
	task := model.Task{}
    //タスクリポジトリのGetTaskByIdメソッドを呼び出して、特定のユーザーの特定のタスクを取得します。
    //タスクリポジトリのGetTaskByIdメソッドは、データベースから指定されたユーザーIDとタスクIDのタスクを取得し、task変数に格納します。
	if err := tu.tr.GetTaskById(&task, userId, taskId); err != nil {
		return model.TaskResponse{}, err
	}
    //model.TaskResponse型の新しいオブジェクトを作成し、取得したタスクの情報 (task.ID, task.Title, task.CreatedAt, task.UpdatedAt) を設定します。
	resTask := model.TaskResponse{
		ID:        task.ID,
		Title:     task.Title,
		CreatedAt: task.CreatedAt,
		UpdatedAt: task.UpdatedAt,
	}
    //取得したタスクの情報を含むmodel.TaskResponseオブジェクトとエラーオブジェクトnilを返します。
	return resTask, nil
}

func (tu *taskUsecase) CreateTask(task model.Task) (model.TaskResponse, error) {
    //タスクリポジトリのCreateTaskメソッドを呼び出して、新しいタスクを作成します。
    //タスクリポジトリのCreateTaskメソッドは、データベースに新しいタスクレコードを作成します。
    //タスク作成処理中にエラーが発生した場合、エラーを返します。
	if err := tu.tr.CreateTask(&task); err != nil {
		return model.TaskResponse{}, err
	}
    //作成されたタスクの情報を格納するmodel.TaskResponse型の新しいオブジェクトを作成し、作成されたタスクの情報 (task.ID, task.Title, task.CreatedAt, task.UpdatedAt) を設定します。
	resTask := model.TaskResponse{
		ID:        task.ID,
		Title:     task.Title,
		CreatedAt: task.CreatedAt,
		UpdatedAt: task.UpdatedAt,
	}
    /作成されたタスクの情報を含むmodel.TaskResponseオブジェクトとエラーオブジェクトnilを返します。
	return resTask, nil
}

func (tu *taskUsecase) UpdateTask(task model.Task, userId uint, taskId uint) (model.TaskResponse, error) {
    //タスクリポジトリのUpdateTaskメソッドは、データベースのタスクレコードを更新します。
    //タスク更新処理中にエラーが発生した場合、エラーを返します。
	if err := tu.tr.UpdateTask(&task, userId, taskId); err != nil {
		return model.TaskResponse{}, err
	}
    //更新されたタスクの情報を格納するmodel.TaskResponse型の新しいオブジェクトを作成し、更新されたタスクの情報 (task.ID, task.Title, task.CreatedAt, task.UpdatedAt) を設定します。
	resTask := model.TaskResponse{
		ID:        task.ID,
		Title:     task.Title,
		CreatedAt: task.CreatedAt,
		UpdatedAt: task.UpdatedAt,
	}
    //更新されたタスクの情報を含むmodel.TaskResponseオブジェクトとエラーオブジェクト nil を返します。
	return resTask, nil
}

func (tu *taskUsecase) DeleteTask(userId uint, taskId uint) error {
    //eleteTask(userId, taskId): タスクリポジトリのDeleteTaskメソッドは、データベースのタスクレコードを削除します。
    //タスク削除処理中にエラーが発生した場合、エラーを返します。
	if err := tu.tr.DeleteTask(userId, taskId); err != nil {
		return err
	}
    //エラーが発生しなかった場合、return nilで、エラーオブジェクトnilを返します。これは、正常に処理が完了したことを示します。
	return nil
}
```

<h2>コントローラの実装</h2>
下記のコードは、タスクの取得、特定のIDのタスクを取得、タスクの新規作成、更新、削除を処理を行うためのコントローラを定義しています。

```controller/task_controller.go
package controller

import (
	"go-rest-api/model"
	"go-rest-api/usecase"
	"net/http"
	"strconv"

	"github.com/golang-jwt/jwt/v4"
	"github.com/labstack/echo/v4"
)

type ITaskController interface {
    //ユーザーのすべてのタスクを取得するメソッドです。
	GetAllTasks(c echo.Context) error
    //特定のIDのタスクを取得するメソッドです。
	GetTaskById(c echo.Context) error
    //新しいタスクを作成するメソッドです。
	CreateTask(c echo.Context) error
    //タスクを更新するメソッドです。
	UpdateTask(c echo.Context) error
    //タスクを削除するメソッドです。
	DeleteTask(c echo.Context) error
}

type taskController struct {
    //タスクユースケースへの依存関係を保持するフィールドです。
	tu usecase.ITaskUsecase
}
    
    //タスクユースケースオブジェクトを受け取り、userController構造体のインスタンスを生成して返します。
func NewTaskController(tu usecase.ITaskUsecase) ITaskController {
	return &taskController{tu}
}

func (tc *taskController) GetAllTasks(c echo.Context) error {
    //echo.Contextオブジェクトcからuserキーで格納された認証情報を取得します。
	user := c.Get("user").(*jwt.Token)
    //取得した認証情報userから、jwt.MapClaims型のペイロードデータを取得します。
	claims := user.Claims.(jwt.MapClaims)
    //ペイロードデータから user_id の値を取得します。
	userId := claims["user_id"]
    //タスクユースケースの GetAllTasks メソッドは、ユーザーIDを受け取り、そのユーザーのすべてのタスクを[]model.TaskResponse型のリストとして返します。
	tasksRes, err := tc.tu.GetAllTasks(uint(userId.(float64)))
    //エラーが発生した場合、HTTP ステータスコード 500 (Internal Server Error)と、エラーメッセージをJSON形式で返します。
	if err != nil {
		return c.JSON(http.StatusInternalServerError, err.Error())
	}
    //タスクの取得が成功した場合、HTTP ステータスコード 200 (OK) と、取得したタスクのリスト (tasksRes) をJSON形式で返します。
	return c.JSON(http.StatusOK, tasksRes)
}

func (tc *taskController) GetTaskById(c echo.Context) error {
    //echo.Contextオブジェクトcからuserキーで格納された認証情報を取得します。
	user := c.Get("user").(*jwt.Token)
    //取得した認証情報userから、jwt.MapClaims型のペイロードデータを取得します。
	claims := user.Claims.(jwt.MapClaims)
    //ペイロードデータからuser_idの値を取得します。
	userId := claims["user_id"]
    //リクエストパスからtaskIdパラメータを取得します。これは、echo.ContextのParam メソッドを使って取得します。
	id := c.Param("taskId")
    //taskIdパラメータを整数型に変換します。
	taskId, _ := strconv.Atoi(id)
    //タスクユースケースの GetTaskByIdメソッドは、ユーザーIDとタスクIDを受け取り、そのタスクの情報をmodel.TaskResponse型のオブジェクトとして返します。
	taskRes, err := tc.tu.GetTaskById(uint(userId.(float64)), uint(taskId))
    //タスクの取得処理中にエラーが発生した場合、エラーを返します。
	if err != nil {
        //エラーが発生した場合、HTTP ステータスコード 500 (Internal Server Error) と、エラーメッセージをJSON形式で返します。
		return c.JSON(http.StatusInternalServerError, err.Error)
	}
    //タスクの取得が成功した場合、HTTP ステータスコード 200 (OK) と、取得したタスクの情報をJSON形式で返します。
	return c.JSON(http.StatusOK, taskRes)
}

func (tc *taskController) CreateTask(c echo.Context) error {
    //echo.Context オブジェクトcからuserキーで格納された認証情報を取得します。
	user := c.Get("user").(*jwt.Token)
    //取得した認証情報userから、jwt.MapClaims型のペイロードデータを取得します。
	claims := user.Claims.(jwt.MapClaims)
    //ペイロードデータからuser_idの値を取得します。
	userId := claims["user_id"]
    //新しいタスクの情報を格納するmodel.Task構造体を空で作成します。
	task := model.Task{}
    //c.Bind(&task) は、EchoフレームワークのコンテキストオブジェクトcのBindメソッドを呼び出し、リクエストボディからデータを取得して、task構造体にバインドします。
    //バインド処理中にエラーが発生した場合、エラーを返します。この場合、ステータスコード 400 Bad RequestとともにエラーメッセージをJSON形式で返します。
	if err := c.Bind(&task); err != nil {
		return c.JSON(http.StatusBadRequest, err.Error())
	}
    // 取得したユーザーIDを、task構造体のUserIdフィールドに設定します。
    //タスクユースケースのCreateTaskメソッドは、新しいタスクを作成します。
	task.UserId = uint(userId.(float64))
    //タスクの作成処理が成功した場合、taskRes変数には、作成されたタスクの情報を格納するmodel.TaskResponse構造体が格納されます。
    //タスクの作成処理中にエラーが発生した場合、err変数には、エラーオブジェクトが格納されます。
	taskRes, err := tc.tu.CreateTask(task)
    //タスクの作成処理中にエラーが発生した場合、HTTPステータスコード 500 (Internal Server Error) を返します。
	if err != nil {
		return c.JSON(http.StatusInternalServerError, err.Error())
	}
    //タスクの作成が成功した場合、HTTPステータスコード 201 (Created) と、作成されたタスクの情報をJSON形式で返します。
	return c.JSON(http.StatusCreated, taskRes)
}

func (tc *taskController) UpdateTask(c echo.Context) error {
    //c.Get("user").(*jwt.Token): echo.Contextオブジェクトcからuserキーで格納された認証情報を取得します。
	user := c.Get("user").(*jwt.Token)
    //取得した認証情報 user から、jwt.MapClaims型のペイロードデータを取得します。
	claims := user.Claims.(jwt.MapClaims)
    //ペイロードデータからuser_idの値を取得します。
	userId := claims["user_id"]
    //リクエストパスからtaskIdパラメータを取得します。これは、echo.ContextのParamメソッドを使って取得します。
	id := c.Param("taskId")
    //taskId パラメータを整数型に変換します。これは、strconv.Atoi 関数を使って行います。
	taskId, _ := strconv.Atoi(id)
    //更新するタスクの情報を格納するmodel.Task構造体を空で作成します。
	task := model.Task{}
    //c.Bind(&task) は、EchoフレームワークのコンテキストオブジェクトcのBindメソッドを呼び出し、リクエストボディからデータを取得して、task構造体にバインドします。
    //バインド処理中にエラーが発生した場合、エラーを返します。この場合、ステータスコード 400 Bad Request とともにエラーメッセージをJSON形式で返します。
	if err := c.Bind(&task); err != nil {
		return c.JSON(http.StatusBadRequest, err.Error())
	}
    //タスクユースケースの UpdateTask メソッドは、ユーザーID、タスクID、更新するタスク情報を渡して、タスクの更新処理を実行します。
    //更新処理が成功した場合、taskRes変数には、更新されたタスクの情報を格納する model.TaskResponse 構造体が格納されます。
    //更新処理中にエラーが発生した場合、err 変数には、エラーオブジェクトが格納されます。
	taskRes, err := tc.tu.UpdateTask(task, uint(userId.(float64)), uint(taskId))
    //更新処理中にエラーが発生した場合、HTTPステータスコード 500 (Internal Server Error) を返します。
	if err != nil {
		return c.JSON(http.StatusInternalServerError, err.Error())
	}
    //タスクの更新が成功した場合、HTTP ステータスコード 200 (OK) と、更新されたタスクの情報をJSON形式で返します。
	return c.JSON(http.StatusOK, taskRes)
}

func (tc *taskController) DeleteTask(c echo.Context) error {
    //echo.Context オブジェクトcからuserキーで格納された認証情報を取得します。
	user := c.Get("user").(*jwt.Token)
    //取得した認証情報userから、jwt.MapClaims型のペイロードデータを取得します。
	claims := user.Claims.(jwt.MapClaims)
    //ペイロードデータから user_idの値を取得します。
	userId := claims["user_id"]
    //リクエストパスからtaskIdパラメータを取得します。
	id := c.Param("taskId")
    //taskId パラメータを整数型に変換します。
	taskId, _ := strconv.Atoi(id)
    //タスクユースケースのDeleteTaskメソッドは、ユーザーIDとタスクIDを受け取り、タスクの削除処理を実行します。
    //削除処理中にエラーが発生した場合、err変数には、エラーオブジェクトが格納されます。
	err := tc.tu.DeleteTask(uint(userId.(float64)), uint(taskId))
    //削除処理中にエラーが発生した場合、HTTPステータスコード 500 (Internal Server Error) を返します。
	if err != nil {
		return c.JSON(http.StatusInternalServerError, err.Error())
	}
    //タスクの削除が成功した場合、HTTP ステータスコード 204 (No Content) を返します。これは、削除が成功したことを示しますが、レスポンスボディに何も含めないことを意味します。
	return c.NoContent(http.StatusNoContent)
}
```
<h2>routerの実装</h2>
以前作成したrouterにタスク関連のエンドポイントを追加していきます。

https://qiita.com/sawadashinji0526/items/7492f19b868833cab343

```router/router.go
package router

import (
	"go-rest-api/controller"
	"os"

	echojwt "github.com/labstack/echo-jwt/v4"
	"github.com/labstack/echo/v4"
)

func NewRouter(uc controller.IUserController, tc controller.ITaskController) *echo.Echo {
	e := echo.New()
	e.POST("/signup", uc.SignUp)
	e.POST("/login", uc.Login)
	e.POST("/logout", uc.LogOut)
    //echo.Echo オブジェクトeに対して、/tasksというプレフィックスを持つグループを作成します。これにより、/tasksの下に複数のエンドポイントを定義することができます。
	t := e.Group("/tasks")
    //echo-jwtミドルウェアを使って、tasksグループのすべてのエンドポイントに対して、JWTトークンによる認証を適用します。
    //echojwt.Config: echo-jwtミドルウェアの設定オプションを定義します。
	t.Use(echojwt.WithConfig(echojwt.Config{
        ////JWTトークンを署名するために使用される秘密鍵です。環境変数 SECRET から取得されます。
		SigningKey:  []byte(os.Getenv("SECRET")),
        //JWTトークンを取得する場所を指定します。この場合、クッキーのtokenという名前で取得します。
		TokenLookup: "cookie:token",
	}))
    //GETメソッドで/tasksエンドポイントにアクセスしたときに、tc.GetAllTasks関数を呼び出すように設定します。
	t.GET("", tc.GetAllTasks)
    //GET メソッドで/tasks/:taskId エンドポイントにアクセスしたときに、tc.GetTaskById 関数を呼び出すように設定します。
	t.GET("/:taskId", tc.GetTaskById)
    ///tasksエンドポイントにアクセスしたときに、tc.CreateTask関数を呼び出すように設定します。
	t.POST("", tc.CreateTask)
    //PUTメソッドで/tasks/:taskIdエンドポイントにアクセスしたときに、tc.UpdateTask 関数を呼び出すように設定します。
	t.PUT("/:taskId", tc.UpdateTask)
    //DELETEメソッドで/tasks/:taskIdエンドポイントにアクセスしたときに、tc.DeleteTask関数を呼び出すように設定します。
	t.DELETE("/:taskId", tc.DeleteTask)
	return e
}
```

<h2>mainの実装</h2>
以前作成したmain.goにタスクリポジトリ、タスクユースケース、タスクコントローラーを追加していきます。

```main.go
package main

import (
	"go-rest-api/controller"
	"go-rest-api/db"
	"go-rest-api/repository"
	"go-rest-api/router"
	"go-rest-api/usecase"
)

func main() {
	db := db.NewDB()
	userRepository := repository.NewUserRepository(db)
    //dbを使って、タスクリポジトリの新しいインスタンスを作成します。
	taskRepository := repository.NewTaskRepository(db)
	userUsecase := usecase.NewUserUsecase(userRepository)
    //taskRepositoryを使って、タスクユースケースの新しいインスタンスを作成します。
	taskUsecase := usecase.NewTaskUsecase(taskRepository)
	userController := controller.NewUserController(userUsecase)
    //taskUsecaseを使って、タスクコントローラーの新しいインスタンスを作成します。
	taskController := controller.NewTaskController(taskUsecase)
	e := router.NewRouter(userController, taskController)
	e.Logger.Fatal(e.Start(":8080"))
}
```

<h2>プログラムを起動</h2>
下記のコマンドでプログラムを起動していきます。
`GO_ENV=dev go run main.go`

起動できました！
![スクリーンショット 2024-11-04 14.01.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3628257/9aec9920-2107-932d-f8f9-c5e6feb0717d.png)

次はPostmanを使って動作確認します。
`http://localhost:8080/tasks`のエンドポイントにGETメソッドでアクセスします。
タスクのエンドポイントはJWTでプロテクションされているので、JWTトークンがない場合は`401 Unauthorized`のエラーが発生します。

![スクリーンショット 2024-11-04 14.16.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3628257/2db4221e-3e0a-00e0-a77b-27093fc299fa.png)

JWTトークンを付与するためにログインを行います。
エンドポイントを`http://localhost:8080/login`に変更してPOSTメソッドでアクセスしていきます。
Bodyのrawを選択し、前回作成したユーザーでログインしていきます。
Cookiesタブを確認するとJWTトークンが出来ます。
![スクリーンショット 2024-11-04 14.23.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3628257/7844af91-035e-0439-b310-4e3badd03f18.png)

JWTトークンが出来たので、再度`http://localhost:8080/tasks`のエンドポイントにGETメソッドでアクセスします。
![スクリーンショット 2024-11-04 14.37.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3628257/5ecd2565-af3c-0ab4-8025-b13fd3f30020.png)
今度はJWTの認証が通って、ステータスがOKで空の配列が返ってきます。

それでは、タスクの新規作成を行ってみたいと思います。
titleがhelloというタスクを作ります。
`http://localhost:8080/tasks`に変更してPOSTメソッドでアクセスしていきます。
![スクリーンショット 2024-11-04 14.41.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3628257/8ceb5054-7644-88f8-2cd3-3fa20649ea5a.png)

ステータス201 Createdが返ってきて新しく作成されたタスクのオブジェクトがJSONで返ってきます。
pdAdminでも確認していきます。
![スクリーンショット 2024-11-04 14.46.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3628257/129704ba-fabf-e08a-dc5b-6eb090d861b5.png)
DBにも新規作成したタスクが追加されたのを確認できました。

GetTaskByIdでID12のタスクを1つだけ取得していきたいと思います。
まずは、`http://localhost:8080/tasks`に変更してPOSTメソッドでアクセスしてもう一つタスクを作成しておきます。
![スクリーンショット 2024-11-04 14.50.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3628257/ab12f0a2-59df-5eca-56e1-7d6760e7374a.png)

`http://localhost:8080/tasks/12`のエンドポイントにGETメソッドでアクセスします。
![スクリーンショット 2024-11-04 14.55.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3628257/98dc9fc7-73aa-18ec-23a9-0c08fee944c3.png)
そうするとIDが12のタスクを1つ取得できました。

それでは、次は更新を試していきたいと思います。
PUTメソッドに変更して、そしてエンドポイント`http://localhost:8080/tasks/`の末尾に変更したいタスクのIDを追加します。
ここではID12のtitleをupdateに更新していこうと思います。

![スクリーンショット 2024-11-04 15.01.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3628257/d4371550-29d2-4708-1956-c725405f888a.png)

titleが更新されて、更新した日時も変更されました。
pgAdminを確認するとデータベースの方にも更新内容が反映されました。
![スクリーンショット 2024-11-04 15.03.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3628257/17ed8f4a-2950-5d2d-1aa8-a429fdd8569c.png)

次はID12のタスクを削除していきたいと思います。
DELETEメソッドに変更して`http://localhost:8080/tasks/12`のエンドポイントにアクセスします。

![スクリーンショット 2024-11-04 15.07.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3628257/9327f4df-e6d3-54e9-babb-6a5291e6ec02.png)

そうすると204 No Contentが返ってきました。
pgAdminを確認するとデータベースの方もIDが12のタスクが削除されているのを確認できました。
(Executeボタンを押下)

![スクリーンショット 2024-11-04 15.11.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3628257/b68d2084-ea43-ecdc-7763-993cf07ba4fe.png)

そして、POSTメソッドでログアウトのエンドポイントにアクセスすると、Cookieが削除されますので、この状態で、DELETEメソッド`http://localhost:8080/tasks/11`のエンドポイントにアクセスしても，JWTトークンがありませんということで、401 Unauthorizedが返ってきます。
![スクリーンショット 2024-11-04 15.25.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3628257/516f01e6-9715-92e9-755f-f3aa827873bf.png)

![スクリーンショット 2024-11-04 15.26.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3628257/46fd5cb8-25f5-9c7f-a400-257162b861b2.png)

<h2>まとめ</h2>
今回はタスクの新規作成、更新、削除機能を実装しました。<br>
次回は、バリデーションの機能を実装していきます。