20200630のNode.jsに関する記事は11件です。

React.jsをDockerで動かした時のメモ

環境

$ cat /etc/linuxmint/info 
RELEASE=19.3
CODENAME=tricia
EDITION="Cinnamon"
DESCRIPTION="Linux Mint 19.3 Tricia"
DESKTOP=Gnome
TOOLKIT=GTK
NEW_FEATURES_URL=https://www.linuxmint.com/rel_tricia_cinnamon_whatsnew.php
RELEASE_NOTES_URL=https://www.linuxmint.com/rel_tricia_cinnamon.php
USER_GUIDE_URL=https://www.linuxmint.com/documentation.php
GRUB_TITLE=Linux Mint 19.3 Cinnamon

Dockerのインストール

$ apt update
$ apt install docker-ce docker-ce-cli docker-compose

node.jsとcreate-react-appのインストール

$ apt install nodejs
$ npm install create-react-app yarn

サンプルReact.jsアプリを作る

$ create-react-app docker-test
$ cd docker-test

とりあえず普通に起動してみる。

yarn start

ブラウザで http://localhost:3000 にアクセスしReact.jsが動作していることを確認。
確認できたらCtrl-Cで落とす。

Dockerfileとdocker-compose.ymlを作成

作成したReactアプリのルートディレクトリに以下のような内容で Dockerfiledocker-compose.yml を作る。

Dockerfile
FROM node:14.4.0-buster-slim
WORKDIR /usr/src/app
RUN npm install --save prop-types
RUN npm install -g create-react-app
docker-compose
version: '3'
services:
  node:
    build:
      context: .
    tty: true
    environment:
      - NODE_ENV=production
    volumes:
      - ./:/usr/src/app
    command: sh -c "yarn start"
    ports:
      - "3000:3000"

上記のような内容だと、作成したReactアプリのルートディレクトリがDocker環境内では /usr/src/app にマウントされるため、 WORKDIRを /usr/src/app に指定してあれば、実行するスクリプトは sh -c "yarn start" だけとなるため、これを command で指定している。

docker-hub nodeを見に行くと、alpine buster stretch などの名前がバージョンについています。なんのことかと思ったらLinuxディストリビューションですね。

slim とついているものは、Debianディストリビューションでエクストラパッケージが含まれないものです。いずれにしても今回はDockerでnodeが動けばいいので node:14.4.0-buster-slim を選択しました。
ちなみにDebianの現在のバージョンは buster(10.0) であり、それ以前は以下のようになります。

Debian 10 (buster) — 現在の安定版リリース
Debian 9 (stretch) — 過去の安定版リリース
Debian 8 (jessie) — 過去の安定版リリース
Debian 7 (wheezy) — 過去の安定版リリース

alpine linuxはパワーユーザー向けの軽量Linuxのようです。軽量だけにイメージが小さく抑えられるかもしれませんが、私はDebianやUbuntuしか使ったこと無いので alpine の使用はやめておきました。

Dockerイメージをビルド

$ docker-compose build

DockerでReact.jsアプリを実行する

起動

$ docker-compose up

ブラウザで http://localhost:3000 にアクセスしReact.jsが動作していることを確認。
Ctrl-C で停止。

バックグラウンドで起動

$ docker-compose up -d

停止

$ docker-compose down

イメージ情報を確認

$ docker images | grep dockertest
dockertest_node     latest               daf22d9465c7        26 minutes ago      194MB

おまけ

すべてのDockerイメージを停止

$ docker stop $(docker ps -q)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Node.jsのExpressでbody-parserを使いたくないけど特定のハンドラーでだけボディのパースをしたい

特定のURL以下だけパースしたいが、その他の広いパスではパースしたくないみたいなことが5年に1度ぐらい発生するかもしれないので、そのやり方のメモ。例えば99%のリクエストはプロキシーとしてそのまま他のプロセスに流す場合、パース作業はほぼ無駄になってしまうので必要なところでだけやりたいですよね?routerを作ってその下だけbody-parserを適用とかできるならそれでもいいかもしれませんが、微妙にそれもしにくい・・・みたいな場合に。

app.ts
import express, { Request, Response } from 'express';
// 本当はimportしなくてもいいはずだけどTypeScriptはimportしないとエラーになるっぽい
import { URLSearchParams } from 'url';

app.post('/form/receive', (req: Request, res: Response) => {
    let body = '';
    // streamのAPIを直接扱ってdata/endで情報を読み込む
    req.setEncoding('utf8');
    req.on('data', (chunk) => {
        body += chunk;
    });
    req.on('end', () => {
        // フォームのポストを受け取るのでURLSearchParams.
        // JSONならJSON.parse
        const params = new URLSearchParams(body);
        console.log(params); // パースできてる!
        res.end();
    });
});

