LoginSignup
0
1

Node.js+MySQL+Dockerを使用したAPIの作成

Last updated at Posted at 2023-07-12

はじめに

Node.js+MySQL+Dockerを使用したAPIの作成をしていきます。

記事を書くにあたり、trelloクローンアプリの作成を最終目標としています。
その過程で、バックエンド構築の際に今回の記事の内容で作成してみました。

作成にあたり主に下記の記事を参考にしております。

【Docker】DockerにMySQL環境を構築する(M1 Mac 対応)
https://midorigame-jo.com/docker-mysql/
DockerでMySQL・Node.jsコンテナを用いたToDoリストアプリを作成する
https://qiita.com/niisan1ban/items/f70eb0ed891568f71f9b
次世代の Node.js ORM 、Prisma をサクッと入門する
https://zenn.dev/pyhrinezumi/articles/431be604f9ad50
【Web API】Talend API Testerを使ってみた
https://www.isoroot.jp/blog/4241/

環境

MacBookAir m1
VSCode
Node.js ver 18
MySQL ver 8.0.33
TypeScript
Prisma
Docker

1.環境構築

はじめに、docker-composeを作成します。
内容は下記の設定としております。

docker-compose.yml
services:
  db:
    image: mysql:8.0.33
    #Macbook m1を使用しているためplantformを指定
    platform: linux/amd64
    container_name: mysql
    command: --default-authentication-plugin=mysql_native_password
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_USER: ユーザ名
      MYSQL_PASSWORD: パスワード
      MYSQL_DATABASE: trello
      TZ: "Asia/Tokyo"
    volumes:
      - db-data:/var/lib/mysql
    ports:
      - 3307:3306

  backend:
    image: node:18-slim
    volumes:
      - ./backend:/usr/src/app
    working_dir: /usr/src/app
    command: bash -c "npm install && npm start"
    ports:
      - 3000:3000
    depends_on:
      - db
    #URLはlocalhostではなく、docker-composeで指定しているservicesのdbを指定
    environment:
      DATABASE_URL: mysql://ユーザー名:パスワード@db:3306/trello

volumes:
  db-data:

手間を省くため、docker-compose上でDBアクセス用のユーザの払い出しとデータベースの設定をしました。
それに伴いURLも変更しております。

使用している環境により設定する内容が変わるため、詳細は公式リファレンスを確認するようにしてください。
筆者はMacBookM1だったため、環境構築に苦労しました、、、

上記をプロジェクトのルートフォルダへ配置し、ターミナルで下記コマンドを実行します。

docker-compose up -d

これにより、MysqlとNode.jsの環境構築が完了しました。

2.テーブル設定

では、次にデータベースのテーブル設定を行います。
今回は、Prismaを使用してマイグレーションを行います。
Node.jsのORMはいくつかありますが、TypeScriptと親和性があり、使用しやすいイメージをがあったため、今回採用してみました。

ディレクトリをbackendフォルダに移動し、下記コマンドを順番に実行します。

npm init -y
npm install prisma --save-dev
npx prisma init --datasource-provider mysql

実行後、自動的にbackendフォルダ直下に.envファイルとPrismaフォルダとschema.prismaが作成されます。
ここでテーブル、カラム設定を行うことができます。
今回は下記のように設定を行います。

schema.prisma
generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "mysql"
  url      = env("DATABASE_URL")
}

model Boards {
  id            Int     @id @default(autoincrement())
  name          String
  board_order   Int
}

.envファイルには、マイグレーション用のURLが記述してあります。
Dockerの環境に合わせ、下記のように設定しております。

.env
DATABASE_URL="mysql://root:root@0.0.0.0:3307/trello"

localhostではなく、0.0.0.0を設定しています。
ポートはDocker-composeで設定した値です。

設定完了後、ターミナルでbackend下で下記コマンドを入力します。

npx prisma migrate dev --name init

完了すると、テーブルとカラムの設定が自動で行われます。
Dcoker DeskTopで確認してみましょう。
スクリーンショット 2023-07-11 17.26.23.png

Prismaでは、下記コマンドを実行するとクラインとツールが起動し、DBの操作も行えるため非常に便利です。

