20201006のNode.jsに関する記事は6件です。

Apex UpでNode.js × expressをAWS Lambda, API Gatewayに速攻デプロイしてみる

はじめに

Node.js × expressのAPIのデプロイ先としてLambdaを使おうと思ったのですが
手取り早くデプロイして試したかったので、Apex Upを使ってみた。今回はその時の備忘録

Upとは

APIや静的ウェブサイトをLambda × API Gatewayにupコマンドでデプロイしてくれるツールです。

使ってみる

Upのインストール

curl -sf https://up.apex.sh/install | sh

プロジェクト作成

mkdir node-up
cd node-up
npm init
entry point: (index.js) app.js # 自分はapp.jsに変更

expressのインストール

npm install express
touch app.js
touch up.json # Upの設定ファイル

app.js

const express = require('express')
const app = express()
const { PORT = 3000 } = process.env

app.get('/', (req, res) => {
  res.send('Hello World!')
})

app.listen(PORT);

Upの設定は以下の内容に設定

{
  "name": "node-up"
}

これで設定は完了です。

最後にgit commitします。
git commitしとかないとupしたときにエラーが出てしまいます。

git init
git add .
git commit -m "First commit"

最後にデプロイします。

up

しばらくすると以下のような内容が出てくるのでURLをチェックして表示されればおkです。

stack: complete (19.175s)
endpoint: https://.../staging/

実際の表示
hello-up.png

削除

up stack delete

さいごに

Apex Upを使えば気軽にデプロイできるのでとりあえずデプロイしたいときは重宝しそうです。

参考資料

Up公式
Up GitHub

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

mongoseでfindして、そのobjectに値を追加できない | node.js

lean()使うとできるようになる。

Item.findById(req.params.id).lean().exec(function(err, doc) {      
  ...
});
doc.new = "Hi"

https://stackoverflow.com/questions/18554350/unable-to-add-properties-to-js-object

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

【備忘録】WindowsでNode.jsをアップデートする方法

プログラミング勉強日記

2020年10月6日
Node.jsのバージョンをアップデートするときに少し詰まったので備忘録として記録する。
Windowsではnが使えないので、今回はnodistを用いてNode.jsをアップデートした。

0. nodistをインストールする

 コマンドプロンプトでnodist -vを実行してnoditが使えるかどうかを確認する。使えない場合は、インストールする。以下のようになっていればインストールはできている。

コマンドプロンプト
>nodist -v
0.9.1

 こちらのページからインストーラーをダウンロードしてインストールする。

1005.PNG

1. Node.jsをインストールする

 次にnodist distでインストールできるバージョンの一覧を表示する。以下のようにとても長くなる。

コマンドプロンプト
>nodist dist
  0.1.14
  0.1.15
  0.1.16
  0.1.17
  0.1.18
  0.1.19
  0.1.20
  0.1.21
  0.1.22
  0.1.23
  0.1.24
  0.1.25
  0.1.26
  0.1.27
  0.1.28
  0.1.29
  0.1.30
  0.1.31
  0.1.32
  0.1.33
  0.1.90
  0.1.91
  0.1.92
  (省略)
  11.0.0
  11.1.0
  11.2.0
  11.3.0
  11.4.0
  11.5.0
  11.6.0
  11.7.0
  11.8.0
  11.9.0
  11.10.0
  11.10.1
  11.11.0
  11.12.0
  11.13.0
  11.14.0
  11.15.0
  12.0.0
  12.1.0
  12.2.0
  12.3.0
  12.3.1
  12.4.0
  12.5.0
  12.6.0
  12.7.0
  12.8.0
  12.8.1
  12.9.0
  12.9.1
  12.10.0
  12.11.0
  12.11.1
  12.12.0
  12.13.0
  12.13.1
  12.14.0
  12.14.1
  12.15.0
  12.16.0
  12.16.1
  12.16.2
  12.16.3
  12.17.0
  12.18.0
  12.18.1
  12.18.2
  12.18.3
  12.18.4
  13.0.0
  13.0.1
  13.1.0
  13.2.0
  13.3.0
  13.4.0
  13.5.0
  13.6.0
  13.7.0
  13.8.0
  13.9.0
  13.10.0
  13.10.1
  13.11.0
  13.12.0
  13.13.0
  13.14.0
  14.0.0
  14.1.0
  14.2.0
  14.3.0
  14.4.0
  14.5.0
  14.6.0
  14.7.0
  14.8.0
  14.9.0
  14.10.0
  14.10.1
  14.11.0
  14.12.0
  14.13.0

 この中でバージョンを選びインストールする。

コマンドプロンプト
> nodist + 13.14.0
> nodist 13.14.0
> node -v (確認)
13.14.0

