TypeScriptとPostgreSQLを用いたcrudなアプリケーション開発 ~Backend ローカル環境セットアップ編~
作るもの
crud処理をするシンプルなアプリケーション。設計図も含め詳細は後ほど記載する。
実行環境
- M1Mac
- Node.js v14.16.0
- Docker v20.10.12
- psql (PostgreSQL) 14.1
- TypeScript 4.5.5
1. DB選定
個人開発でcrudなアプリケーションを作る上でDBを安く抑えたかった。(学生だし。)いろいろなクラウドでコストを算出したところ、永久無料枠のあるHerokuPostgres@Herokuを利用することにした。算出の過程でAWSって意外と高いんだな〜という印象を持った。確か一番低スペックで月2000円とかした気がする。
参考記事:
フォルダ構成
crud-front
crud-backend
|- app.ts
|- Repository = TyprORM
|- MyProject - inedx.ts
|- ormconfig.json
- src - index.ts
|- entity - Choice.ts
|- Main.ts
crud-db
環境構築
1. PostgreSQLのサーバーを構築
ローカルで動作を確認するために、docker-composeでPostgreSQLのサーバーを立てていく。
以下の記事を参考にした。
1. docker-compose.yml作成
version: '3'
services:
postgres:
image: postgres:latest
restart: always
environment:
POSTGRES_USER: mebee
POSTGRES_PASSWORD: password
PGPASSWORD: password123
POSTGRES_DB: sample
TZ: "Asia/Tokyo"
ports:
- 5432:5432
volumes:
- postgres:/var/lib/postgresql/data
pgadmin:
image: dpage/pgadmin4
restart: always
ports:
- 81:80
environment:
PGADMIN_DEFAULT_EMAIL: info@mebbe.info
PGADMIN_DEFAULT_PASSWORD: password
volumes:
- pgadmin:/var/lib/pgadmin
depends_on:
- postgres
volumes:
postgres:
pgadmin:
docker-compose.ymlではdbの定義をしている。
2. pgadminを起動
以下のコマンドでdockerを立ち上げ、http://localhost:81
を入力しpgadminを起動。
$ docker-compose up -d
3. データベースを作成
記事に従ってsampleデータベースを作成していく。
2. ローカルで動作を確認
$ npm run start
MissingDriverError: Wrong driver: "postgresql" given. Supported drivers are: "aurora-data-api", "aurora-data-api-pg", "better-sqlite3", "capacitor", "cockroachdb", "cordova", "expo", "mariadb", "mongodb", "mssql", "mysql", "nativescript", "oracle", "postgres", "react-native", "sap", "sqlite", "sqljs".
早速エラーが出た。
ormconfig.jsonのtypeプロパティの値がpostgresqlってなってたのが原因。以下のようにtypeに
修正を加える。
{
"type": "postgres",
"host": "localhost",
"port": 5432,
"username": "test",
"password": "test",
"database": "test",
"synchronize": true,
"logging": false,
"entities": [
"src/entity/**/*.ts"
],
"migrations": [
"src/migration/**/*.ts"
],
"subscribers": [
"src/subscriber/**/*.ts"
],
"cli": {
"entitiesDir": "src/entity",
"migrationsDir": "src/migration",
"subscribersDir": "src/subscriber"
}
}
再度実行。するとまた違う内容のエラーが。
$ npm start
error: password authentication failed for user "test"
docker-compose.ymlでdbの定義をしているのに対し、ormconfig.jsonではdb接続のための定義が書かれている。だからdocker-compose.ymlに合わせてormconfig.jsonの内容を書き換えねばならない。username, password, database の値を適切に変えていく。
{
"type": "postgres",
"host": "localhost",
"port": 5432,
"username": "mebee",
"password": "password",
"database": "sample",
:
:
:
}
3. PostgreSQLに接続
crud-backendリポジトリ直下にpsqlコマンドをインストールし、PostgreSQLに接続していく。
$ brew update
$ brew install libpq
$ echo 'export PATH="/usr/local/opt/libpq/bin:$PATH"' >> ~/.bash_profile
$ source ~/.bash_profile
$ psql --version
$ psql -h 0.0.0.0 -p 5432 -d sample -U mebee
PostgreSQL のDBインスタンスには、次のコマンドでログイン。DB接続情報を次のとおりオプションに指定しコマンドを実行。スペースはちゃんと入力するように。passwordの部分はpasswordだけ記述すればok。--dbnameのうしろに\はいらない。
psql \
--host=localhost \
--port=5432 \
--username=mebee \
--password \
--dbname=test
参考記事:
2. TypeOrmのインストール
今回Clean Architectureを採用。以下の公式ドキュメントを参照してTypeORMをインストールしていく。
crud-backendリポジトリ内で以下のコマンドを実行。$ sudo npm install typeorm -g
以下のコマンドを実行し、crud-backendリポジトリ内にMyProjectフォルダを作成する。
$ typeorm init --name MyProject --database postgressql
Project created inside /Users/xxxx/Documents/GitHub/crud-backend/MyProject directory.
$ npm i
sh: ts-node: command not found
エラーが表示された。実行するディレクトリのパスが間違っていたので以下のように変更。MyProject直下でnpm i。
$ cd MyProject
$ npm install
作成されたormconfig.json を編集する。
{
"type": "postgresql",
"host": "localhost",
"port": 5432,
"username": "test",
"password": "test",
"database": "test",
"synchronize": true,
"logging": false,
"entities": [
"src/entity/**/*.ts"
],
"migrations": [
"src/migration/**/*.ts"
],
"subscribers": [
"src/subscriber/**/*.ts"
]
}
3. テーブルの作成
1. DB正規化
DBに格納されるオリジナルのデータは以下のようなものを想定している。
{
"questionId" : "buMQwLVq5wZ2",
"isPublished" : true,
"text" : "",
"correctChoiceId" : "QGYqT8Z0eieu4"
"choices" : [
{
"choiceId" : "QGYqT8Z0eieu1",
"text" : "AlexNet"
},
{
"choiceId" : "QGYqT8Z0eieu2",
"text" : "GoogLeNet"
}
],
}
],
"categories" : ""
}
非正規形は以下の通り。
第一正規形は以下の通り。
第二正規形は以下の通り。
4. Entity作成
1. Entityの作成
以下の公式ドキュメントを参照し、Many-to-One, One-to-Many のEntityを作成していく。
import {Entity, PrimaryGeneratedColumn, Column, ManyToOne} from "typeorm";
import { Main } from "./Main";
@Entity()
export class Choice {
@PrimaryGeneratedColumn()
choiceId?: string;
@Column()
choiceText?: string;
@ManyToOne(() => Main, main => main.choices)
main: Main;
}
import {Entity, PrimaryGeneratedColumn, Column, OneToMany} from "typeorm";
import { Choice } from "./Choice";
@Entity()
export class Main {
@PrimaryGeneratedColumn()
questionId: string;
@Column()
isPublished?: boolean;
@Column()
correctChoiceId?: string;
@Column({ nullable: true })
question?: string;
@Column({ nullable: true })
categories?: string;
@OneToMany(() => Choice, choice => choice.main)
choices: Choice[];
}
// import "reflect-metadata";
import {createConnection} from "typeorm";
import {Choice} from "./entity/Choice";
import {Main} from "./entity/Main";
createConnection(
).then(async connection => {
const choice1 = new Choice();
choice1.choiceText = "レアジョブ";
await connection.manager.save(choice1);
const choice2 = new Choice();
choice2.choiceText = "ビズメイツ";
await connection.manager.save(choice2);
const choiceRepository = connection.getRepository(Choice);
const choices = await choiceRepository.find();
console.log(choices)
const main = new Main();
main.isPublished = true
main.correctChoiceId = "xyz"
main.choices = [choice1, choice2];
main.categories = "english"
main.question = "ビジネス英会話教室はどれでしょう?";
console.log(main)
await connection.manager.save(main);
// const mainRepository = connection.getRepository(Main);
// const mains = await mainRepository.find({ relations: ["choices"] });
//console.log("Loaded mains: ", JSON.stringify(choices));
}).catch(error => console.log(error));
2. イニシャルデータの作成
以下の記事を参考にイニシャルデータを作成していく。
まず、docker-compose.ymlのvolumesにdb:/docker-entrypoint-initdb.dを定義する。version: '3'
services:
postgres:
image: postgres:latest
restart: always
environment:
POSTGRES_USER: mebee
POSTGRES_PASSWORD: password
PGPASSWORD: password123
POSTGRES_DB: sample
TZ: "Asia/Tokyo"
ports:
- 5432:5432
volumes:
- postgres:/var/lib/postgresql/data
pgadmin:
image: dpage/pgadmin4
restart: always
ports:
- 81:80
environment:
PGADMIN_DEFAULT_EMAIL: info@mebbe.info
PGADMIN_DEFAULT_PASSWORD: password
volumes:
- pgadmin:/var/lib/pgadmin
- ./db:/docker-entrypoint-initdb.d
depends_on:
- postgres
volumes:
postgres:
pgadmin:
次にdocker-compose.ymlが定義されているディレクトリ直下に新たにdbディレクトリを定義し、そのディレクトリ内にinit.sqlファイルを作成する。以下の記事を参考にした。
set client_encoding = 'UTF8';
create table Main (
questionId serial primary key,
isPublished varchar not null,
correctChoiceId varchar not null,
question varchar not null,
categories varchar not null
);
create table Choice (
choiceId serial primary key,
choiceText varchar not null,
CONSTRAINT fk_main FOREIGN KEY(main_questionId) REFERENCES main(questionId)
);
insert into Choice(choiceId, choiceText) values
('xxx', 'gcp'),
('yyy', 'aws'),
('zzz', 'azure')
;
insert into Main(questionId, isPublished, correctChoiceId, question, categories) values
('aaa', true, 'xxx', 'よく利用するクラウドサービスは?', 'cluster')
;
続く
Discussion