const port = process.env.NODE_PORT || 3000;
console.log(`listening at ${port}`);`
app.listen(port);
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Alexa]stylesを使って押したら色が変わるボタンを作成する

stylesを使うとうれしいこと

APLのドキュメント部では、layoutsやcommandsのように共通要素を外だしすることができます。
これと同様に、stylesを使用すると、コンポーンントの特定プロパティの値だけを取り出して定義・共通化することができます。
さらに、stylesでは「対象のコンポーネントが押下されたときだけ色を変える」といったことも可能です。
本記事では、APLでstylesを利用した例を挙げてみようと思います。

APLドキュメントファイルの作成

以下は、stylesを利用したAPLドキュメントファイルの例です。
"styles"にてスタイル"pushButtonStyle"を定義し、Frame要素にて"pushButtonStyle"を適用しています。

StyleTestTemplate.json
{
    "type": "APL",
    "version": "1.3",
    "settings": {},
    "theme": "dark",
    "import": [],
    "resources": [],
    "styles": {
        "pushButtonStyle": {
            "value": [
                {
                    "backgroundColor": "#564387"
                },
                {
                    "when": "${state.pressed}",
                    "backgroundColor": "#ac8fee"
                },
                {
                    "when": "${state.focused}",
                    "borderColor": "#ffffff",
                    "botderWidth": 10
                }
            ]
        }
    },
    "onMount": [],
    "graphics": {},
    "commands": {},
    "layouts": {},
    "mainTemplate": {
        "parameters": [
            "payload"
        ],
        "items": [
            {
                "type": "Container",
                "width": "100vw",
                "height": "100vh",
                "alignItems": "center",
                "items": [
                    {
                        "type": "TouchWrapper",
                        "width": "50%",
                        "height": "20%",
                        "items": [
                            {
                                "type": "Frame",
                                "style": "pushButtonStyle",
                                "inheritParentState": true,
                                "width": "100%",
                                "height": "100%",
                                "items": [
                                    {
                                        "type": "Text",
                                        "width": "100%",
                                        "height": "100%",
                                        "textAlign": "center",
                                        "text": "色が変わるよ!",
                                        "textAlignVertical": "center"
                                    }
                                ],
                                "borderWidth": "2"
                            }
                        ]
                    }
                ],
                "justifyContent": "center"
            }
        ]
    }
}

こちらの例では、
 ・普段は背景色が#564387
 ・pressed時( = 押された時)は背景色が#ac8fee
 ・focused時( = フォーカスが合った時)は枠の色が#ffffff
というスタイルを定義し、Frameにセットしています。

上記jsonではFrameの"inheritParentState"がtrueに設定されており、親コンポーネントであるTouchWrapperの状態を継承するため、押された状態やフォーカスがあった時には色が変化します。

他に、styles関連でのポイントとしては以下あたりが挙げられます。

・スタイルには、こちらの「スタイル設定可能なプロパティ」に該当するプロパティのみ設定できる。
・上記に該当するプロパティであっても、コンポーネントが持っていないプロパティは反映されない。
 例:Containerに対して"backgroundColor"の値がセットされているスタイルを設定しても背景色は変わらない
・コンポーネントに直接値がセットされているプロパティは、スタイルに記載された設定よりも優先される。
 (→上記の例だと、Frameに直接backgroundColorの値をセットしてしまうと、StyleのbackgroundColorは機能しなくなる)

Node.js実装例

Node.jsに実装すると、以下のような感じになります。
上記のStyleTestTemplate.jsonがaplフォルダ内に配置されている前提となります。

index.js
// 対話モデルにてStyleTestIntentを作成しておく
// StyleTestIntent発話時にこのハンドラに入る想定
const StyleTestIntentHandler = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
            && Alexa.getIntentName(handlerInput.requestEnvelope) === 'StyleTestIntent';
    },
    handle(handlerInput){
        let st = 'スタイルのテストです。';

        // APLドキュメントとデータソースを取得
        const StyleTestTemplate = require('./apl/StyleTestTemplate.json');

        // 画面作成 ※実際はAPL対応デバイスかどうかの確認が必要
        handlerInput.responseBuilder.addDirective({
            type: 'Alexa.Presentation.APL.RenderDocument',
            version: '1.0',
            document: StyleTestTemplate,
            datasources: {}        // データソース部不要のため空のオブジェクトをセット
        });

        return handlerInput.responseBuilder
            .speak(st)
            .reprompt(st)
            .getResponse();
    }
}

// 以下略…

この状態でスキルを実行すると、スタイル設定が反映されたボタンが画面に表示されると思います。
また、ボタンをタッチするとボタンの色が変わります。
style.gif

補足

「focusedってどういう状態だよ、pressedは分かるけどフォーカスが合ってる状態ってなんだよ」と思う方もいるかもしれません。

FireTVからこのAPLで描写した画面を開いて見てみると、分かると思います。
FireTVをお持ちでない方向けに簡単に説明すると、
以下のような感じでリモコン操作のUIを構築することができます。

参考URL

APL Style Definition and Evaluation | Alexa Skills Kit
APLスタイル設定可能プロパティ | Alexa Skills Kit

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

express+postgreSQL+Herokuでアプリ公開した手順

使用するパッケージがexpress,pg(とejs)のみで作成したWebアプリを、Herokuにて公開するまでをまとめました。git自体の詳細は省いたりしていますが、基本この通りやっていけば公開まではいけると思います。
全体的に情報が古めだったけど、良いサイトないかな。

OS:Windows10
terminal:powershell
テキストエディタ:VScode

1.準備

1-1.ローカル環境でexpress+postgreSQLのアプリ作成

この記事は主にローカル環境で出来上がっているけど公開の手順が分からない人向けなので、ここは省略します。

1-2.Herokuアカウント作成

https://dashboard.heroku.com

1-3.HerokuのCLIを使えるようにする。

https://devcenter.heroku.com/articles/heroku-cli
でDLしたあとpathを通す。

terminal
> heroku -v
heroku/7.42.1 win32-x64 node-v12.16.2

などでバージョン確認ができればok。

2.Herokuアプリに接続する

ここから先はだいたいこのサイトを参考にしてます。追加情報が必要なときのみその時の参考サイトも貼りました。
https://tacamy.hatenablog.com/entry/2013/02/16/235127

2-1.Herokuへログインする
terminal
> heroku login

ブラウザへ飛ばされたあと、そこに「成功」的な画面が出ていればok。

2-2.Herokuにアプリ作成
terminal
> heroku create

アプリ名にこだわるなら別の手順が必要ですが、僕はどうせheroku.appってurlに入るからどうでもいいかなと思って素のままいきました。

2-3.Procfile作成

app.js(自分のjsファイル名)が置かれている層にPricfile(拡張子なし)というページを作り、以下の内容のみ記述します。

Procfile
web: node app.js

WSGIという、Webアプリを動かす指示書的なものらしい。詳しくはhttps://creepfablic.site/2019/05/01/heroku-procfile/

2-4.gitignore作成

先程のProcfile,app.jsと同じ層に.gitignoreを作成して、以下の内容を記述。nodeのパッケージ等はHeroku側で用意されているので、gitでアップロードしないように制御する。

.gitignore
node_modules

現在はこのような位置関係になっていると思います。

-app.js
-package.json
-Procfile
-.gitignore
-node_module
-public
...etc

2-5.package.jsonにengineの記述追加

特定のバージョンのみでしか動かないようにするために、package.jsonのdependenciesの後に以下の内容を記述。

package.json
...
  "dependencies": {
    "ejs": "^3.1.3",
    "express": "^4.17.1",
    "pg": "^8.2.1",
  },
  "engines": {
    "node": "^12.17.0", //自分のバージョン
    "npm":  "^6.14.4" // 自分のバージョン
  }
...

https://qiita.com/suin/items/994458418c737cc9c3e8

3.HerokuのDBへ接続する

3-1.Heroku上でDBを追加

Herokuのサイトに行き、アプリ名をクリック→Resources→Add-onsからHeroku Postgresを検索し、追加

3-2.各種データの確認

SettingsのReveal config varでURLを確認し、各種必要な値を得て、ローカル環境でのものと置き換える。

URLの構成:postgres:// ユーザー名:パスワード @ホスト名:ポート番号/データベース名
app.js
const pool = new Pool({
  user: 'ユーザー名',
  host: 'ホスト名',
  database: 'データベース名',
  password: 'パスワード',
  port: 5432, //ポート番号
});

https://qiita.com/shosho/items/5ebabf11efb1f3b604f7#url%E3%81%AE%E6%A7%8B%E6%88%90

3-3.portをHeroku用に変える

ローカル環境だとapp.listen(3000)みたいになってるところを以下の内容に変更。

app.js
let port = process.env.PORT || 3000;
app.listen(port, () => {
    console.log("Listening on " + port); //ここは要らないけど一応
});

適当な人のgithubからパクった。
https://github.com/FTraian/node-express-postgresql-heroku-tutorial/blob/master/web.js

4.gitでファイルをアップロードする

4-1.git commitまで
terminal
> git init
> git add .
> git commit -m "first commit"
4-2.公開鍵を設定(一番面倒でした)

ここは僕自身理解度が低いですが、ひとまず僕が成功した手順を書いておきます。
まず、公開鍵が設定されていないことを確認します。

terminal
> heroku keys
!  You have no SSH keys.

確認できたら、ssh公開鍵を作成します。"xxx@gmail.com"の部分はherokuに登録したメールアドレスを入力します。

terminal
> ssh-keygen -t rsa -C "xxx@gmail.com"

すると以下のようにでてくるので、Enterキーを押したりpassphraseを設定したりします。

terminal
Generating public/private rsa key pair.
Enter file in which to save the key (C:\Users\ユーザー名/.ssh/id_rsa):  //そのままEnterキー
Created directory 'C:\Users\ユーザー名/.ssh'.
Enter passphrase (empty for no passphrase): //パスフレーズを入力(忘れないように)

その後、Herokuに登録して完了です。

terminal
> heroku keys:add C:\Users\ユーザー名\.ssh\id_rsa.pub //id_rsa.pubがあるファイル名を指定
> heroku keys
=== xxx@gmail.com keys
...(公開鍵) xxx@gmail.com

https://daipresents.com/2012/post-5010/
https://knowledge.sakura.ad.jp/3543/

4-3.git push

初回のみ以下のコマンド。

terminal
> git remote add heroku git@アプリ名.git

あとはいつもどおり。

terminal
> git push origin master

ここまでで、自分のアプリのURLをクリックしたら開けると思います。ありがとうございました。

おまけ

ローカル環境からDBをいじるには
terminal
> heroku pg:psql -a  アプリ名
アプリ名::DATABASE=>

 
https://sysrigar.com/2019/01/20/heroku-postgres%E3%82%92%E3%83%AD%E3%83%BC%E3%82%AB%E3%83%AB%E7%92%B0%E5%A2%83%E3%81%8B%E3%82%89%E6%89%8B%E5%8B%95%E3%81%A7%E6%93%8D%E4%BD%9C%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95%E3%81%AB%E3%81%A4/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

express+postgreSQL+HerokuでWebアプリを公開した手順

使用するパッケージがexpress,pg(とejs)のみで作成したWebアプリを、Herokuにて公開するまでをまとめました。git自体の説明は省いたりしていますが、基本この通りやっていけば公開まではいけると思います。
全体的に情報が古めだったけど、良いサイトないかな。

OS:Windows10
terminal:powershell
テキストエディタ:VScode

1.準備

1-1.ローカル環境でexpress+postgreSQLのアプリ作成

この記事は主にローカル環境で出来上がっているけど公開の手順が分からない人向けなので、ここは省略します。

1-2.Herokuアカウント作成

https://www.heroku.com/

1-3.HerokuのCLIを使えるようにする

https://devcenter.heroku.com/articles/heroku-cli
でDLしたあとpathを通す。

terminal
> heroku -v
heroku/7.42.1 win32-x64 node-v12.16.2

などでバージョン確認ができればok。

2.Herokuアプリに接続する

ここから先はだいたいこのサイトを参考にしてます。追加情報が必要なときのみその時の参考サイトも貼りました。
https://tacamy.hatenablog.com/entry/2013/02/16/235127

2-1.Herokuへログインする
terminal
> heroku login

ブラウザへ飛ばされたあと、そこに「成功」的な画面が出ていればok。

2-2.Herokuにアプリ作成
terminal
> heroku create

アプリ名にこだわるなら別の手順が必要ですが、僕はどうせheroku.appってurlに入るからどうでもいいかなと思って素のままいきました。

2-3.Procfile作成

app.js(自分のjsファイル名)が置かれている層にPricfile(拡張子なし)というページを作り、以下の内容のみ記述します。

Procfile
web: node app.js

WSGIという、Webアプリを動かす指示書的なものらしい。詳しくはhttps://creepfablic.site/2019/05/01/heroku-procfile/

2-4.gitignore作成

先程のProcfile,app.jsと同じ層に.gitignoreを作成して、以下の内容を記述。nodeのパッケージ等はHeroku側で用意されているので、gitでアップロードしないように制御する。

.gitignore
node_modules

現在はこのような位置関係になっていると思います。

-app.js
-package.json
-Procfile
-.gitignore
-node_module
-public
...etc

2-5.package.jsonにengineの記述追加

特定のバージョンのみでしか動かないようにするために、package.jsonのdependenciesの後に以下の内容を記述。

package.json
...
  "dependencies": {
    "ejs": "^3.1.3",
    "express": "^4.17.1",
    "pg": "^8.2.1",
  },
  "engines": {
    "node": "^12.17.0", //自分のバージョン
    "npm":  "^6.14.4" // 自分のバージョン
  }
...

https://qiita.com/suin/items/994458418c737cc9c3e8

3.HerokuのDBへ接続する

3-1.Heroku上でDBを追加

Herokuのサイトに行き、アプリ名をクリック→Resources→Add-onsからHeroku Postgresを検索し、追加

3-2.各種データの確認

SettingsのReveal config varでURLを確認し、各種必要な値を得て、ローカル環境でのものと置き換える。

URLの構成:postgres:// ユーザー名:パスワード @ホスト名:ポート番号/データベース名
app.js
const pool = new Pool({
  user: 'ユーザー名',
  host: 'ホスト名',
  database: 'データベース名',
  password: 'パスワード',
  port: 5432, //ポート番号
});

https://qiita.com/shosho/items/5ebabf11efb1f3b604f7#url%E3%81%AE%E6%A7%8B%E6%88%90

3-3.portをHeroku用に変える

ローカル環境だとapp.listen(3000)みたいになってるところを以下の内容に変更。

app.js
let port = process.env.PORT || 3000;
app.listen(port, () => {
    console.log("Listening on " + port); //ここは要らないけど一応
});

適当な人のgithubからパクった。
https://github.com/FTraian/node-express-postgresql-heroku-tutorial/blob/master/web.js

4.gitでファイルをアップロードする

4-1.git commitまで
terminal
> git init
> git add .
> git commit -m "first commit"
4-2.公開鍵を設定

ここは僕自身理解度が低いですが、ひとまず僕が成功した手順を書いておきます。
まず、公開鍵が設定されていないことを確認します。

terminal
> heroku keys
!  You have no SSH keys.

確認できたら、ssh公開鍵を作成します。"xxx@gmail.com"の部分はherokuに登録したメールアドレスを入力します。

terminal
> ssh-keygen -t rsa -C "xxx@gmail.com"

すると以下のようにでてくるので、Enterキーを押したりpassphraseを設定したりします。

terminal
Generating public/private rsa key pair.
Enter file in which to save the key (C:\Users\ユーザー名/.ssh/id_rsa):  //そのままEnterキー
Created directory 'C:\Users\ユーザー名/.ssh'.
Enter passphrase (empty for no passphrase): //パスフレーズを入力(忘れないように)

その後、Herokuに登録して完了です。

terminal
> heroku keys:add C:\Users\ユーザー名\.ssh\id_rsa.pub //id_rsa.pubがあるファイル名を指定
> heroku keys
=== xxx@gmail.com keys
...(公開鍵) xxx@gmail.com

https://daipresents.com/2012/post-5010/
https://knowledge.sakura.ad.jp/3543/

4-3.git push

初回のみ以下のコマンド。

terminal
> git remote add heroku git@アプリ名.git

あとはいつもどおり。

terminal
> git push origin master

ここまでで、自分のアプリのURLをクリックしたら開けると思います。ありがとうございました。

おまけ

ローカル環境からDBをいじるには
terminal
> heroku pg:psql -a  アプリ名
アプリ名::DATABASE=>

 
https://sysrigar.com/2019/01/20/heroku-postgres%E3%82%92%E3%83%AD%E3%83%BC%E3%82%AB%E3%83%AB%E7%92%B0%E5%A2%83%E3%81%8B%E3%82%89%E6%89%8B%E5%8B%95%E3%81%A7%E6%93%8D%E4%BD%9C%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95%E3%81%AB%E3%81%A4/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Angular 'Tour of Heroes' でMEANスタック

始めに

前回の記事MEANスタックの一歩前。Angular と Express の連携の続きです。
Angular の公式チュートリアル、'Tour of Heroes'をMEANスタック化します。

前回はAngular の公式からダウンロードした'Tour of Heroes'を若干修正して実際のサーバにアクセスするようにしました。
その後Expressサーバーを構築しました。
ですがデータベースは使用しておらず、サーバー内に配列としてヒーローのリストを持つのみとしていました。

今回はWindows環境ですが、MongoDBをセットアップしてMEANの「M」の部分を完成させたいと思います。

MongoDB のインストール

まずはMongoDBのダウンロードを行います。MongoDB公式で、「Available Downloads」に必要な環境情報を登録して「Download」をクリック。インストールパッケージをダウンロードします。私が使用したパッケージファイル名は「mongodb-win32-x86_64-2012plus-4.2.7-signed.msi」でした。

ダウンロードしたら、一応ウイルスチェックを行いましょう。
公式サイトでウイルスを検出してしまうことは経験上ありませんが、念のため。

続けてインストールを進めていきます。
今回は、すべてデフォルト設定を受け入れるだけなので難しい部分はありませんでした。

では早速、インストールを進めていきます。
まずはインストーラをダブルクリックで起動。
0001.png
Nextで次に進みます。

0002.png
ライセンスに同意する必要があります。

0003.png
Complete を選択します。

0004.png
問題が無ければデフォルト設定のまま、Nextをクリックします。

0005.png
GUI環境であるCompassを使用するため、チェックしたままNextをクリックします。

0006.png
Install をクリックしてインストールを開始します。

0007.png
インストール作業の途中でMongoDBのGUIツールであるCompasが起動しました。
0008.png
完了です。
自動で起動したCompasの画面です。
image.png

MongoDBにデータベースを登録

'Tour of Heroes'で使用するデータベースをMongoDBに登録します。
先ほどのCompasの画面中央にある緑のConnectボタンをクリックします。
1002.png
画面上部の「CREATE DATABASE」をクリックします。

1003.png
データベースの名前の登録を求められるので、「Database Name」と「Collection Name」に"heroes"と入力し、「CREATE DATABASE」をクリックします。

1004.png
heroes のデータベースが追加されているのが分かります。
「Heroes」をクリックします。

1005.png
データベースのコレクションとしても、先ほど登録したheroes が登録されているのが分かります。
さらにheroesをクリックします。
1006.png
すると、データが何もないことが分かります。
ここに新しいデータを登録していきます。

このタイミングでデータを登録しなくても、この後で作成する
MEANスタックの完成形から1項目ずつ登録する事で、'Tour of Heroes'の
データを再現する事もできます。

1007.png
「ADD DATA」をクリックし、ドロップダウンの「Insert Document」をクリックします。
1008.png
この様にJSON形式でデータを登録します。
ちなみに、数値以外の項目はすべて「"」でくくる必要があります。
チョットだけはまりポイントでした。コピペ用にテキストも張っておきます。

[
{ "id": 11, "name": "Dr Nice" },
{ "id": 12, "name": "Narco" },
{ "id": 13, "name": "Bombasto" },
{ "id": 14, "name": "Celeritas" },
{ "id": 15, "name": "Magneta" },
{ "id": 16, "name": "RubberMan" },
{ "id": 17, "name": "Dynama" },
{ "id": 18, "name": "Dr IQ" },
{ "id": 19, "name": "Magma" },
{ "id": 20, "name": "Tornado" }
]
1009.png
この様に、データが登録されました。

Express のMongoDB 対応

まずは Node でMongoDBにアクセスするため mongoose のモジュールを追加します。
コマンドプロンプトでプロジェクトのディレクトリに移動し、

D:\angular\toh-srv>npm install mongoose

として、mongooseをサーバープログラムに追加します。

次にソースファイルを修正していきます。
MongoDB に登録したデータベース対応するため、モデルを定義します。
前回の記事MEANスタックの一歩前。Angular と Express の連携のソースファイルに
model.js を追加します。

toh-srv\model.js
const mongoose = require('mongoose');

mongoose.connect("mongodb://localhost/heroes");

const HeroesModelSchema = new mongoose.Schema({
  id: Number,
  name: String
});

module.exports = mongoose.model('heroes', HeroesModelSchema);

この時、

mongoose.connect("mongodb://localhost/heroes");

の heroes は、MongoDBにデータベースを登録で登録した「Database Name」に対応しています。

続けて、サーバーのメインプログラムを修正していきます。

index.js
const express = require('express'); // expressモジュールを読み込む
const bodyParser = require('body-parser');  // body-parserモジュールを読み込む 
const multer = require('multer'); // multerモジュールを読み込む

var HeroesModel = require('./model');

const app = express(); // expressアプリを生成する
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())

// heroes リストデータ
/*const heroes  = [
    { id: 11, name: 'Dr Nice' },
    { id: 12, name: 'Narco' },
    { id: 13, name: 'Bombasto' },
    { id: 14, name: 'Celeritas' },
    { id: 15, name: 'Magneta' },
    { id: 16, name: 'RubberMan' },
    { id: 17, name: 'Dynama' },
    { id: 18, name: 'Dr IQ' },
    { id: 19, name: 'Magma' },
    { id: 20, name: 'Tornado' }
  ];
//*/
// 新しいヒーローIDを作成.
function genId(){
    return new Promise(function (resolve, reject){
        HeroesModel.find().sort({id:-1}).limit(1).exec({}, function(err, heroes){
            if(err) return handleError(err);
            if(heroes.length>0) resolve(heroes[0].id + 1);
            else resolve(11);
        });
    });
}

// ヒーローのリストを取得
app.get('/heroes', (req, res) => {
    console.log('@@ get heroes');

    if(req.query.name)
    {
        // req.query.name を含むヒーローのリストを作成して返す.
        const query_name = req.query.name;

        HeroesModel.find( { "name" : new RegExp( ".*"+query_name+".*") } ).select('id name').exec({}, function(err, heroes){
            if(err) return handleError(err);
            res.json(heroes);
        });
    }else{
        HeroesModel.find().select('id name').exec({}, function(err, heroes){
            if(err) return handleError(err);
            res.json(heroes);
        });
    }
});

// idを指定してヒーローを取得
app.get('/heroes/:id', (req, res) => {
    const query_id = req.params.id;
    console.log('@@ get heroes id:' + query_id);

    HeroesModel.find( { "id" : query_id } ).select('id name').exec({}, function(err, heroes){
        if(err) return handleError(err);
        if(heroes.length<=0)res.sendStatus(404);
        res.json(heroes[0]);
    });
});

// 新しいヒーローを登録
app.post('/heroes', (req, res) => {
    const query_name = req.body.name;
    console.log('@@post heroes :' + query_name);

    genId().then(function(newId){
        console.log(newId);
        var heroes = new HeroesModel({
            id: newId,
            name: query_name
        });
        heroes.save(function(err){
            if(err) return handleError(err);
            // 追加した項目をクライアントに返す
            res.json(heroes);
        });
    }).catch(function (error) {
        // 非同期処理失敗。呼ばれない
        console.log('catch err :' );
        console.log(error);
    });
});

// ヒーロー削除
app.delete('/heroes/:id', (req, res) => {
    const query_id = req.params.id;
    console.log('@@delete heroes id:' + query_id);

    HeroesModel.remove( { "id" : query_id }, function(err){
        if(err) return handleError(err);
        // ステータスコード200:OKを送信
        res.sendStatus(200);
    });
});

// ヒーロー名修正
app.put('/heroes', (req, res) => {
    // URLの:idと同じIDを持つ項目を検索
    const query_id = req.body.id;
    const query_name = req.body.name;
    console.log('@@put heroes / req.body:' + query_id);
    console.log('@@put heroes / req.body:' + query_name);

    HeroesModel.update( { "id" : query_id }, {$set: { name: query_name}} ).select('id name').exec({}, function(err){
        if(err) return handleError(err);
        // ステータスコード200:OKを送信
        res.sendStatus(200);
    });
});

// ポート3000でサーバを立てる
app.listen(3000, () => console.log('Listening on port 3000'));

先ほど定義したモデルをインポートしています。
代わりに heroes リストデータ をコメントアウトしています。
この部分は削除して構いません。

新しいヒーローIDを作成する genId 関数と併せて、
get、post、delete、put、それぞれの部分でMongoDBに対応したコードに置き換わっています。
特に post で genId を使用する部分では少し苦労しました。
Promise についての理解がなかったため MDNのサイトExpress Web フレームワーク (Node.js/JavaScript)で基本から学び直しました。

テスト

さて、これで完了です。
あとはサーバーとクライアントを双方起動させれば'Tour of Heroes'の元の状態を再現出来ていると思います。

終わりに

一応 MEANスタックと呼べる状態には出来たと思います。
しかし、本格的なWebアプリを構築しようとするとまだまだやるべき事があるように思います。
ここから何かしらの改造をできるかどうか・・・モチベーション次第かと(^^ゞ

リンク

MDN web docs。 Express だけではなく、Web系の開発のイロハが盛りだくさんでした。
Express Web フレームワーク (Node.js/JavaScript)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

どうしてもKubernetesを使う気になれず、ECSを使ってみる

ECSが好き

単純にdocker-composeの延長で使えてfargateにしておけばだいたい安定しているイメージ
運用に必要な知識が少ないし、簡単に作れるしというところでいつもECSを使うことが多いです。

準備

今回はECS上にnginxとnode.jsのコンテナを作って、EFSをマウントするところまでを実施します。
載せるのは簡単なVueプロジェクト+APIなので基本の基本としてメモしておきます。

クラスターの作成

まずはECSクラスタを作成します。
一瞬でできあがるのが素晴らしいですね。
クラスター名などは適当に変えてください。

クラスタの作成.png

2クラスタの作成.png
3クラスタの作成.png

そうするとクラスターが作成されます。
一瞬だったでしょう?
次はdockerでいうdoccker-compose相当になるタスク定義などを作成していきます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

docker-composeでNode.js(Express)とPostgresSQLを連携したサービスを作る勉強会

概要

先日行われた勉強会にて簡易ではありますが表題のサービスを作りました。Dockerの勉強の延長で取り組みました。この記事ではそのときの勉強会の内容を記載します。

どんなモノをつくるのか?

  • チャットのようなアプリケーション
  • docker-composeによるデータベースとの連携
    • Node.js(Express)でフロントエンド&バックエンド
    • データベースはPostgresSQL

完成したソースコード

https://gitlab.com/tamoco-mocomoco/study-docker-compose

参考にさせていただいた記事

https://qiita.com/ryo-ohnishi/items/b54e649b14b51694ef77

作ったモノの説明

docker-compose

サービス:app

  app:
    build:
      context: ./      # Dockerfile保存場所
    depends_on: 
      - database
    image: n-app                  # イメージ名
    container_name: n-app         # コンテナ名
    ports:                          # ポート接続
      - 3000:5000
    environment:
      PORT: 5000
      DB_USER: postgres
      DB_HOST: database
      DB_PORT: 5432
      DB_NAME: test_db

appNode.js(Express)がメインとなるサービスになります。ここでは学習のために敢えてデフォルトのポートを5000に変更しています。これは参考にさせていただいたExpressの環境変数でPORTが使用されており、それをdocker-compose5000に上書きしています。コンテナ内の5000をホスト側の3000に転送しています。ちなみにExpressで環境変数が使用されている箇所はapp/bin/wwwというファイルで確認できます。

app/bin/www

#!/usr/bin/env node

/**
 * Module dependencies.
 */

var app = require('../app');
var debug = require('debug')('myapp:server');
var http = require('http');

/**
 * Get port from environment and store in Express.
 */

var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);
~以下省略~

サービス:database

  database:
    image: postgres:12.3
    volumes:
      - ./init-sql:/docker-entrypoint-initdb.d:ro
    environment:
      POSTGRES_DB: test_db
      TZ: "Asia/Tokyo"
      POSTGRES_HOST_AUTH_METHOD: trust

databasePostgresSQLを使用しています。docker-composeで環境変数であるPOSTGRES_DBtest_dbに設定することで自動的にtest_dbという名前のデータベースが作成されます。

volumesの設定はinit-dbというフォルダにSQLファイルを格納しておくことで、SQLファイルに書かれているクエリを自動的に実行してくれます。
初期スキーマの設定(テーブル構造の作成)やダミーデータなどを挿入するときに利用します。

実行順序はファイル名の昇順で実行されるので、ファイル名に番号などをつけるとよさそうです。

今回、作成したデータベースにはpostgresというユーザーが作成されますが、パスワードが設定されていません。そのためPOSTGRES_HOST_AUTH_METHODtrustに設定する必要があります。

あと、TZでタイムゾーンを設定しています。タイムゾーンの変更については以下のクエリでも可能です。

ALTER DATABASE test_db SET timezone TO 'Asia/Tokyo';
SELECT pg_reload_conf();

init-dbにクエリをファイルとして格納しておけばタイムゾーンも変更可能です。ただし、どこでタイムゾーンが設定されているか後追いしづらいので、環境変数で設定できるなら環境変数を使った方が良いと思います。

Dockerfile:Node.js(Express)

今回のアプリケーションの部分(サービス:app)はappフォルダ配下
Expressのソースが格納されています。それらのソースを元にDockerfileでコンテナを作成します。

Dockerfile

# ベースイメージを指定
FROM node:10.12

# 環境変数設定
ENV NODE_ENV="development"

# 作業ディレクトリ作成&設定
WORKDIR /src

COPY ./app /src

RUN npm install

CMD npm run start

ここで重要なのでコンテナの中にsrcというフォルダを作成し、そこにホスト(自分の端末)のappフォルダの中身をコピーします。srcWORKDIRに設定されているのでsrcでコピーしたソースファイルを元にnpm installなどを実行できます。

今回はDockerfileでソースをコピーしましたがdocker-composeでも同じようなことが可能です。

Node.js(Express)

ここは別記事にしてもよいくらい説明する箇所が多いので抜粋して記載します。今回のExpressのソースではapp/routesフォルダのファイルでエンドポイントを設定して、表示内容をapp/viewsフォルダのテンプレートのファイルに渡してコンテンツを表示する流れになっています。

処理の流れ
app/routes → app/views

localhost:3000 にアクセス

app/routes/index.js
↓
app/views/index.ejs

index.jsではPostgresSQLchatテーブルをセレクトした結果をindex.ejsに渡しています。

localhost:3000/insert?msg=SEND_TEST

app/routes/insert.js
↓
app/views/index.ejs

insert.jsではURLパラメーターでmsgを受け取ることができます。msgの値をchatテーブルにインサートします。もしパラメーターがない場合は「ふにょふにょ」という値がインサートされます。

表示する内容はindex.ejsと同じですがインサートされた後のテーブルの情報を表示します。app/routesのファイルとapp/viewsのファイルは必ず1対1にする必要はなく、同じテンプレートでも渡す内容で表示内容を切り替えるなどの制御も可能だったりします。

データベース(PostgresSQL)への接続:db_client.js

module.exports = {
    pg_client: function () {
        const { Client } = require('pg')

        const client = new Client({
            user: process.env.DB_USER,
            host: process.env.DB_HOST,
            port: process.env.DB_PORT,
            database: process.env.DB_NAME
        })
        return client
    },
    pupupu: function () {
      console.log('pupupupu')
    }
  };

db_client.jsでデータベースの接続処理を共通で使用できるようにしています。PostgresSQLに必要なユーザー情報などはdocker-composeから環境変数で受け取りそれを元に接続を行います。

総評

ある程度コンパクトにまとまっており、構成もそこまで複雑ではないかなと思いました。ただ開発するときに少し難がありExpressは個別でnpm installなどでpackage.jsonなどを更新する必要があります。これはdocker-compose buildappフォルダをコピーしているので、アプリのnode_moduleなどは手動で更新しないとコンテナで動作してくれません。逆にnpm startなどで開発すればよい話になりますが、データベースとの接続ができないので困ったところです。
しかしながら、今回の勉強会ではいろいろとdocker-composeについて理解を深めることができたのでよかったかなと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

秋月電子で買ったLEDクラスターランプをobnizで試したメモ #obniz

少し前に秋月電子の店先で見つけて 30円という安さもあり試しに買ったLEDクラスターランプを触ってみました。

LEDクラスターランプ

http://akizukidenshi.com/catalog/g/gM-06699/

店先に置いてあって面白そうだったので購入してみました。

緑と赤のランプです。

データシートを読んでみた

データシート読んだ仕様などはこちらにメモしてます。

obnizから利用してみた

電気を流すかGNDにするかの指定だけみたいだったので一旦これで試してみました。

'use strict'

const Obniz = require('obniz');
const obniz = new Obniz(process.env.OBNIZID);

obniz.onconnect = async function () {
    obniz.io0.output(false); //ケーブル赤 io0 -
    obniz.io1.output(true); //ケーブル白 io1 +
    obniz.io2.output(false); //ケーブル緑 io2 -
}
$ OBNIZID=xxxx node led.js

特に問題なく起動

全てのLEDが点灯しました。

遊んでみた

この辺のコードもこちらに。

けっこう楽しい。1分くらいは見てられる()

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Python Node.js 文字操作

Python, Node.jsともに以下の順で変換
16進数文字列   ⇔  バイナリ  ⇔      文字列      ⇔  Unicode(10進数) ⇔ 16進数文字列
                          10進数(int型)     ⇔ 2進数(str型)、8進数(str型)
Base64              ⇔  バイナリ  ⇔  urlエンコード

そして、Python, Node.jsどちらもデフォルトのエンコーディングはUTF-8です。

Python

#-*- encoding:utf-8 -*-
import base64
import import urllib.parse

str_data = "あ"
encoded = str_data.encode() #b'\xe3\x81\x82'
hex_str = encoded.hex() #"e38182"
encoded = bytes.fromhex(hex_str) #b'\xe3\x81\x82'
base64_str = base64.b64encode(encoded) #b'44GC'
encoded = base64.b64decode(base64_str) #b'\xe3\x81\x82'
str_data = encoded.decode() #"あ"
urlencoded = urllib.parse.quote(str_data) #'%E3%81%82'
str_data = urllib.parse.quote(urlencoded) #"あ"

unicode = ord(str_data) #12354
hex_str = hex(unicode) #'0x3042'
unicode = int(hex_str,16) #12354
binary = bin(unicode) #'0b11000001000010'
unicode = int(binary,2) #12354
oct_str = oct(unicode) #'0o30102'
unicode = int(oct_str,8) #12354
str_data = chr(unicode) #"あ"

Node.js

let strData = "";
let encoded = Buffer.from(strData); //<Buffer e3 81 82>
let hexStr = encoded.toString("hex"); //"e38182"
encoded = Buffer.from(hexStr,"hex"); //<Buffer e3 81 82>
let base64Str = Buffer.from("").toString("base64"); //'44GC'
encoded = Buffer.from(base64Str,"base64") //<Buffer e3 81 82>
strData = Buffer.from(encoded).toString(); //"あ"
urlencoded = encodeURIComponent(strData) //'%E3%81%82'
strData = decodeURIComponent(urlencoded); //"あ"

let unicode = strData.codePointAt(0); //12354
let hexStr = unicode.toString(16); //'3042'
unicode = parseInt(hexStr,16); //12354
let binary = unicode.toString(2); //'11000001000010'
unicode = parseInt(binary,2) //12354
let octStr = unicode.toString(8); //'30102'
unicode = parseInt(octStr,8) //12354
strData = String.fromCodePoint(unicode); //"あ"

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

S3に保存されたMP3ファイルをLambdaでGoogle Cloud Speech-to-Textを使って文字起こしする

S3に保存されたMP3ファイルをLambdaでGoogle Cloud Speech-to-Textを使って文字起こしする

はじめに

前回のS3に保存されたwavファイルをLambdaでGoogle Cloud Speech-to-Textを使って文字起こしするに引き続き、
今回はMP3ファイルを対象に、S3に保存されたMP3ファイルをLambdaでGoogle Cloud Speech-to-Textを使って文字起こしする手順をまとめます。

重複する点が非常に多いので、相違点になるコードの部分だけ(前回でいう5-9のみ)ご紹介します。

Code

実行するコードは以下になります。

const AWS = require('aws-sdk');
const speech = require('@google-cloud/speech').v1p1beta1;
const client = new speech.SpeechClient();

const s3 = new AWS.S3({
    apiVersion: '2012-09-25'
});

exports.handler = function(event, context) {

    const bucket = event.Records[0].s3.bucket.name;
    const key = event.Records[0].s3.object.key;
    const params = {
        Bucket: bucket,
        Key: key
    };
    s3.getObject(params, async (err, data) => {
        if (err) {
            console.log(err, err.stack);
        } else {
            const audioBytes = data.Body.toString('base64');
            const audio = { content: audioBytes };
            const config = {
                encoding: 'MP3',
                sampleRateHertz: 44100,
                languageCode: 'ja-JP',
            };
            const request = {
                audio: audio,
                config: config,
            };

            const [response] = await client.recognize(request);
            const transcription = response.results.map(result => result.alternatives[0].transcript).join('\n');
            console.log(transcription);
        };
    });
}

ポイント

1. Cloud Speech-to-TextをMPx3で使うにはβ版を使う

音声エンコードの概要に記されている通り、MP3ファイルの場合はベータ版のみが使用できます。 なので2行目に変更があります。

const speech = require('@google-cloud/speech').v1p1beta1;

2. async/awaitで非同期処理対策

認識をしているときに、非同期で処理が行われてしまいうまく文字起こしできない可能性があるので、前回は入れなかったasync/awaitを入れてその対策をしました

const [response] = await client.recognize(request);

3. sampleRateHertzの調整

お好みのツールを使ってsampleRateHertzを確認して調整していただければと。もしかしらこのパラメータ設定しなくても行けるかもしれません。

結果

transcriptionの中に入っています。個人的には結構な認識精度で驚いています。

最後に

今回は、前回の記事に続いて、MP3のデータを扱ってみました。
正直、JavaScript、Node.jsを雰囲気でやってる人間なので間違いなどあったらぜひ教えていただきたいです。

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む