よりひろい フロントエンド
Author : Kazuhiro Hara
Author : Kazuhiro Hara
Wed Aug 28 2024

Vapor で MySQL と接続して JSON レスポンス

Vapor に慣れるために Hello World より少し進めてみる。今回はデータベースに接続して データを POST メソッドにて保存、JSON レスポンスを返すまでをやってみる。

とはいっても、前述の状態を実現するだけなら簡単で Vapor Toolbox にて新規プロジェクトを作成する際に Fluent を利用するようにすることによりマイグレーション用のファイルやコントローラなどが自動で生成できる。

さっそく新規プロジェクトを作成。

simple-api というプロジェクト名で作ってみる。プロジェクト生成時点で Todo というシンプルなテーブルを使うアプリケーションが組み込まれている。この Todo のモデルやコントローラなどのコードを眺めているとなんとなくどんな風に設定していくのかがわかるかもしれない。

$ vapor new simple-api
Cloning template...
name: simple-api
Would you like to use Fluent (ORM)? (--fluent/--no-fluent)
y/n> y
fluent: Yes
db: MySQL
Would you like to use Leaf (templating)? (--leaf/--no-leaf)
y/n> y
leaf: Yes
Generating project files

...

環境変数でデータベースの接続情報をセットアップ

生成されたコードを見ると、データベースの接続方法がなんとなくわかる

Sources/App/configure.swift

import NIOSSL
import Fluent
import FluentMySQLDriver
import Leaf
import Vapor

public func configure(_ app: Application) async throws {
    ...

app.databases.use(DatabaseConfigurationFactory.mysql(
        hostname: Environment.get("DATABASE_HOST") ?? "localhost",
        port: Environment.get("DATABASE_PORT").flatMap(Int.init(_:)) ?? MySQLConfiguration.ianaPortNumber,
        username: Environment.get("DATABASE_USERNAME") ?? "vapor_username",
        password: Environment.get("DATABASE_PASSWORD") ?? "vapor_password",
        database: Environment.get("DATABASE_NAME") ?? "vapor_database"
    ), as: .mysql)

    app.migrations.add(CreateTodo())

    ...
}

これはもしや .env ファイルから環境変数を設定することもできるかなとドキュメントを見たら、できそうだった。

ということで設定してみる。データベースは vapor-sample-1 という名前で CREATE SCHEMA vapor-sample-1 DEFAULT CHARACTER SET utf8mb4 ; にてとりあえず作成しておいた。

DATABASE_HOST=localhost
DATABASE_PORT=3306
DATABASE_USERNAME=XXXXXXXX
DATABASE_PASSWORD=XXXXXXXX
DATABASE_NAME=vapor-sample-1

ローカルの接続情報を設定した。MySQL 自体はローカルの Docker コンテナとして動かしている。

マイグレーション

データベースはまだ何のテーブルもない状態だが、 fluent にはマイグレーション機能があり、初期テーブルの作成がコマンドラインにて以下のように行うことが出来る。

$ swift run App migrate
Building for debugging...
[9/9] Linking App
Build complete! (2.10s)
Migrate Command: Prepare
The following migration(s) will be prepared:
+ App.CreateTodo on <default>
Would you like to continue?
y/n> y
[ INFO ] [Migrator] Starting prepare [database-id: mysql, migration: App.CreateTodo]
[ INFO ] [Migrator] Finished prepare [database-id: mysql, migration: App.CreateTodo]
Migration successful

これで、データベース上にテーブル todo が作成された。

Todo コントローラの中身

サンプルコントローラを見ると、indexcreatedelete というルートが作られたのがわかる

Sources/App/Controllers/TodoController.swift

import Fluent
import Vapor

struct TodoController: RouteCollection {
    func boot(routes: RoutesBuilder) throws {
        let todos = routes.grouped("todos")

        todos.get(use: self.index)
        todos.post(use: self.create)
        todos.group(":todoID") { todo in
            todo.delete(use: self.delete)
        }
    }

    @Sendable
    func index(req: Request) async throws -> [TodoDTO] {
        try await Todo.query(on: req.db).all().map { $0.toDTO() }
    }

    @Sendable
    func create(req: Request) async throws -> TodoDTO {
        let todo = try req.content.decode(TodoDTO.self).toModel()

        try await todo.save(on: req.db)
        return todo.toDTO()
    }

    @Sendable
    func delete(req: Request) async throws -> HTTPStatus {
        guard let todo = try await Todo.find(req.parameters.get("todoID"), on: req.db) else {
            throw Abort(.notFound)
        }

        try await todo.delete(on: req.db)
        return .noContent
    }
}

アプリを起動して、一つ todo レコードを生成してみる

アプリの起動は $ swift run にて行う。ちょっと REST クライアントで POST メソッドをたたいてみよう。My Title というタイトルを持ったレコードを一つ作成してみる。

POST http://localhost:8080/todos HTTP/1.1
content-type: application/json

{
    "title": "My Title",
}

すると、うまく設定されていれば以下のようなレスポンスが返される。無事レコードもデータベース上に作成されており、一山超えた満足感がある。

HTTP/1.1 200 OK
content-type: application/json; charset=utf-8
content-length: 64
connection: close
date: Tue, 27 Aug 2024 13:26:02 GMT

{
  "id": "4587E60A-E9ED-43EB-B692-294124BB4E13",
  "title": "My Title"
}
VaporSwift

Share

About site

カンソクインダストリーズのロゴ

「よりひろいフロントエンド」運営元 カンソクインダストリーズ では、フロントエンドを中心によろずご相談お受けいたします。お気軽にお問い合わせください。