🤖

Go言語で学ぶWebアプリケーション開発2:[データベース連携&認証機能の追加]

2025/03/02に公開

はじめに

前回の記事では、Go + Ginを使ったバックエンドとReact + Viteを使ったフロントエンドで作成したシンプルなToDoアプリを作成しました。
今回はそこに、データベース連携(SQLite + GORM)とJWT認証機能の追加を行い、より実用的なWebアプリケーションに発展させます。

対象読者

  • Goでデータベースと連携したWebアプリを作成したい方
  • JWT認証を導入し、ログイン機能を実装したい方
  • フルスタック開発を学びたい方

目次

  1. データベース連携(GORM + SQLite)
    • GORMのセットアップ
    • モデルの定義とマイグレーション
    • CRUD操作の実装
  2. 認証機能(JWT)
    • ユーザーモデルの作成
    • ログインAPIの実装
    • JWTトークンの発行と認証
  3. フロントエンドとの統合
    • Reactからの認証リクエスト
    • 認証されたユーザーのみAPIにアクセス可能にする

1. データベース連携(GORM + SQLite)

1.1 GORM のセットアップ

まず、GORMとSQLiteドライバをインストールします。

go get -u gorm.io/gorm
go get -u gorm.io/driver/sqlite

1.2 モデルの定義とマイグレーション

データベースに格納するTodoモデルを定義します。

models/todo.go

package models

import "gorm.io/gorm"

type Todo struct {
    ID     uint   `gorm:"primaryKey"`
    Task   string `json:"task"`
    Status bool   `json:"status"`
}

データベースの接続とマイグレーションを行います。

database/database.go

package database

import (
    "gorm.io/driver/sqlite"
    "gorm.io/gorm"
    "log"
    "models"
)

var DB *gorm.DB

func InitDatabase() {
    var err error
    DB, err = gorm.Open(sqlite.Open("app.db"), &gorm.Config{})
    if err != nil {
        log.Fatal("Failed to connect database")
    }
    DB.AutoMigrate(&models.Todo{})
}

main.goでデータベースを初期化します。

database.InitDatabase()

1.3 CRUD操作の実装

controllers/todo_controller.go

package controllers

import (
    "github.com/gin-gonic/gin"
    "net/http"
    "database"
    "models"
)

func GetTodos(c *gin.Context) {
    var todos []models.Todo
    database.DB.Find(&todos)
    c.JSON(http.StatusOK, todos)
}

func CreateTodo(c *gin.Context) {
    var newTodo models.Todo
    if err := c.ShouldBindJSON(&newTodo); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }
    database.DB.Create(&newTodo)
    c.JSON(http.StatusCreated, newTodo)
}

ルーティングの設定を行います。

router.GET("/api/todos", controllers.GetTodos)
router.POST("/api/todos", controllers.CreateTodo)

2. 認証機能(JWT)

2.1 ユーザーモデルの作成

models/user.go

package models

type User struct {
    ID       uint   `gorm:"primaryKey"`
    Username string `json:"username"`
    Password string `json:"password"`
}

2.2 ログインAPIの実装

controllers/auth_controller.go

package controllers

import (
    "github.com/gin-gonic/gin"
    "net/http"
    "database"
    "models"
    "time"
    "github.com/golang-jwt/jwt/v4"
)

type LoginRequest struct {
    Username string `json:"username"`
    Password string `json:"password"`
}

func Login(c *gin.Context) {
    var req LoginRequest
    var user models.User
    if err := c.ShouldBindJSON(&req); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }
    database.DB.Where("username = ?", req.Username).First(&user)
    if user.ID == 0 || user.Password != req.Password {
        c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid credentials"})
        return
    }
    
    token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
        "username": user.Username,
        "exp":     time.Now().Add(time.Hour * 2).Unix(),
    })
    tokenString, _ := token.SignedString([]byte("secret"))
    c.JSON(http.StatusOK, gin.H{"token": tokenString})
}

ルーティングを設定します。

router.POST("/api/login", controllers.Login)

3. フロントエンドとの統合

3.1 React からの認証リクエスト
src/Login.jsx

import { useState } from "react";
import axios from "axios";

function Login() {
    const [username, setUsername] = useState("");
    const [password, setPassword] = useState("");
    
    const handleLogin = async () => {
        const response = await axios.post("http://localhost:8080/api/login", { username, password });
        localStorage.setItem("token", response.data.token);
    };
    
    return (
        <div>
            <input type="text" placeholder="Username" onChange={(e) => setUsername(e.target.value)} />
            <input type="password" placeholder="Password" onChange={(e) => setPassword(e.target.value)} />
            <button onClick={handleLogin}>Login</button>
        </div>
    );
}

export default Login;

まとめ

今回は、GORMを使ったデータベース連携とJWT認証機能の追加を行い、より実用的なWebアプリケーションに近づけました。
次はさらにユーザー管理やロールベース認証の追加 をまとめていこうと思います!

Discussion