2. npmをアップデートする

 Node.jsをインストールすると、npmも同時にインストールされるが、nodistでインストールされたnpmのバージョンは古いことがあるので、コマンドでnpmのバージョンを確認し、アップデートする必要がある。

コマンドプロンプト
> npm -v
> nodist npm 6.14.4

 

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

WebアプリへのLINEログイン実装で少しハマったところ

WebサイトへLambda+Node.js+APIGateWayを含めて、LINEログイン機能を実装しようとした。
参考にしたサイトが
「WebアプリにLINEログイン機能を組み込む」
[URL] http://xp-cloud.jp/blog/2019/11/27/6116/

非常に分かり易くて参考になったが、一部どうしても動かない(LINE認証画面が出てこない)為、エラーメッセージを眺めてみると、「stateが無い」といったことを言われているので、あらためて、LINE公式を確認すると

"queryStringParameters": {
    "error": "invalid_request",
    "error_description": "'state' is not specified."
    },

stateというパラメータは「必須」ということだった。

20201006-1.JPG
LINE公式サイト:
https://developers.line.biz/ja/docs/line-login/integrate-line-login/#making-an-authorization-request
 
 
パラメタに「state」を足してパラメタ付きのリンクをたたくと、無事に表示されたLINEログイン画面。
20201006-2.png

その他の動作(Lamda~APIGateway)に関しては問題なく稼働。ありがとうございます作者様。

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

VagrantにHerokuを入れる際に起きたエラーと解決方法と参考記事

はじめに

ドットインストールで構築したローカル環境でアプリを開発し、公開するため、いざherokuを入れようとしたらエラーが起きまくりました。
【初心者向け】railsアプリをherokuを使って確実にデプロイする方法【決定版】
この記事を参考にしていました。
しかし、herokuを入れる際に、この記事の開発環境が全く違うため、その先に進めずエラーと戦うことになりました。

パスがない問題

エラー解決に必死だったため、エラーのコピーは取っていませんが、このようなエラーがでたと思います。

↓エラーの最後の文
Your path is missing /usr/local/bin, you need to add this to use this installer.

/usr/local/binのパスが無いと言われているため、パスを作る必要があるみたいです。

パスの確認

$ echo $PATH

パスを確認してみますが/usr/local/bin:はあっても/usr/local/binがないと駄目みたいです。

解決方法の前に、解決する中で知った知識

sudoとは、僕の解釈では権限の高いコマンド。
$#で違いもあり、#のほうが権限が高い。($の方で実行できないことが#で実行できたから)

解決方法:偉い権限を使ったり、viエディタで書き換えたりする。

パスの設定に必要な.bash_profileの設定とsudoの設定は以下を参考にしました。
仮想環境構築後に設定しておきたいこと -メモ-

sudoの設定はこちらの方が親切です。
CentOS で sudo 時に実行ユーザーのPATHを引き継ぐ

$ sudo visudo

の実行後に、viエディタの操作が必要だったため以下を参考にしました。
viエディタの使い方

パスが通ったと思ったら、シンタックスエラー問題

$ heroku --version

herokuのバージョン確認をすると、以下のエラーが...

/usr/local/lib/heroku/node_modules/@oclif/command/lib/index.js:3
const path = require("path");
^^^^^
SyntaxError: Use of const in strict mode.
    at Module._compile (module.js:439:25)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Module.require (module.js:364:17)
    at require (module.js:380:17)
    at Object.<anonymous> (/usr/local/lib/heroku/bin/run:5:1)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)

Node.jsのバージョンが古すぎたみたいです。

$ node -v

確認してみたところv0.10.48でした。

解決方法:キャッシュを消して、新しいバージョンを入れる。

いろいろと試したため、ダイレクトな解決方法にならない場合はすみません。

npmがインストールされていなかったので、インストールしました。ついでにnodeもインストール。
Herokuを入れるためにnpmが必要かどうかはわかりませんが、とりあえずインストールしました。

$ sudo yum install nodejs npm

偉い権限の方に行く。

$ sudo -s

左のやつが$から#に変わる。

インストールするために必要なやつ(正直、よくわかってないですがsetup_11.xの数字の部分でインストールするバージョンを指定。)

# curl --silent --location https://rpm.nodesource.com/setup_11.x | bash 

ここから、インストールとアンインストールを繰り返したため、手順が間違っていたらすみません。
以下を参考にしました。先の読んでおいて欲しいです。
yumでのnodejsのバージョンアップにはまった話と解決方法

古い方のrpmを削除。

# rm /etc/yum.repos.d/nodesource-el.repo

アンインストールする。

# yum -y remove nodejs

キャッシュを削除。

# yum clean all

インストールする。

# yum -y install nodejs