npx prisma studio

スクリーンショット 2023-07-11 17.29.48.png

以上でデータベースの設定が完了です。

3.Node.jsでAPI構築

Node.jsのexpressを使用してAPIの作成をしていきます。
データベースの接続は本来であればPrismaをそのまま使用していきたいですが、MacBookM1でDockr上のMysqlをlinux/amd64で起動しているとエラーが出てしまい、うまく解消ができませんでした。
そのためmysql2を使用していきます。
まず、下記コマンドを実行していきます。

npm install express
npm install --save-dev typescript ts-node @types/node @types/express
npm install -D nodemon
npm install --save doting
npm install --save mysql2
npm install cors --save

次にtsconfig.jsonを作成し、下記内容とします。

tsconfig.json
{
    "compilerOptions": {
      "target": "esnext",
      "module": "commonjs",
      "esModuleInterop": true,
      "forceConsistentCasingInFileNames": true,
      "strict": true,
      "skipLibCheck": true,
      "sourceMap": true,
      "outDir": "dist",
      "lib": ["esnext"],
    }
  }

package.jsonに下記内容を修正、追加します。

package.json
  //修正
  "main": "server.ts"
 //追加
  "scripts": {
    "start": "nodemon server.ts",
  }

backend直下にserver.tsを作成します。
このファイルでAPIのサーバーの設定を行います。

server.ts

import express, { response } from 'express';
import { BoardsController } from './controllers/boardsController';
import cors from 'cors'


require('dotenv').config();


const app: express.Express = express();
const port = 3000;

const boardsController = new BoardsController();

app.use(express.json());
app.use(cors());


app.listen(port,() => {
    console.log("起動"+ port);
})


app.get("/",(request:express.Request,response:express.Response) => {
    response.send("HTTPリクエスト確認");
})

app.get("/boards",boardsController.getAllBoards);
app.get("/boards/:id",boardsController.getBoardById);
app.post("/boards",boardsController.createBoard);
app.put("/boards/:id",boardsController.updateBoard);
app.delete("/boards/:id",boardsController.deleteBoard);

app.use((error:any,request:express.Request,response:express.Response, next:express.NextFunction) => {
    console.error(error);
    response.status(500).send("エラーが発生しました。");
});

backend直下にcontrollersフォルダを作成し、
boardsController.tsを作成します。
ここで、HTTPリクエストの処理を行うメソッドを定義していきます。

boardsController.ts
import express from 'express';
import { BoardsModel } from '../models/boardsModel';

const boardsModel = new BoardsModel();


export class BoardsController {
    
    async getAllBoards(request:express.Request,response:express.Response, next:express.NextFunction)  {
        try {
            const rows = await boardsModel.getAllBoards();
            response.json(rows);
        } catch (error:any) {
            next(error);
        }
    }

    async getBoardById(request:express.Request,response:express.Response, next:express.NextFunction){
        const boardId = request.params.id;
        try {
            const rows = await boardsModel.getBoardById(boardId);
            response.json(rows);
        } catch (error:any) {
            next(error);
        }
    }

    async createBoard(request:express.Request,response:express.Response, next:express.NextFunction) {
        const {name, board_order } = request.body;
        try {
            const result  = await boardsModel.createBoard(name,board_order);
            response.json(result);
        } catch (error:any) {
            next(error);
        }
    }

    async updateBoard(request:express.Request,response:express.Response, next:express.NextFunction) {
        const {id} = request.params;
        const {name, board_order } = request.body;
        try {
            const result = await boardsModel.updateBoard(id,name,board_order);
            response.json(result);
        } catch (error:any) {
            next(error);
        }
    }

    async deleteBoard(request:express.Request,response:express.Response, next:express.NextFunction) {
        const {id} = request.params;
        try {
            const result = await boardsModel.daleteBoard(id);
            response.json(result);
        } catch (error:any) {
            next(error);
        }
    }

}

backend直下にmodelsフォルダを作成し、
boardsModels.tsを作成します。
ここでは、データベースにおけるCRUD処理を記述します。

boardsModels.ts
import mysql from 'mysql2/promise';


require('dotenv').config();


