- 投稿日:2020-09-12T22:22:59+09:00
NestJS+TypeORMでDB接続して動作確認まで
基本的に自分用メモです.
DBの準備
今回はmysqlを使います.
予め適当なデータベースとユーザを作成して権限付与までしておきます.テーブルはまだ作らなくてOKです.
CREATE DATABASE testdb; CREATE USER 'testuser'@'localhost' IDENTIFIED BY 'password'; GRANT ALL ON testdb.* TO 'testuser'@'localhost';NestJSのプロジェクト作成
NestJSのプロジェクトを作成して簡単な動作確認まで行います.
細かいところは公式ドキュメントを参照: https://docs.nestjs.com
npm i -g @nestjs/cli nest new nest-typeorm-handson // とりあえずnpm選択して進めます nest g module user nest g controller user nest g service user
これでnest-typeorm-handson以下にこんな感じにファイルが生成されます.
src ├── app.controller.spec.ts ├── app.controller.ts ├── app.module.ts ├── app.service.ts ├── main.ts └── user ├── user.controller.spec.ts ├── user.controller.ts └── user.module.ts最低限の動作の追加
UserServiceに以下を追加します
user.service.tssample(): string { return 'UserService'; }UserControllerに以下のコンストラクタとメソッドを追加します.
user.controller.tsconstructor(private readonly service: UserService) {} @Get() get() { return this.service.sample(); }
npm run start
で起動し,ブラウザでlocalhost:3000/user
にアクセスするとUserServiceの文字が出るはずです.これで準備完了です.
TypeORMを使ってDB接続する
ここからTypeORMの出番です.
まずは準備します.
npm install --save @nestjs/typeorm typeorm mysql
そしてAppModuleの@Moduleの中身のimportsを以下のように書き換えます
app.module.tsimports: [ UserModule, // UserModuleはもともと追加されています TypeOrmModule.forRoot({ type: 'mysql', host: 'localhost', port: 3306, username: 'testuser', password: 'password', database: 'testdb', entities: [], synchronize: false, }) ],さて,ここで
npm run start
します.すると以下のようなエラーが出る場合があります
[Nest] 70420 - 2020-09-11 22:23:00 [TypeOrmModule] Unable to connect to the database. Retrying (1)... +8ms Error: ER_NOT_SUPPORTED_AUTH_MODE: Client does not support authentication protocol requested by server; consider upgrading MySQL client at Handshake.Sequence._packetToError (/Users/home/nest-typeorm-handson/node_modules/mysql/lib/protocol/sequences/Sequence.js:47:14)これはユーザの設定関連が原因なのでmysqlで以下を実行すると治ります.
ALTER USER 'testuser'@'localhost' IDENTIFIED WITH mysql_native_password BY 'password';次にEntityを作ります.src/user/user.entity.tsを作成し,以下のように作ります.
user.entity.tsimport { Entity, PrimaryColumn, Column } from "typeorm"; @Entity() export class User { @PrimaryColumn({ length: 255 }) id: string; @Column({ length: 255 }) name: string; @Column() age: number; }さらにAppModuleに先ほど書いたDBの接続情報にこのEntityを使うことを教えてあげます.
app.module.tsTypeOrmModule.forRoot({ // 一部省略 database: 'testdb', entities: [User], // <-追加 })UserModuleには以下を追加します.
user.module.tsimports: [TypeOrmModule.forFeature([User])],そして,Repositoryの準備です.UserServiceを以下のように書き換えます.コピペでOKです.
user.service.ts@Injectable() export class UserService { constructor(@InjectRepository(User) private readonly repository: Repository<User>) {} sample(): Promise<Array<User>> { return this.repository.find(); } }さて,ここで
npm run start
してlocalhost:3000/user
にアクセスするとエラーが出ます.エラー内容は
ER_NO_SUCH_TABLE: Table 'testdb.user' doesn't exist
です.そりゃあそうですね,テーブルまだ作って無いですから.マイグレーション機能でテーブルを作る
EntityなどからDDLを生成することができます.
そのために一旦先ほども書いたDBの接続情報を以下のように./ormconfig.tsにも書きます.
ormconfig.tsmodule.exports = { type: 'mysql', host: 'localhost', port: 3306, username: 'testuser', password: 'password', database: 'testdb', entities: ['src/**/*.entity.ts'], migrations: ['db/migrations/*.ts'], synchronize: false, cli: { migrationsDir: 'db/migrations' } }そしてpackage.jsonのscriptsに以下を追加します
"typeorm": "ts-node ./node_modules/.bin/typeorm --config ormconfig.ts"npm run typeorm -- -n Initializeすると./db/migrationsに'数列-Initialize.ts'というファイルが生成され,中にはDDLが書かれていることがわかります.
そして,以下のコマンドを実行するとテーブルが作成されます
npm run typeorm migration:runmysql> show tables; +------------------+ | Tables_in_testdb | +------------------+ | migrations | | user | +------------------+そして再度
npm run start
すると起動できます(もしかするとエラーが出るかもしれませんが,AppModuleのprovidersにUserServiceが指定されていたら消してください.自分の環境ではそれで治りました)
これで接続完了です.テーブルに適当にinsertしてから
localhost:3000/user
にアクセスするとinsertした内容が出力されるはずです.
- 投稿日:2020-09-12T20:16:05+09:00
node.jsでサーバーを立ててみる
はじめに
私の最初の投稿になります。
qiitaについても素人同然なので何かあれば教えていただけると幸いです。これから何回かに分けてチャットウェブアプリを作っていき、その過程をメモとして残していこうと思います。(mac 環境)
今回は node.js を用いてサーバーを立ててみます。Node.jsのインストール
まずnode.jsがインストールされているか確認をします。
node.jsのバージョンを確認します。
確認は$ node --version v12.18.3で確認。
node.jsが入っていない場合node.jsのインストールをおこなう。
node.jsのバージョン管理はnpm (Node Package Manager) か n を使って管理するとのことです。今回nを使っていきたいのですが、nを入れるために。(ここら辺もいまいちわかっていないですが)
ともかく以下のコマンドで n をインストールします$ brew install node $ npm install -g n-g オプションはグローバル環境で使えるようにするためにつけるようです。
n の簡単な使い方をまとめてみると$ n list #インストール済みのバージョン一覧表示 node/8.2.1 node/12.18.3 $ n latest #最新版をインストールする $ n バージョン #バージョンを指定してインストール $ n #何も指定しないとインストールされている中から選択する画面になるといったところです。
node.jsのパッケージ初期化
まず以下のようにしてnode.jsのパッケージの初期化を行います。
$ npm initこれを行うと
package name: (training) version: (1.0.0) description: entry point: (index.js) test command: git repository: keywords: author: license: (ISC)のよう入力を促してくるのでそれぞれ入力してエンターを押して進めていきます。
何も書かずにエンターすると()のなかのものがデフォルトで入力されます。
今回はentry pointのjsファイルの名前をserver.jsにして進めています。最後yで確定するとpackage.jsonが作られます
pacage.json{ "name": "training", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC" }node.jsのパッケージのインストール
今回使っていくパッケージをインストールしていく
今回はExpressとsocket.ioを使用していくためこの二つを以下のようにしてインストールする$ npm install express --save $ npm install socket.io --save--saveはpackage.jsonのdependenciesに書き込むオプションであるのでpackage.jsonは
余談ーーー
ちなみに--save--devとするとdevDependenciesに書き込まれるらしいです。
この違いはgit clone した時などに必要な環境を整える時にインストールするかどうかを分けているらしいです。
ーーーーーpackage.json{ "name": "mychat", "version": "1.0.0", "description": "", "main": "server.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC", "dependencies": { "express": "^4.17.1", "socket.io": "^2.3.0" } }となる。
またこれによってnode_modulesディレクトリとpackage-lock.jsonファイルが作られる。サーバーの起動
早速起動していきます。
サーバーの起動を行うファイルserver.jsを作ります(npm initでentry pointで指定した名前で作る)'use strict'; // モジュール const express = require( 'express' ); const http = require( 'http' ); const socketIO = require( 'socket.io' ); // オブジェクト const app = express(); const server = http.Server( app ); const io = socketIO( server ); // 定数 const PORT = process.env.PORT || 1337; // サーバーの起動 server.listen( PORT, () => { console.log( 'Server on port %d', PORT ); } );これについて簡単に説明していきます。
まずserver.js'use strict';これは厳格(strict)モードとして実行することの宣言のようです。
つまりエラーなどの許容を厳格にして曖昧な表現を禁止したり動作の高速化、潜在的な問題をわかりやすくするなどあるようです。
次にserver.jsconst express = require( 'express' ); const http = require( 'http' ); const socketIO = require( 'socket.io' );で使われているrequier()
これはモジュールを読み込むために使用するようです。
余談ーーー
htmlでモジュールを読み込む時は<script>タグで行うが、それができないサーバーなどでモジュールを読み込むために作られた仕様のようです。
ーーーーーserver.jsconst app = express(); const server = http.Server( app ); const io = socketIO( server );1行目のexpress() はexpressモジュールの変数の初期化。
2行目の3行目はhttp.Server(app)はhttp.ServerインスタンスにexpressとsocketIOを両立させるために行なっているようです。(この辺りの根本はまだ理解に時間がかかりそうです)server.jsconst PORT = process.env.PORT || 1337;これはポート番号の指定している。
私がjs歴がほとんどないのでおそらくですがprocess.env.PORTは環境変数PORTを示していて、これをor演算を行なっていると考えると、もし環境変数PORTがfalseを示すものだった場合(None等)数値1337を変数PORTに代入しているのだと思います。
つまり環境変数として指定すればいつでもどのファイルとの統一を測れるのではないかと考えています。server.jsserver.listen( PORT, () => { console.log( 'Server on port %d', PORT ); } );server.listen(PORT、function)はサーバーを立ち上げる関数のようです(これは正しいかちょっと自信ないです。)
PORTはポート番号の指定をして、functionは立ち上げた時に働く関数を定義するようです。
今回関数はアロー関数式で定義しています。
(valiable)=>{function}
で記述できるようです。(アロー演算子はあまり使ったことがない)
ここの関数ではconsole.log()でコンソールに標準出力しているだけです。余談ーーー
サーバーの起動はapp.listen(port)などいろんな方法があるようです。今回はexpressとsocket.ioを同じポートで待ち受けるためにこのような方法をとります。
ーーーーーこれでserver.jsの説明は以上です。
実際にサーバーを起動してみます。起動の仕方は$node server.js Server on port 1337で行う。
止める時はCtrl+Cで止める。公開フォルダ・ファイルの作成と指定
公開フォルダは実際に公開する公開ファイルを置いておくディレクトリのようです。
$ mkdir publicこれは説明しなくてもいいかと思いますが、publicディレクトリを作っています。
そしてpublicディレクトリの中に実際に表示するindex.htmlファイルを以下のように記述します。public/index.html<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta charset="utf-8" /> <title>My Chat</title> </head> <body> <h1>My Chat</h1> </body> </html>htmlについては別にまとめるつもりなのでそちらを参照
続いて公開フォルダを指定します。
server.jsのサーバーの起動の前にserver.jsapp.use(express.static(__dirname+'/public'));express.static関数に公開ディレクトリのパスを渡してあげて指定するようです。
ここはまた今度調べますサーバーにアクセス
実際にサーバーを起動してみてからブラウザの検索画面に"localhost:1337"を入力してアクセスする。
うまくindex.htmlが出てくれば成功です。クライアントから接続要求と、接続時の処理の作成
この辺りの通信あたりは苦手意識があって忘れてるところも多いので今後勉強していきますので今回は簡単に流します……ごめんなさい
publicディレクトリのなかにクライアントから呼び出すjsファイルclient.jsを作る。public/client.js// クライアントからサーバーへの接続要求 const socket = io.connect(); // 接続時の処理 // ・サーバーとクライアントの接続が確立すると、 // サーバー側で、'connection'イベント // クライアント側で、'connect'イベントが発生する socket.on( 'connect', () => { console.log( 'connect' ); } );呼び出された時に実行する関数をアロー関数で定義していると思います。
そしてhtmlファイルのbodyの末尾に以下を記述しますpublic/index.html<script src="/socket.io/socket.io.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <script src="client.js"></script>そして最後にserver.jsの公開フォルダの指定の上に
server.js// 接続時の処理 // ・サーバーとクライアントの接続が確立すると、 // サーバー側で、'connection'イベント // クライアント側で、'connect'イベントが発生する io.on( 'connection', ( socket ) => { console.log( 'connection' ); } );を記述する。
これでもう一度サーバーを立ててブラウザから"localhost:1337"にアクセスすると、ブラウザの検証のコンソールとサーバーのコンソールにそれぞれ指定した標準出力が表示されれば成功となります。server.jsを色々書き加えたのでここまでの更新状況を置いておきます。
server.js'use strict'; // モジュール const express = require( 'express' ); const http = require( 'http' ); const socketIO = require( 'socket.io' ); // オブジェクト const app = express(); const server = http.Server( app ); const io = socketIO( server ); // 定数 const PORT = process.env.PORT || 1337; // 接続時の処理 // ・サーバーとクライアントの接続が確立すると、 // サーバー側で、'connection'イベント // クライアント側で、'connect'イベントが発生する io.on( 'connection', ( socket ) => { console.log( 'connection' ); } ); // 公開フォルダの指定 app.use( express.static( __dirname + '/public' ) ); // サーバーの起動 server.listen( PORT, () => { console.log( 'Server on port %d', PORT ); } );終わりに
今回はチャットウェブアプリを作る前段階としてnode.jsでサーバーを立ててみました。
前にPHPを使ってサーバーを立ててみた時はapacheなど書くことが多く難しい、苦手、といった意識があったのですがnode.jsを使った時は意外と簡単に立てられたのが意外でした。
でもサーバー周りはなかなか理解が追いついてなかったり忘れてたりするのでこれを機に進めながら勉強し直していきたいと考えています。[参考]
https://www.hiramine.com/programming/chat_nodejs_socketio/01_connect.html [クライアントとサーバーの接続時の処理を作る]
https://techacademy.jp/magazine/16151 [初期化処理を行う!npm initの使い方【初心者向け】]
https://qiita.com/heyheyww/items/092fcbc490a249a2d05c [npm install の --save-dev って何?]
https://qiita.com/havveFn/items/c5beda8572aa8c1e6be6 [npm install時に「--save」オプションはいらない]
https://qiita.com/miri4ech/items/ffcebaf593f5baa1c112 [【JavasScript】use strictとは]
https://qiita.com/uryyyyyyy/items/b10b012703b5396ded5a [require()とは何か?何が便利なのか]
https://teratail.com/questions/8894 node.jsでrequire('http');する理由
https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q14142502624 知恵袋
https://nodejs.org/api/net.html#net_server_listen_port_host_backlog_callback Node.js v14.10.1 Documentation
https://expressjs.com/ja/starter/static-files.html Express での静的ファイルの提供
- 投稿日:2020-09-12T17:53:39+09:00
Docker ComposeでNode.jsの環境構築
動機
Create React Appを少し遠回りしてはじめようをローカル環境を汚さずに実践したかっため、タイトルのような環境構築を目指しました。
最終的なディレクトリ構成
最終的なディレクトリ構成node-docker/ |--docker-compose.yml |--node/ |--app/docker-compose.yml
Docker Compose 概要
Compose とは、複数のコンテナを定義し実行する Docker アプリケーションのためのツールです。まずプロジェクトフォルダ
node-docker
を作成し、そこにdockercompose.yml
ファイルを作成します。現在のディレクトリ構成node-docker/ |--docker-compose.ymldocker-compose.ymlversion: '3' services: node: image: node:14.9.0-alpine3.10 container_name: node volumes: - ./node/app:/app tty: true ports: - 3000:3000version
docker-compose.yml
のファイルフォーマットのバージョンservices
各コンテナをサービスとして定義できます。
node
サービス名
image
コンテナを実行時に元となるイメージを指定します。
イメージが存在していなければ、ComposeはDocker Hubからpull(取得)を試みます。
Dockerfile
からイメージを作成する場合は、例えばnode
ディレクトリの下にDockerfile
を作成し、以下のように指定します。
build: ./node
container_name
デフォルトで生成される名前の代わりに、カスタム・コンテナ名を指定します。なお、デフォルトだとnode-docker_node_1という名前になります。
volumes
ローカルのパス(左)とコンテナのパス(右)を共有します。
tty
端末を起動するかどうかを指定しています。(多分)
ports
ポートを公開します。ホストとポートを指定(ホスト:コンテナ)するか、コンテナのポートのみ指定します(ホスト側のポートはランダムに選ばれます)。
expose
はポートを露出し、リンクされたサービス間でのみアクセス可能になります。コンテナの作成と起動
ここまでで必要なファイルが揃ったので、コンテナの作成と起動を行っていきます。
Dockerfile
からイメージを作成する場合は、Docker-compose build
する必要がありますが、今回は既にビルドされたイメージをリモートから取得するので必要ありません。
作業はdocker-compose.yml
が置いてあるディレクトリ上で行います。terminal$ docker-compose up -d
d
オプションにより、バックグラウンドで起動することができます。nodeコンテナに入る
以上でnode環境は整いましたが、node環境はコンテナ上にあるため、作業はその上でする必要があります。
そのために、起動中のコンテナ内で指定したコマンドを実行できるexec
コマンドを利用して、コンテナ上でシェル、端末を起動します。terminal$ docker-compose exec node shコンテナ内のディレクトリ構成は以下のようになっていて、
docker-compose.yml
で指定したためコンテナでのapp
ディレクトリとローカルのnode/app
ディレクトリは対応しています。コンテナ内のディレクトリ構成/ |--app |--bin |--dev |--home |--lib |--media |--mnt |--opt |--proc |--root |--run |--sbin |--srv |--sys |--tmp |--usr |--var作業は、ローカルと共有できているappディレクトリでした方が良いと思います。
参考
Compose file version 3 reference
Compose ファイル・リファレンス
Docker Compose - docker-compose.yml リファレンス
【初心者向け】Dockerで手軽にNode.js開発環境構築 (2)
- 投稿日:2020-09-12T15:05:49+09:00
Node.jsのバージョンあげたら「Node Sass could not find a binding for your current environment」とでた場合
概要
Node.jsのメジャーバージョンをあげると、node-sassがエラーをはくことがある
「Node Sass could not find a binding for your current environment」
そのときは、node-sassをリビルドして自分のnode環境に合った状態にする
対処法
npm rebuild node-sassこれでOK
そのほか対処法
それでもだめなら
npm rebuild node-sass --forceそれでもだめなら、いったんnode-sass消してから。いれなおす。
(入れ直しでも、環境にあった適切なbinaryが生成される)npm uninstall node-saas npm install node-sass --save-dev
- 投稿日:2020-09-12T15:04:05+09:00
【初心者向け】API利用の基本テクニック(ローディングの描画と標準時間の変換について)
背景とこの記事の目的
この度、TwitterAPIを利用したWEBアプリを構築しました。
その際に、「あーこの技術は今後も間違いなく活用するだろうなー」と思ったものがいくつかあったので、備忘として残します。
便利なライブラリもあったのでそちら紹介も兼ねます。
※フロントはvueで作っていますので、その前提で以下のトピックを紹介していきます。目次
1.非同期通信の際のローディングでぐるぐるさせるやつ(スピナー、インジケーター)
2.APIのレスポンスの中にある時刻を日本時間に変換する非同期通信の際のローディングでぐるぐるさせるやつ(スピナー、インジケーター)
こういうの実装したいと思ったことは皆さん誰しも一度はあるでしょう。
API通信で時間のかかる処理などでは非常に重宝します。もし皆さんがvueを使っているのであれば、「vue-loading-overlay」をおすすめします。
詳しい使い方は以下が参考になります。
https://www.npmjs.com/package/vue-loading-overlay
https://www.kabanoki.net/4916/カスタマイズもある程度できますし、比較的コードの記述量も少ないです。
axiosと組み合わせると以下のような書き方になります。App.vue//scriptタグ内の記述 import Loading from 'vue-loading-overlay'; import 'vue-loading-overlay/dist/vue-loading.css'; export default { name: 'app', data(){ return { isLoading: false, fullPage: true, } }, components:{ "loading":Loading }, methods:{ getTimeline:function(){ alert('Timelineを取得します'); //overlayの処理 let self = this; self.isLoading = true; axios .get('APIのURL', { headers: { //必要に応じて記載 }, params:{ //必要に応じて記載 } }) .then(response => { // handle success self.isLoading = false; }) .catch(function (error) { // handle error self.isLoading = false; console.log(error); }) }, onCancel:function() { console.log('User cancelled the loader.') } //以下略App.vue<!-- templete内の記述 --> <loading :active.sync="isLoading" :can-cancel="true" :on-cancel="onCancel" :is-full-page="fullPage"></loading> <button v-on:click="getTimeline" class= "btn btn-primary"> Timelineを取得 </button>APIのレスポンスの中にある時刻を日本時間に変換する
TwitterのAPIのレスポンスの中に
"created_at": "Thu Apr 06 15:28:43 +0000 2017"
こういうものがありました。
APIのレスポンスには、必ずと言っていいほど時間に関する情報が入っています。そして日本のサービス出ない限り、大抵は標準時で入っています。
これを日本時間に変換する処理をする際は「Moment.js」というライブラリが便利です。詳しい使い方は下記が参考になります。
https://qiita.com/osakanafish/items/5ef636bbcb2c3ef94953
https://momentjs.com/実際の例としては以下の通り
App.vueconst moment = require('moment'); require('moment-timezone'); //略(APIの処理) .then(response => { let obj = JSON.parse(response.data); moment.tz.setDefault('Asia/Tokyo'); //日本時間に合わせる for(var t=0; t<obj.length; t++){ obj[t].created_at = moment(obj[t].created_at,'dd MMM DD HH:mm:ss ZZ YYYY','en').format('YYYY-MM-DD_HH:mm:ss'); }日付の記法は地域(というか国?)によってほんとに様々あるようで、それに応じてコーディングも微妙に変える必要があるようですね。
まとめ
実際に作ってみるといろいろと細かいテクニックが必要になることがよくわかりました。
ボタンの活性・非活性の制御、モーダルウィンドウ(子画面)の作り方、無限スクロールの実装の仕方などなど
上げればキリがないのですが、有用そうなものは時間があるときに記事にして残そうと思います