バージョンを確認。

# node -v
v11.15.0

成功です!
そして、偉い権限のところから出る。

# su vagrant

herokuのバージョンも確認

$ heroku -v
heroku/7.44.0 linux-x64 node-v11.15.0

他の参考記事です。
https://qiita.com/daskepon/items/16a77868d38f8e585840
https://inaba.hatenablog.com/entry/2018/11/13/023933

ほぼ同じエラーが出ていた記事
https://teratail.com/questions/256490

感謝

参考にさせていただいたサイト管理人の皆様、誠にありがとうございました。

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

Amazon Echo で Google Drive 上の mp3 を 再生する (その2)

はじめに

前回の記事 のコード部分を説明します。
AudioPlayer のリファレンスはこちら

AudioPlayer の使い方(考え方)

プレイリストの曲をすべて再生することだけを考えるなら、最初に全ての曲をキューに登録してしまうという使い方もあります。しかし後に実装する次の曲、前の曲、シャッフル再生などは任意のタイミングで指示され、登録していたキューが無駄になることもあるため、以下の考え方で使用します。

  • キューには 1 曲しか登録しない
  • 次の曲、前の曲、曲の終了、などのリクエストにより、次に再生する曲をキューに入れ替え登録する (再生中の曲をそのままに登録する方法と、再生中の曲を含めて入れ替える方法があり使い分ける)
  • 機能に必要な情報はトークンに埋め込み、次回リクエスト時にトークンから必要な情報を取得する

トークンについて

トークンについてはリファレンスaudioItem.stream.token に記述がありますが、再生中の曲(ストリーム)を識別するための 1024 文字以内の任意の文字列です。このトークンに情報を埋め込むことで、DB 等に再生中の曲の情報を保存したりせずに必要な機能を実現します。
今回、トークンはコロン ( : ) で区切った以下の形式で組み立てることにします。

プレイリストファイルID : 乱数seed : 再生曲番号 : ループ再生フラグ : リピートフラグ : 曲情報テキスト

項目 説明
プレイリストファイルID 再生中のプレイリスト(json) の Google Drive 上のファイルID です。これにより再生開始後は root.json を見る必要がなくなります。
乱数seed シャッフル再生用の項目です。0 が順次再生、0 以外がシャッフル再生です。未実装部分
再生曲番号 現在何曲目を再生しているかの番号です。
ループ再生フラグ loop なら全体ループ再生、空文字列なら一度限り再生を示します。未実装部分
リピートフラグ repeat なら次回も同じ曲を再生(1回のみ)、空文字列なら次の曲を再生します。未実装部分
曲情報テキスト 曲名を訪ねた際に読み上げるテキストです。コロンを含めないよう注意が必要です。未実装部分

コード概説

getRequestTypeOrIntentName() (index.js 11行目~)

今回のスキルでは、RequestType か IntentName で処理を分岐しますが、その分岐をしやすくするため適切な方の値を返却するだけの関数です。

const getRequestTypeOrIntentName = handlerInput => {
  return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
    ? Alexa.getIntentName(handlerInput.requestEnvelope)
    : Alexa.getRequestType(handlerInput.requestEnvelope);
};

LaunchRequestHandler() (index.js 17行目~)

「Alexa、プレイミュージックを開いて」などとスキルの起動のみが行われた場合の処理です。
プレイリスト名の発話を促す返答を行い発話待ちにします。

const LaunchRequestHandler = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'LaunchRequest';
    },
    handle(handlerInput) {
        const speakOutput = 'プレイリスト名を教えてください。';
        return handlerInput.responseBuilder
            .speak(speakOutput)
            .reprompt(speakOutput)
            .getResponse();
    }
};

PlayMusicHandler() (index.js 30行目~)

個別に見ていきます。

canHandle(), async handle()

canHandel() で PlayMusicIntentAudioPlayer.PlaybackNearlyFinished の場合に true を返却することで、それらのリクエストが発生した場合に handle() が呼び出されます。
未実装のリクエストは 94行目の CancelAndStopIntentHandler() に記述してあるので、実装が完了したものは随時こちらの配列に移していく形になります。
handle() の基本的な処理の流れは以下になります。

  • 再生用パラメータを初期化 (38行目~44行目)
  • リクエストに応じて再生用パラメータを調整 (45行目~)
  • 再生する曲の url とトークンを作成 (85行目~87行目)
  • addAudioPlayerPlayDirective で再生を指示 (88行目)

PlayMusicIntent (index.js 46行目~)