const dbConnect = mysql.createPool({
    host : process.env.DB_HOST,
    user : process.env.DB_USER,
    password : process.env.DB_PASSWORD,
    database : process.env.DB_NAME,
    connectTimeout: 100,
});

export class BoardsModel {
    
    async getAllBoards() {
        const [rows] = await dbConnect.execute("select * from Boards order by board_order ");
        return rows;
    }

    async getBoardById(id:string) {
        const [rows] = await dbConnect.execute("select * from Boards where id = ? ",[id]);
        return rows;
    }

    async createBoard(name:string,board_order:number){
        const [result] = await dbConnect.execute("insert into Boards set name = ?,board_order = ?  ",[name, board_order]);
        return result;
    }

    async updateBoard(id:string,name:string,board_order:number){
        const [result] = await dbConnect.execute("update Boards set name = ?, board_order = ? where id = ?", [name, board_order, id]);
        return result;
    }

    async daleteBoard(id:string) {
        const [result] = await dbConnect.execute("delete from Boards where id = ?", [id]);
        return result;
    }


}

以上で、Node.jsの設定が完了です。
これで、API機能の完成です。

この状態で、「http://localhost:3000/」へアクセスすると
app.get("/")のレスポンスで設定していたsend()の内容が返却されます。

スクリーンショット 2023-07-11 18.14.55.png

4.APIに対するHTTPリクエストの確認

APIの動作確認として、今回はchromeの拡張機能であるTalend API Testerを使用してみました。

Talend Cloud API Tester
https://help.talend.com/r/ja-JP/Cloud/api-tester-user-guide

まず、確認用のデータをBoardsテーブルに用意します。
スクリーンショット 2023-07-11 19.42.48.png

①Getリクエスト

画面上部のタブから「リクエスト」を選択します。
メソッドから「GET」を選択します。
server.tsにて設定したURL「localhost」(http://localhost:3000/boards)
を指定し、送信を押下します。

スクリーンショット 2023-07-12 9.26.50.png

画面下部にリクエストのレスポンス結果が表示されます。
先ほど追加した2つのデータがJSON形式で返却されることが確認できました。
スクリーンショット 2023-07-12 9.36.09.png

②Postリクエスト

次に、POSTリクエストでデータを追加してみましょう。
メソッドから「POST」を選択します。
ボディに追加用のデータをJSON形式で記述し、送信を押下します。
スクリーンショット 2023-07-12 9.47.54.png

下記のようなレスポンスが返却されます。
スクリーンショット 2023-07-12 10.06.24.png

再び、Getリクエストを送信してデータを確認してみると、Postリクエストで送信したデータが追加されていることが確認できます。

スクリーンショット 2023-07-12 10.09.51.png

③Putリクエスト

今度は、先ほど追加したデータを更新してみましょう。
メソッドから「PUT」を選択し、URLに更新したいデータのidを指定し、送信を押下します。

スクリーンショット 2023-07-12 10.12.50.png

下記のレスポンスが返却されたので、再度Getリクエストを送信し、対象のデータが更新されたか確認してみましょう。

スクリーンショット 2023-07-12 10.14.23.png

先ほどPutリクエストで送信した内容で更新されたことが確認できます。

スクリーンショット 2023-07-12 10.16.32.png

④Deleteリクエスト

最後にDeleteリクエストでデータを削除してみましょう。
メソッから「DELETE」を選択し、URLに削除したいデータのidを指定し、送信を押下します。

スクリーンショット 2023-07-12 10.20.35.png

スクリーンショット 2023-07-12 10.21.24.png

Getリクエストで削除されたかどうか確認してみましょう。
無事削除が正常に処理されていました。

スクリーンショット 2023-07-12 10.22.54.png

以上で、実装したHTTPリクエストが全て正常に処理されていることが確認できました。
これにて、Node.js、Mysql、Dockerを使用したAPIの完成です。

最後に

初めてNode.jsやDockerを使用したAPI構築を行いましたが、環境構築でかなり手間取ってしまいました。
しかし、一から作り上げることで仕様を学ぶいい機会となりました。
この調子で最終目標であるtrelloのクローンアプリ完成を目指していきます。

最後まで記事を読んでいただきありがとうございました。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1