PlayMusicIntent2.3.4. インテント で設定したサンプル発話に合致する発話がなされた場合にリクエストされます。
再生パラメータの初期値は初回再生時と同じであるため変更しませんが、プレイリストのファイル ID が未取得のため、{playlist} スロットに格納されたプレイリストの id をキーに、root.json からプレイリストファイルのファイル ID だけ取得します。

    case('PlayMusicIntent'): {
        // intent.slots.{スロット}.resolutions 配下にユーザーが選択したスロットが格納される
        // ユーザーが発話したプレイリスト名に対応する Google Drive 上の json ファイルを取得するが、
        // スロット値の ID を利用することで、発話の揺らぎに対応しつつ対象ファイルを一意にしている
        const resolvedSlot = handlerInput.requestEnvelope.request.intent.slots.playlist.resolutions.resolutionsPerAuthority[0].values;
        if (resolvedSlot === undefined) {
            const speakOutput = 'プレイリストを認識できませんでした。もう一度お願いします。';
            return handlerInput.responseBuilder
                .speak(speakOutput)
                .reprompt(speakOutput)
                .getResponse();
        }
        const rootjson = await getJson(makeDriveUrl(rootFileId));
        playlistId = rootjson.playlists[resolvedSlot[0].value.id];
        break;
    }

AudioPlayer.PlaybackNearlyFinished (index.js 62行目~)

AudioPlayer.PlaybackNearlyFinished は曲の終わりが近づいてキューに次の曲を追加できるようになった時に自動的にリクエストされてきます。
behavior の初期値の REPLACE_ALL は再生中の曲が終了してしまうため、再生中の曲に影響を与えずにキューを置き換える REPLACE_ENQUEUED に変更します。
また、再生中の曲のトークンから再生パラメータを切り出し、次の曲にするため再生曲番号を +1 します。この段階ではプレイリストに何曲あるか分からないため +1 した結果が最後の曲を超えているかどうかは 75 行目で考慮しています。

    case('AudioPlayer.PlaybackNearlyFinished'): {
        // 曲の終了間際の場合は再生中の曲をそのままにするため REPLACE_ENQUEUED でキューを置き換える
        behavior = 'REPLACE_ENQUEUED';
        const audioPlayer = handlerInput.requestEnvelope.context.AudioPlayer;
        [playlistId, seed, track, loop, repeat, ] = audioPlayer.token.split(':');
        track = (+track) + 1;
        break;
    }

プレイリスト取得 (index.js 75行目~)

root.json またはトークンから取得したプレイリストのファイル ID からプレイリストデータを読み込みます。
ここで全曲数がわかるため、次の再生曲番号が最後の曲を超えていたらキューをクリアして終了します。「再生を終了します」などのアナウンスをしたいところですが、AudioPlayer.Playback~ では .speak による発言ができない仕様になっています。

        // 全て再生したら終了する
        const playlist = await getJson(makeDriveUrl(playlistId));
        if (track >= playlist.length) {
            return handlerInput.responseBuilder
                .addAudioPlayerClearQueueDirective('CLEAR_ALL')
                .withShouldEndSession(true)
                .getResponse();
        }

再生指示 (index.js 84行目~)

情報が揃ったため、再生指示の応答を返却します。
idx はシャッフル再生時に track とずれるため用意しています。

    // addAudioPlayerPlayDirective を利用して AudioPlayer に音楽再生の指示を応答する
    const idx = track;
    const url = makeDriveUrl(playlist[idx].id);
    const token = `${playlistId}:${seed}:${track}:${loop}:${repeat}:${playlist[idx].info}`;
    return handlerInput.responseBuilder
        .addAudioPlayerPlayDirective(behavior, url, token, offset, null)
        .getResponse();

getJson (index.js 180行目~)

指定した (Google Drive の) URL の json データを読み込む関数です。
uc?export=download&id= を使うと直接ファイルにアクセスできているように見えますが、一度リダイレクトされています。
https モジュールは、リダイレクトを追跡するようにはなっておらず、追跡できる request モジュールは標準では使えませんので、https モジュールを使って簡易的にリダイレクトを追跡する処理を組みます。

const getJson = (url) => new Promise((resolve, reject) => {
    Https.get(url, (res) => {
        res.setEncoding('utf8');
        let json = '';

        if(res.statusCode === 301 || res.statusCode === 302) {
            resolve(getJson(res.headers.location));
        } else {
            let bufs = [];
            res.on('data', (chunk) => json += chunk);
            res.on("end", () => {
                try {
                    resolve(JSON.parse(json));
                } catch (err) {
                    console.log(err);
                    reject(err);
                }
            });
            res.on('error', (e) => {
                console.log(e.message);
                reject(e);
            });
        }
    });
});

おわりに

無駄に長くなっていまいましたが以上になります。つまづく箇所はリダイレクトの追跡くらいでしょうか。
その3 では未実装機能を実装します。

参考

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