Quantcast
Channel: Node.jsタグが付けられた新着記事 - Qiita
Viewing all 9086 articles
Browse latest View live

MEVN STACKとは〜Vueでフルスタックに作りたい〜

$
0
0

MEVN STACKって何?

MEVN STACKってこういうのだよ

MEVN STACKとは、

  • Mongodb
  • Express
  • Vue.js
  • Node.js

の頭文字を取ったもの。
類似にMEAN STACK(Mongodb,Express,AngularJS,Node.js)もありますね!
Qiitaには、MEANはあったけどMEVNについてはなかったので、これから頑張って書いていきたいと思います。
もし少しでもいいね!と思ったら「Looks Good To Me!」をください。励みになります!

構成はこうすればできるよ

それぞれの役割についてです。

vue-node-express-mongodb-crud-mean-stack-architecture.png
[引用画像:https://bezkoder.com/vue-node-express-mongodb-mevn-crud/]

上記のような構成で構成します。
構築方法は以下の通りです。

環境構築

■動作環境

OS:macOS Catalina 10.15.6
Node:v13.8.0
Vue:@vue/cli 4.2.2
mongodb:v4.2.3

mongodbの起動

ターミナルにて以下のコマンドを実行してmongodbを起動します。パスに記載されているところに「mongod.conf」が作成されているので、必要な項目を設定する。

terminal.
hoge@hoge-MacBook-Air hoge %mongod --config /usr/local/etc/mongod.conf

僕はソースコードのプロジェクトはdocumentsの配下に格納するようにしているので、storageにはdocumentsの下にdataフォルダを作成してデータベースのデータを格納できるようにしているよ。任意でパスは変更してね。

mongod.conf
systemLog:
  destination: file
  path: /usr/local/var/log/mongodb/mongo.log
  logAppend: true
storage:
  dbPath: /Users/Seastar/Documents/data/db
net:
  bindIp: 127.0.0.1

mongodbの起動を完了したらアプリケーションプロジェクトを作成はじめます!

プロジェクトの作成

今回はhogeという名前で作っているので、必要に応じて変更してくだせぇ。

terminal.
hoge@hoge-MacBook-Air documents %mkdir hoge && cd hoge
hoge@hoge-MacBook-Air hoge %npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
package name: (hoge)
version: (1.0.0)
description:
entry point: (index.js)
test command:
git repository:
keywords:
author:
license: (ISC)
About to write to /Users/hoge/Documents/hoge/package.json:

{
  "name": "hoge",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}


Is this OK? (yes) y


   ╭────────────────────────────────────────────────────────────────╮
   │                                                                │
   │      New minor version of npm available! 6.13.6 → 6.14.7       │
   │   Changelog: https://github.com/npm/cli/releases/tag/v6.14.7   │
   │               Run npm install -g npm to update!                │
   │                                                                │
   ╰────────────────────────────────────────────────────────────────╯

上記のコマンドを無事に実行するとhogeフォルダの配下に「package.json」が作成される。
作成されたのを確認したら以下のコマンドを続けて実行する。

terminal.
hoge@hoge-MacBook-Air hoge %npm install express bcryptjs passport passport-jwt jsonwebtoken mongoose body-parser cors concurrently mongodb
hoge@hoge-MacBook-Air hoge % npm i -D nodemon

それぞれの役目は以下の通り

  • express
    • Node.jsのフレームワークの一つ
    • サーバーサイド処理を書くために必要
  • bcryptjs
    • パスワードなどをハッシュ化するため
  • passport
    • 認証機能を実装するため
  • passport-jwt
    • passport JSON Web Tokeで電子署名付きのJsonで改ざんチェック
  • jsonwebtoken
    • JSON Web Tokeでのチェック
  • mongoose
    • mongodbへの操作のため。ちなみにマングース
  • body-parser
    • formの入力値を受け取るため
  • cors
    • Cross-Origin Resource Sharing
  • concurrently
    • npmのコマンドをサーバー側とフロント側で同時に実行するため
  • mongodb
    • mongodbを操作するためにドライバーが必要
  • nodemon
    • アプリケーションのコードの変更を監視し、変更があった際に自動でサーバーの再起動をしてくれるツー

インストールが完了すると以下のような構成になる。
スクリーンショット 2020-07-30 1.15.58.png

その後、「server」のフォルダとVueクライアントの「client」を作成していく。

terminal.
hoge@hoge-MacBook-Air hoge % mkdir server
hoge@hoge-MacBook-Air hoge % vue create client


Vue CLI v4.2.2
┌─────────────────────────────────────────┐
│                                         │
│   New version available 4.2.2 → 4.4.6   │
│    Run npm i -g @vue/cli to update!     │
│                                         │
└─────────────────────────────────────────┘

? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, Router, Vuex, Linter
? Use history mode for router? (Requires proper server setup for index fallback
in production) Yes
? Pick a linter / formatter config: Basic
? Pick additional lint features: (Press <space> to select, <a> to toggle all, <i
> to invert selection)Lint on save
? Where do you prefer placing config for Babel, ESLint, etc.? In package.json
? Save this as a preset for future projects? No

....省略.....

🎉  Successfully created project client.
👉  Get started with the following commands:

 $ cd client
 $ npm run serve

hoge@hoge-MacBook-Air hoge %cd client
hoge@hoge-MacBook-Air client %npm i axios
hoge@hoge-MacBook-Air client %cd ../
hoge@hoge-MacBook-Air hoge %cd server
hoge@hoge-MacBook-Air server %touch index.js

この記事はここまで

長くなるので今回はここまでにします。
次回以降に具体的なコードを作っていきます。


Node.js新規アプリケーション作成時の設定手順

$
0
0

前提条件
node.jsがインストールされていること

手順

1: 任意の場所に、アプリケーションを格納するフォルダを作成する
・・・今回はデスクトップにnodeフォルダを作成
 

2: コマンドプロンプトにて下記コマンドを入力し、package.jsonを生成する

入力
>cd C:\Users\yamapi1012bs\Desktop\node
#              ↑ユーザー名> npm init --yes# package.jsonがフォルダに生成される
#以下のように出力されればOK
Wrote to C:\Users\yamapi1012bs\Desktop\node\package.json:

{"name": "node",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {"test": "echo \"Error: no test specified\"&& exit 1"},
  "keywords": [],
  "author": "",
  "license": "ISC"}>

 
 
3: npmパッケージをインストールする

入力
> npm install express ejs
# 今回は、expressとejsの2つのパッケージをインストール
# 以下のように出力されればOK> node --harmony ./postinstall.js

Thank you for installing EJS: built with the Jake JavaScript build tool (https://jakejs.com/)

npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN node@1.0.0 No description
npm WARN node@1.0.0 No repository field.

+ express@4.17.1
+ ejs@3.1.3
added 65 packages from 44 contributors and audited 65 packages in 18.403s
found 0 vulnerabilities

>

 

nodeフォルダ内に

  • node_modules(フォルダ)
  • package.json
  • package-lock.json

が生成されていれば設定完了
 

実行してみる

1: nodeフォルダ内に以下を作成

  • index.js
  • views(フォルダ)
    • sample.ejs(viewsフォルダ内)
index.js
constexpress=require('express');constapp=express();app.get('/',(req,res)=>{res.render('sample.ejs');});app.listen(3000);
sample.ejs
<h1>Hello World</h1>

2: コマンドプロンプトにて下記コマンドを入力

入力
> node index.js

3: ブラウザを開いて「localhost:3000」にアクセス
sample.ejs(Hello World)が表示されればOK

MERNスタックを学べる動画をまとめてみた(外国語の動画です)

$
0
0

MERNスタック is 何?

MongoDB, Express, React.js, Node.jsの4つを使った開発環境のアーキテクチャのことです。主に、Webアプリ開発に用いられます。

MongoDBとは

ドキュメント志向のNo-SQLデータベース。データの保存などに使う。DBは、データベースの意。データがJSON形式みたいな感じなので非常にJavaScriptと相性が良い。

ExpressJSとは

Node.jsのフレームワーク。Node.jsの機能と一緒に使う事で、バックエンドの構築が出来る。

ReactJS

単一ページWebアプリのUIを作成するUIコンポーネントを構築出来る。アプリケーションのView部分を担っている。

NodeJSとは

サーバサイドJavaScriptエンジン のことです。

MERNスタックの良いところは?

ずばり!!
全部JaveScriptで書けることです!JavaScriptはフロントだけでしか使えへんやろ・・・。と思われがちですが、なんでも行けちゃうんです。もし、HTML,CSS,JavaScriptという静的なサイトしか作ったことがないという人は、新しく言語を学ばずともWebアプリケーションを開発することができるのでオススメです!!

MERNスタックを学べる動画一覧(英語のみ)

Youtubeの動画か再生リストになっていて一年以内(2019年の8月以降)に投稿されているものをまとめました。

JavaScriptの勉強している人は分かると思いますが、アロー関数とか非同期処理(async await)とか、Hooksの書き方をしている教材がすくないですよね・・・。でも、こちらに挙げているものは、どれも最新の記法で書かれているのでコードを眺めるだけでもけっこう勉強になります!

CODERS NEVER QUIT
インスタのクローンアプリが作れます。
動画本数:53本 1動画:10分前後 合計:9時間

John Ahn
Youtubeのクローンアプリが作れます。
合計:4時間30分

John Ahn
映画の紹介サイトが作れます。
合計:1時間30分

John Ahn
ECサイトが作れます。
合計:5時間

Coding Garden with CJ
旅行のログを投稿するサイト。Live形式です。
合計:4時間

Programming with Jaiti
ログイン認証アプリが作れます。
まだ完成するところまでの動画はupされていませんが分かりやすいですね。

おススメ勉強法

外国語だったとしてもコードの書き方は同じなので基本的な書き方さえ知っていれば分かります。しかし、この動画をみて学ぶのはファイル構成だったり、使用するライブラリの使い方、モダンな書き方を一から見て、自分で次に何かプロダクトを作るときに再利用することができるようにすることです。そのためにお勧めしたいのが・・・

コメントアウト勉強法です!

その名の通り、コメントアウトしまくります。どのタイミングでコメントアウトするかは人それぞれなんですが、一番良いのは再生リストになってるやつは一つの動画が終わるたびにコメントアウトするとよいですね。長い動画だと30分か、一つの機能を実装する度にすると良いと思います!

メリットとしては、コードを写経するだけだと見返したとしても何も覚えてなかったりしますが、これをすると自分の言葉でそのコードを表現する必要があるので、アウトプット要素が含まれます。本当のアウトプットは自分のプロダクトを作るときだとは思いますが、インプットと同時に簡単なアウトプットをしておくことで次にコードを見る時や他の人のコードを理解する時にも役に立ちます!ぜひお試しください!

heroku×node.jsでcorsエラーがなかなか解決しないときに気をつけること

$
0
0

node.js(express)でサーバーサイドを記述し、herokuに上げていたのですが、今まではすんなりと解消できていたcorsのエラーに嵌ってしまったので、記事を書くことにしました。

環境

  • heroku: 7.42.5
  • node.js: 12.16.2
  • express: 4.17.1
  • cors: 2.8.5

基本的な対応方法

今回はサーバーサイドとフロントエンドを異なるレポジトリに書いているため、フロントエンドからfetch等でサーバーサイドのAPIを呼ぶ際には、corsのエラーが生じてしまいます。

Access to fetch at 'https://xxx.com' from origin 'https://yyy.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

基本的にはnpm install corsをして、

const express = require('express')
const cors = require('cors')
const app = express()
app.use(cors())

と書けば解決します。

注意点① - credentialsをtrueにする場合

ただ、credentialsをtrueとしたい場合には、originを具体的に指定する必要があります。

const express = require('express')
const cors = require("cors");
const app = express();
const corsOption = {
  origin: [
    "http://xxx.com",
    "https://yyy.com",
  ],
  credentials: true,
};
app.use(cors(corsOption));

というようにします。
originは、1つの場合は配列なしの文字列でも大丈夫で、1つにしろ複数にしろ正規表現を用いることもできます。
ここまでは、ぐぐると色々な解説が出てくると思うのですが、今回はさらに二重に嵌ってしまっていました。

注意点② - 最後の"/"

1つめは、originの最後に"/"を入れてしまっていたことです。
本来は"http://xxx.com"としなければいけないところを、アドレスバーからコピペするなどして、"http://xxx.com/"としてしまうと、エラーが生じます。

注意点③ - herokuのconfig varsが登録されていない

2つめは、herokuのconfig varsが登録されていなかったということです。
かなり初歩的なミスなのですが、例えば、process.env.TEST_KEYみたいなコードを書いていたとして、ローカルでは.envの変数を参照していたとします。
当然、herokuではconfig varsにTEST_KEYを登録しなければいけないのですが、必要なキーなどを参照できなかった場合も、corsのエラー同様のメッセージが表示されてしまうため、数時間を無駄にしてしまいました。
皆様もお気をつけください。

参考

Express cors middleware
Nodejs express, Heroku CORS

Node.jsとJavascriptの違い、関係について

$
0
0

はじめに

はじめてNode.jsを学んだ方や使用した方はこう思いませんでした?

「なんで同じJavaScriptなのに名前が違うの?サーバーサイドで動くJavaScript?使ってるけどいまいちよくわからない…」

そんな疑問を持った方たちのために簡単に解説していきます。

言語・処理系について

そもそもプログラミング言語とは、コンピュータに情報や命令を伝えるための「文字・記号の集まり及びそのルール」です。そして、プログラミング言語によって書かれた文字列を処理系によって処理することで、コンピュータに情報を伝達することができます。

日本人は日本語を理解することができますよね?逆に、日本語を知らない外国人は日本語を理解することができません。これは、日本人が頭の中に日本語の処理系を持っているから言語を理解できるのです!

このように言語は、その言語の意味やルールを処理・理解してくれる処理系がないと全く機能しなくなってしまいます。

プログラミング言語における処理系とは?

日本語や英語などを理解するための処理系はそれぞれの人間の頭脳でした。じゃあ、プログラミング言語における処理系は何でしょう?
それは、プログラミングをする環境を構築する際にインストールしたやつです!

言語処理系
PythonCPython
CGCC
PHPPHP

など

基本的にプログラミング言語と処理系はセットなのです。

では、フロントでJavaScriptを動かしている方は、何もインストールしてないのに動くじゃん!と思うかもしれないですが、これはブラウザに処理系が搭載されているためです。

じゃあNode.jsって?

そうです、

Node.jsとはJavaScriptの処理系です!

プログラミング言語と処理系の関係について理解できれば、名前が違う!といった疑問も解消できるはずです。

JavaScriptはブラウザで動くフロントサイドの言語と昔は言われていましたが、このNode.jsという処理系によってPythonやPHPといったサーバーサイドの言語と同じようなことができるのです!

おわりに

自分でも曖昧な部分があったので、記事として書き起こすことで整理してみました。
自分の成長のためにも、間違った部分やご指摘があればどんどんコメントお願いします!

npmコマンドを使えるようにするまでのまとめ【mac】

$
0
0

macの初期状態ではnpmコマンドを使うことができないので、それを使える状態になるまでを記事にまとめました。
macでnpmコマンドを使えるようにするには、Homebrewのインストール,Nodebrewのインストール,Node.jsのインストールが必要なので、それを順番にインストールしていきます。

このページの最後に順番にやっていった場合の各場面での画面のスライドを埋めこんでいますので、解説が分からない場合は、そちらで確認しながらやってみてください!

Homebrewのインストール

パッケージ管理システムのひとつのHomebrewをインストールします。
(パッケージをmacに最適にインストールしてくれるようにするツールです)

npmインストール1.png

Homebrew公式にアクセスし、インストールの部分のコードをコピーし、ターミナルを起動してペーストしてEnterで実行します。(インストールは少し時間がかかります。)
一応、インストールのコードを下記に記載します。

ターミナル
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"

Nodebrewのインストール

Node.jsのバージョンを管理するためのツールのNodebrewをインストールします。

brewコマンド(Homebrewのコマンド)を使い、nodebrewをインストールします。

ターミナル
brew install nodebrew

Node.jsのインストール

Node.js公式にアクセスし、推奨版の部分をクリックし、Node.jsの推奨版をインストールします。(最新版でもOKです)
npmインストール9.png

このNode.jsをインストールすることで、npmコマンドが使えるようになっています。

まとめ

今回の解説を手順通り進めると下記のスライドのような画面の移り変わりになっていきます。
分からなくなった場合は是非確認してみてください。(必ずできます)

See the Pen npm_install_howto by engineerhikaru (@engineerhikaru) on CodePen.

この記事が良いと思った方は、LGTMをしていただければ嬉しいです!
フォローも是非お願い致します(^^)

【Express】ミドルウェアとは何か

$
0
0

ミドルウェアとは何か

ミドルウェアとは:サーバーの動作をカスタマイズするもの。
ルーターハンドラが作動する前に何かを行うもの。

・ミドルウェアがない時
新しいリクエスト
↓
通常のルーターハンドラが作動
・ミドルウェアがある時
新しいリクエスト
↓
ミドルウェアが作動!
↓
通常のルーターハンドラが作動

ミドルウェアの設定

app.use()

を用いて設定する

ミドルウェアの動作例

・リクエストしているHTTPメソッドと、リクエストを送っているパスをターミナルに表示させる

app.use((req,res,next)=>{console.log(req.method,req.path) next()//ミドルウェア機能の終了を明示的に表示})
・ミドルウェアを使ってアクセスを制限する

GETリクエストを送るとアクセスを拒否され、それ以外だと通常のルーターハンドラが起動

app.use((req,res,next)=>{if(req.method==='GET'){res.status(400).send('GETリクエストは使用できません')}else{next()//ミドルウェア機能の終了を明示的に表示}})

herokuでNode.jsを立ち上げるまでの手順

$
0
0

色々と理解できていないことは多いですが、とりあえずherokuで立ち上げられたので手順を記載。
エラーでまくったしいろんなサイト見すぎてもはや何を参考にしたのかわからなくなってます。

前提

① Node.js、herokuはインストール済み
② パッケージマネージャーはyarnを仕様
下記の記事を見てyarnにしました
https://qiita.com/qulylean/items/afa2acff6ed963c88798

yarnのインストール

上記の記事を参考にyarnをインストールします。

yarnでhttpモジュールのインストール

コマンドプロンプトを立ち上げて今回作成したいフォルダまでディレクトリの移動をします。

フォルダの移動
$ cd フォルダ名

前に戻る
$ cd ..

フォルダの作成
$ mkdir フォルダ名

フォルダを作成して移動
$ mkdir フォルダ名 && cd フォルダ名

ファイルの作成
$ type nul > ファイル名

ディレクトリの移動が完了したら下記のコマンドを実行します。

$ yarn add http

すると必要なフォルダが用意されて下記の状態になります。

フォルダ
フォルダ
 ├ node_modules
 ├ package.json
 └ yarn.lock

yarnでexpressモジュールのインストール

次のコマンドを実行してexpressのインストールを行います。

expressのインストール
$ yarn add express

実行完了すると下記の表示がでるので、package.jsonに記載するためにバージョンだけ覚えておいてください。

yarn add v1.22.4
[1/5] Validating package.json...
[2/5] Resolving packages...
[3/5] Fetching packages...
[4/5] Linking dependencies...
[5/5] Building fresh packages...
success Saved 1 new dependency.
info Direct dependencies
└─ express@4.17.1
info All dependencies
└─ express@4.17.1
Done in 3.23s.

package.jsonの変更

初期状態だと下記のようになっていると思います。

pakage.jsonデフォルト
{
  "dependencies": {
    "http": "^0.0.1-security"
  }
}

それを次のように変更します。

pakage.json変更後
{
  "name": "hello_world",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.17.1",
    "jade": "*"
  },
  "engines": {
    "node": "12.18.1"
  }
}

現在使用しているnodeのバージョンは下記のコマンドで確認が可能です。

nodeのバージョン確認
$ node --version

JavaScriptファイルの作成

ファイル名は先程、pakage.jsonで指定したapp.jsにして下記を書きます。

app.js
constexpress=require('express');constapp=express();constport=process.env.PORT||5000;app.get('/',(req,res)=>{res.send('Hello World!');});app.listen(port,()=>{console.log('listen: '+port);});

ここまでできたらまずローカルで動くかを確かめてみましょう。

コマンドプロンプトで下記を実行してください。

$ node app.js

listen: 5000と表示されたらブラウザで下記を開いてください。

localhost:5000

無事「Hello World!」が表示されていたらOKです。
閉じる場合コマンドプロンプト上でCtrl + C を押すと終了できます。

Procfileの作成

コマンドプロンプトで下記を実行

Procifleの作成
type nul > Procifle

作成したら開いて下記を入力

web: node app.js

最終的には下記のフォルダ構造になります。

フォルダ
フォルダ
 ├ node_modules
 ├ package.json
 ├ yarn.lock
 ├ app.js
 └ Procfile

herokuにデプロイ

デプロイするのに書きの順番で実行していきます。

herokuへのデプロイ
$ git init
$ git add .$ git commit -m'init'$ heroku login
$ heroku create
もしくは
$ heroku create ファイル名
$ git heroku push master

次回から更新する際は一度にまとめても記載が可能です

heroku
$ git add .&& git commit -m'init'&& git heroku push master

完了したらherokuを開きます

herokuを開く
$ heroku open

もしくはデプロイ時に出てきた
https://{アプリ名}.herokuapp.com/

をブラウザで開いても大丈夫です。
Hello World!が表示されていたら完了です。

herokuへのデプロイ時にエラーがでたら

下記のエラーが出た場合はyarnのバージョンが古いためのエラーです。

error Your lockfile needs to be updated, but yarn was run with `--frozen-lockfile`.

コマンドプロンプトで下記を実行してyarnをアップデートして再度実行してみてください。

$ yarn install

nodebrewで入れたnodeが存在するのにno such file or directoryになる問題

$
0
0

【注意】これを読んでも解決しません

環境

  • Raspberry Pi 4
  • Ubuntu Mate 64bit
  • nodebrew 1.0.1
    • node v12.18.3

症状

nodeコマンドが実行できない

$ node -v-bash: /home/user/.nodebrew/current/bin/node: No such file or directory
$ ls-la /home/user/.nodebrew/current/bin/node
-rwxr-xr-x 1 user user 40769188  7月 31 14:06 /home/user/.nodebrew/current/bin/node

あるじゃないか!!!

…存在するファイルの no such file or directory は、どうやら32bitに対応していない64bit OSで32bitのバイナリを実行しようとしたときに出るらしい?

諦め

たぶん
Can we update the $arch for aarch64 to have capability to choose arm64?
このissueが原因なのですが、PRも放置されマージされそうにないので、諦めて他の方法で入れようと思います

Obnizで動体検知センサー(HC-SR505)を利用して、動体検知したらスマホに通知。

$
0
0

「動体検知したらスマホに通知」をやりたい

こんなことをやってみよう。

使うのはこちら。

<ハードウェア>

<ソフトウェア>

  • Node.js
  • IFTTT

HC-SR505

Obnizに、HC-SR505を直刺しします。

Node.js

Node.jsで用意したコードです。

constifttt_event="obniz";//イベント名constifttt_secret_key="IFTTTのキーを入れます";//キーconstObniz=require("obniz");// デバイスに接続varobniz=newObniz("番号を入れます");//Obnizの番号を指定varwebclient=require("request");obniz.onconnect=asyncfunction(){varsensor=obniz.wired("HC-SR505",{vcc:0,signal:1,gnd:2});//IFTTTのwebhooksのURLを指定。constIFTTT_URL='https://maker.ifttt.com/trigger/'+ifttt_event+'/with/key/'+ifttt_secret_key;sensor.onchange=asyncfunction(val){if(val){//動きを検知したときobniz.display.clear();obniz.display.print("Moving Something!");//IFTTTリクエストwebclient.post({url:IFTTT_URL},function(error,response,body){console.log(body);});}else{//検知しないときobniz.display.clear();obniz.display.print("Nothing moving");}}}

検知したとき

動体検知すると、スマホに通知が届きます。
(”誰か来ました”については、IFTTTのメッセージで設定しています)

Raspberry Pi4 地デジサーバ Chinachu+mirakurun の Node.jsをアップデートしてみた

$
0
0

Raspberry Pi4で地デジサーバを Chinachu +mirakurun で稼働していますが、以下のようにmirakurunをv3.2.0にアップデートした後、chinachuもアップデートしたところ、動かなくなりました。

$ sudo npm install mirakurun -g --unsafe-perm --production  //mirakurunのアップデート

$ cd chinachu
$ ./chinachu updater  //chinachuのアップデート

アップデート後、ステータス($ sudo pm2 status)を確認したところ、erroredと表示されました。

なぜ動かなくなったか

原因は、Node.js をアップデートする前に、mirakurun をアップデートしたためでした。
それで、Node.jsをv8.9.4からv10.16.3にアップデートしました。

正しいアップデートの方法

1.Chinachuのユーザー追加

まず、Chunachu は Raspberry Pi のデフォルトユーザーでは動かない(録画が乱れる)ようなのでユーザーを追加します。(Chinachuを使うためにユーザーをvideoグループに追加する。録画先のディレクトリ権限も変更。)

$ sudo adduser <追加ユーザ>
$ sudo gpasswd -a <追加ユーザ> sudo
$ sudo adduser <追加ユーザ> video
$ sudo chown <追加ユーザ>:video /mnt/hdd1

Sambaの設定も修正します。

$ sudo nano /etc/samba/smb.conf

[recorded]
   comment = Chinachu Recorded Folder
   path = /mnt/hdd1/recorded
   browsable = yes
   read only = no
   guest ok = yes
   force user = <追加ユーザ>

# sambaを再起動
$ sudo service smbd restart

# ディレクトリのオーナーを変更
$ sudo chown <追加ユーザ>:video /mnt/hdd1

2.Node.jsとMirakurunのアップデート

Node.jsをv10.16.3にアップデートします。
また、Mirakurunも更新をします。

$ sudo apt-get install npm
$ sudo npm install -g n
$ sudo n 10.16.3
$ sudo npm install -g npm

$ sudo npm install pm2 -g
$ sudo npm install mirakurun -g --unsafe --production

3.Chinachuのアップデート

その前に・・・Chinachu録画設定ファイルに追加ユーザなどを設定します。

$ cd chinachu
$ vi config.json
  "uid": "<追加ユーザ>"
  "recordedDir" : "/mnt/hdd1/recorded/",
  "temporaryDir" : "/mnt/hdd1/recordtmp/",

そして、Chinachuのアップデートをします。

~/chinachu $ ./chinachu updater

$ sudo pm2 status //全部onlineになっているか確認!
$ sudo reboot

奮闘記(参考までに・・・)

1.Node.jsの調整

$ sudo apt remove nodejs
$ sudo apt autoremove
$ wget https://nodejs.org/dist/v10.22.0/node-v10.22.0-linux-armv7l.tar.xz
$ tar -xvf node-v10.22.0-linux-armv7l.tar.xz
$ cd node-v10.22.0-linux-armv7l
$ sudo rm /usr/local/bin/node  
$ sudo cp -R * /usr/local/
$ cd

今回は、Node.jsはv10.22.0にしましたが、v12やv14でも可能なようです。

ここで、以下の実行でエラーとなりました。

$ sudo apt-get install npm

パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています                
状態情報を読み取っています... 完了
インストールすることができないパッケージがありました。おそらく、あり得
ない状況を要求したか、(不安定版ディストリビューションを使用しているの
であれば) 必要なパッケージがまだ作成されていなかったり Incoming から移
動されていないことが考えられます。
以下の情報がこの問題を解決するために役立つかもしれません:

以下のパッケージには満たせない依存関係があります:
 ・・・

依存関係が壊れているようなので aptitude をインストールし、以下のコマンド実行することにしました。

$ sudo apt-get install aptitude
・・・
$ sudo aptitude install npm
・・・
以下のアクションでこれらの依存関係の問題は解決されます:

     以下のパッケージを現在のバージョンに一時固定する:
1)     libnode-dev [インストールされていません]       
2)     libnode64 [インストールされていません]         
3)     libssl-dev [インストールされていません]        
4)     node-gyp [インストールされていません]          
5)     npm [インストールされていません]               

この解決方法を受け入れますか? [Y/n/q/?]

Y を選択して先に進めます。
そして、次のコマンドを実行しましたがエラーになってしまいました。

$ sudo apt-get update

それで一旦最初からやり直すことにしました。

$ sudo apt remove --purge nodejs npm
$ sudo apt clean
$ sudo apt autoclean
$ sudo apt install -f
$ sudo apt autoremove
$ sudo apt install curl
$ cd ~
$ curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash -
curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash -
$ sudo apt-get install -y nodejs

$ curl -L https://www.npmjs.com/install.sh | sh

結局、以下の方法が良かったようです。

$ sudo npm install -g npm 
$ sudo npm install pm2 -g
$ sudo npm install arib-b25-stream-test -g --unsafe
$ sudo npm install mirakurun -g --unsafe --production

そして、ステータスの確認をします。

$ sudo pm2 status
┌────┬────────────────────┬──────────┬──────┬───────────┬──────────┬──────────┐
│ id │ name               │ mode     │ ↺    │ status    │ cpu      │ memory   │
├────┼────────────────────┼──────────┼──────┼───────────┼──────────┼──────────┤
│ 2  │ chinachu-operator  │ fork     │ 3    │ stopped   │ 0%       │ 0b       │
│ 1  │ chinachu-wui       │ fork     │ 1    │ stopped   │ 0%       │ 0b       │
│ 0  │ mirakurun-server   │ fork     │ 0    │ online    │ 0.3%     │ 76.9mb   │
└────┴────────────────────┴──────────┴──────┴───────────┴──────────┴──────────┘

pm2 に登録して保存します。

$ sudo pm2 start processes.json
$ sudo pm2 save

また、gitの修正を元に戻しておきます。

$ git fetch origin
($ git reset --hard origin/masterでエラー)

試しで録画をし、確認してみたところ、ひどく映像が乱れているのでチューニングを行いました。

2.Mirakurunのチューニング

バッファを増やします。

$ sudo mirakurun config server
#highWaterMark: 25165824 # integer (bytes)
#highWaterMark: 67108864 # 64MB
#highWaterMark: 134217728 # 128MB
highWaterMark: 268435456 # 256MB

次にヒープサイズを増やします。

$ sudo pm2 start mirakurun-server --node-args --max_old_space_size=1024

mirakurun config server で下記設定を追加。

disableIPv6: true

rocesses.jsonを修正してプロセスサイズの最大値も引き上げておく。

$ sudo vi /usr/lib/node_modules/mirakurun/processes.json
    :
      "node_args" : "--max_old_space_size=2048",

再度、mirakurunインストールをします。

$ sudo npm install mirakurun -g --unsafe --production

そして、以下を実行します。

$ export PATH=/home/pi/chinachu/.nave:$PATH 
$ cd /usr/lib/node_modules/npm
$ sudo npm install

ここまで試して、chinachuとmirakurunのステータスは online であるものの、録画が乱れたままでした。結局、前述の「1.Chinachuのユーザー追加」で解決しました。

主な参考サイト

DIGITAL CASSIS SODA内の記事ー https://mimimopu.com/chinachu-errored/
東京工業大学デジタル創作同好会の記事ー https://trap.jp/post/1089/

最後に

うれしいことに、Raspberry Pi4の発売により、地デジサーバを入れる人の人口が増えています。
今後、アップデートをする人も増えると思います。
少しでも、この記事が参考になれば幸いです。

また、Chinahuのこれからの改善や発展を期待しています!

文章から単語を取り出して数えるサービスを作成してみた

$
0
0

概要

「単語を数えるプログラミング」をテーマにお勉強してみました。
単語を数えるということで形態素解析を用いて文章から単語を取り出して単語数を数えてみるサービスを作ることにしました。

お勉強した結果ですが以下のサービスとして公開しておりますので、以下の 作ったモノからご確認いただければ幸いです。

image.png

使用しているフレームワークとライブラリ

今回はGlitchというNode.jsのアプリを公開するためのサービスを使用して公開しています。

Glitch

https://glitch.com/

フレームワークとライブラリについては以下を使用しております。

フレームワーク

ライブラリ

作ったモノ

公開しているサイトとコードは以下のアドレスでご確認いただけます。

公開先(作ったモノの動作を確認する)

https://morphological-analysis.glitch.me/

コード

https://glitch.com/edit/#!/morphological-analysis

作ったモノの説明

コードについては上記のコードをご確認いただければ幸いです。
以下に特記したい内容を記載します。

バックエンド

image.png

const express = require("express");
var compression = require('compression')
const app = express();
const kuromoji = require("kuromoji");
const analyze = require("negaposi-analyzer-ja");
app.use(compression())
app.use(express.static("public"));
app.set("json spaces", 2);

const builder = kuromoji.builder({
  dicPath: "node_modules/kuromoji/dict"
});

app.get("/", (request, response) => {
  response.sendFile(__dirname + "/views/index.html");
});

app.get("/morphological", (request, response) => {
  builder.build(function(err, tokenizer) {
    if (err) {
      throw err;
    }
    let reqMsg =
      request.query.msg ||
      "形態素解析とは文章を意味を持つ最小の単位に分けるイメージです";
    let tokenizedResult = tokenizer.tokenize(reqMsg)
    let result = {
      send_msg: reqMsg,
      morphological_result: tokenizedResult,
      negaposi_score: analyze(tokenizedResult)
    };

    console.log("tokened:" + result);
    response.json({ result });
  });
});

app.get("/negaposi", (request, response) => {
  builder.build(function(err, tokenizer) {
    if (err) {
      throw err;
    }
    let reqMsg =
      request.query.msg ||
      "形態素解析とは文章を意味を持つ最小の単位に分けるイメージです";

    const score = analyze(tokenizer.tokenize(reqMsg));
    builder = null;
    response.json({
      message: reqMsg,
      negaposi_score: score
    });
  });
});

// listen for requests :)
const listener = app.listen(process.env.PORT, () => {
  console.log("Your app is listening on port " + listener.address().port);
});

ExpressによるAPIサーバーです。文章を単語ずつに区切る形態素解析するためのAPIとして実装されています。ウェブサーバーとしてURLのトップにアクセスされた場合は、views/index.htmlを表示するようにしています。

形態素解析

https://ja.wikipedia.org/wiki/形態素解析

上記のWikipediaから抜粋させていただきます。
辞書と呼ばれる単語の品詞等の情報にもとづき、形態素(Morpheme, おおまかにいえば、言語で意味を持つ最小単位)の列に分割し、それぞれの形態素の品詞等を判別する処理です。

今回の場合はkuromoji.jsという形態素解析のライブラリを使用しています。
kuromoji.builderで辞書を読み込むことで形態素解析の処理を行っています。

なお、形態素解析の品質は辞書に依存することがほとんどで、今回はデフォルトの辞書を使用しているため、流行語などで単語を区切ったりすることはできません。

オマケ:ネガポジ判定

kuromoji.jsで形態素解析された結果を使ってネガポジ判定もできるようなので実装してみました。ネガポジ判定とは、主に人の発言や発想などが、前向き(ポジティブ)か後ろ向き(ネガティブ)かを判定するモノになります。

使用させていただいた negaposi-analyzer-jaでは、形態素解析された結果を 単語感情極性対応表を使用してネガポジ判定をしているようです。こちらも単語感情極性対応表に依存することになります。

ネガポジ判定自体はPythonなどの機械学習などの方が向いていると思います。BertWord2Vecなどを使えば、自分だけのネガポジなどを作れそうですね。

フロントエンド

image.png

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>morphological-analysis</title>
    <link
      rel="stylesheet"
      href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"
    />
    <link
      href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,700"
      rel="stylesheet"
    />
    <link
      rel="stylesheet"
      href="https://unpkg.com/bulma@0.8.2/css/bulma.min.css"
    />
  </head>

  <body>
    <section class="hero is-info is-fullheight" id="app">
      <div class="hero-body">
        <div class="container has-text-centered">
          <div class="column">
            <div class="box">
              <div class="field is-grouped">
                <p class="control is-expanded">
                  <input
                    class="input"
                    v-model="sendMsg"
                    type="text"
                    placeholder="形態素解析する文章を入力してください"
                  />
                </p>
                <p class="control">
                  <a class="button is-info" @click="morphoExec">
                    実行する
                  </a>
                </p>
              </div>
            </div>
            <div class="subtitle" v-if="isLoading">
              <progress
                class="progress is-large is-warning"
                max="100"
              ></progress>
            </div>
            <h2 class="subtitle has-text-left" v-if="sendMsg">
              {{sendMsg}}
            </h2>

            <h2 v-if="dataset.length > 0" class="bar-graph">
              <chartjs-horizontal-bar
                :labels="labels"
                :data="dataset"
                :bind="true"
                :datalabel="dataLabel"
                :option="chartOption"
                :height="chartHeight"
              ></chartjs-horizontal-bar>
            </h2>
            <h1 class="subtitle" v-if="negaposiScore">
              ネガポジスコア: {{negaposiScore}}
            </h1>

            <h3 class="subtitle has-text-left" v-if="meishiCountList">
              <p class="subtitle has-text-left">単語一覧</p>
              <li v-for="(item, key) in meishiCountList" class="has-text-left">
                {{ key }} : {{ item }}
              </li>
            </h3>
          </div>
        </div>
      </div>
    </section>

    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <script src="https://unpkg.com/vue-chartjs@2.6.0/dist/vue-chartjs.full.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.3.0/Chart.js"></script>
    <script src="https://unpkg.com/hchs-vue-charts@1.2.8"></script>
    <script>
      "use strict";
      Vue.use(VueCharts);
      new Vue({
        el: "#app",
        data: {
          sendMsg: null,
          meishiCountList: null,
          negaposiScore: null,
          isLoading: false,
          labels: [],
          dataset: [],
          dataLabel: "名詞 カウント",
          chartOption: {
            legend: {
              display: false
            },
            responsive: true,
            maintainAspectRatio: false,
            scales: {
              xAxes: [
                {
                  ticks: {
                    max: 5,
                    min: 0,
                    stepSize: 1
                  }
                }
              ]
            }
          },
          chartHeight: 500
        },
        methods: {
          morphoExec() {
            this.negaposiScore = null;
            this.labels = [];
            this.dataset = [];
            this.sendMsg =
              this.sendMsg ||
              "何も文字が入力されなかったのでサンプルの文章で形態素解析を実行します。何か文字を入力することで単語をカウントします。";
            const sendMsg = this.sendMsg;
            this.isLoading = true;
            const vueThis = this;
            axios
              .get("/morphological?msg=" + sendMsg)
              .then(function(response) {
                const morphoResult = response.data.result.morphological_result;
                let meishiList = [];
                morphoResult.filter(function(item, index) {
                  if (item.pos == "名詞") {
                    meishiList.push(item.surface_form);
                    return true;
                  }
                });
                let meishiCount = {};

                for (let i = 0; i < meishiList.length; i++) {
                  let key = meishiList[i];
                  meishiCount[key] = meishiCount[key]
                    ? meishiCount[key] + 1
                    : 1;
                }

                vueThis.labels = Object.keys(meishiCount);
                vueThis.dataset = Object.values(meishiCount);
                vueThis.negaposiScore = response.data.result.negaposi_score;
                vueThis.meishiCountList = meishiCount;
                vueThis.chartOption.scales.xAxes[0].ticks.max = Math.max.apply(
                  null,
                  Object.values(meishiCount)
                );

                vueThis.chartHeight = 30 * meishiList.length;
                console.log(
                  "最大値:" + Math.max.apply(null, Object.values(meishiCount))
                );
                console.log(vueThis.meishiCountList);
              })
              .finally(() => (this.isLoading = false));
          }
        }
      });
    </script>
    <style>
      .bar-graph {
        margin-left: auto;
        margin-right: auto;
        padding-top: 10%;
        padding-bottom: 10%;
        background-color: #f5f5f5;
      }
    </style>
  </body>
</html>

Vue.js

Expressでもテンプレートエンジンとして使用できるのですが、今回のフロントエンドのフレームワークはVue.jsを使用しています。単純にVue.jsが書きやすかったので選択(いろいろとダメですが・・・)しました。

バックエンドのAPIへの接続から、形態素解析された後のデータの処理をVue.jsmethodsで処理しています。返ってくるデータを整理するために、いろいろと試行錯誤していますが、もう少しイイカンジに書けそうな気がします。

Bulma

CSSや見た目のデザイン面ではBulmaを使用しています。今回は以下のテンプレートから参考させていただきました。

https://bulmatemplates.github.io/bulma-templates/templates/landing.html

Charts.js

以下のサイトを参考にグラフを追加しました。

https://vuejsexamples.net/vue-bar-chart/

当初はなかなかChart.jsのプロパティ(設定)の理解ができませんでした。
しかしながら元のライブラリのコードを読んだり、ドキュメントをちゃんと読むことで少しずつ理解することできました。具体的にやったことはOptionを設定する(上書きする)ことでグラフの設定をカスタマイズすることができました。

課題

形態素解析するときに辞書を読み込むので極端にメモリを消費しています。
image.png

今後辞書を良いものに変えると辞書の容量も増えることになり、もっとメモリを消費することになります。もし品質を向上させる場合は、Glitchをアップグレードするか他のサービスを使うことを検討する必要がありそうです。

ちなみに当初は Netlify Functionsを使おうとしていて、辞書ファイルの読み込みが上手くいきませんでした。これはファイルの読み込み(fs.readFile)に工夫がいるとのことで、今回はGlitchを選択しています。

コードも改善余地があり形態素解析した結果の処理などはもっとイイカンジにできると思います。そもそもExpressに統一した方がよいかもしれません。ただ、今回はお勉強が目的でどんどん動かしていくモノを作っていけたので一旦は良い結果が得られたと思います。

[JavaScript] セミコロンをつければ絶対安心できるのか?;

$
0
0

はじめに

こちらの記事を読んで、改めてJavaScriotの ;について気になったので調べてみました。

結論

私が思ったより安心じゃなかった。

セミコロンありのスタイルでも、下記のように予期せぬセミコロンの挿入が起こってしまうパターンがあります。

functionf(){return{foo:'foo'};}f();// undefined

セミコロンに関することは参考にさせていただいたセミコロンに関する ESLint ルールという記事に全部書いてありました :pray::pray::pray:

以下の内容はリンク先の記事に(個人的な感想以外)書いてあることなので、ぜひそちらを参考にしてください!

ASI(Auto Semicolon Insertion)

JavaScript の改行箇所で構文エラーがあった際にセミコロンにを自動的に補い再解釈する言語機能のことですが、結局のところこいつが曲者で、初心者や他言語からJavaScriptを触るようになった方が理解し難く感じる要因でしょう。

ECMAScript の言語仕様に ASI 利用に関する警告が ES2019 から追加されています。

ASIの振る舞いで気をつけるべきこと

予期せぬセミコロン挿入の欠損

console.log(4)// > 4['foo','bar'].forEach(el=>console.log(el))// > Uncaught TypeError: Cannot read property 'bar' of undefined

console.log(4)['foo', 'bar'].forEachが構文上正しいと解釈され、セミコロンの自動挿入が起こりません。,がカンマ演算子として解釈されるため、 barが未定義というエラーが出ます。

このパターンは実行してみてエラーで気づくことができそうです。また、TypeScriptなら型チェックで気づけます。

予期せぬセミコロン挿入

functionf1(){return2020;}functionf2(){return{foo:'foo'};}functionf3(){return({foo:'foo',bar:'bar'});}f1();// undefinedf2();// undefinedf3();// undefined

こちらが先に上げたパターンと同じですが、 returnのあとにセミコロンが自動挿入され、改行後の値が返されません。

また、f2の関数には Object そのものが存在していません。
{ foo: 'foo' }の部分は Object ではなく、ブロックとして解釈されてしまっています。 foo:fooラベル構文です。意味のないブロックですが、文法そのものに影響しないので無視されています。

C#とか Allman brace style が推奨の言語から来た方は引っかかる?(多くの場合はそんなことないでしょうが)
Lint などがない場合は間違って改行していた場合気づかないかもしれませんね。

ちなみに、ラベル構文の後にカンマをつけることはできないため以下は構文エラーになります。

functionf(){return{foo:'foo',bar:'bar
        };
}

// > Uncaught SyntaxError: Unexpected token :

まとめ

今回調べてみて結局のところ、セミコロンありなのかなしなのかは好みの問題という域を出ないように感じました。
それよりも、ASIの独特の動きを理解して(といってもそんなに多くの動作は無いように思います。)、Lint や Formatter などのツールをしっかり活用することで、こういった問題を起こさない様にすることの方が重要だと思います。

特に、ESLintやPrettierを使う際にはJavaScript Standard Styleのようなプリセットを利用し、極力自力で設定しないことが、このような罠にハマらないために大切です。(JavaScript Standard Styleにはセミコロンあり版が存在します)

また、可読性についても一長一短で、個人によるところが大きいと思います。(私はセミコロンレスの方が見やすく感じます)

参考

セミコロンをつけ忘れただけなのに...【JavaScript】

セミコロンに関する ESLint ルール

To Semicolon, Or Not To Semicolon;

Using Semicolons? Never Use Them!

JavaScript Standard Style

AWS Amplify での Cognito アクセスは React Context.Provider を使って認証処理を Hooks 化しよう

$
0
0

Amplify × React Hooks.png

AWS Cognito は認証・認可を提供している AWS のサービスです。Amplify と統合することで、超高速に構築できます。Cognito を使用することで、API Gateway や S3 など他の AWS サービスとの統合がより簡単にできるようになります。

本記事では、Cognito を使用した React アプリケーションの実装例を紹介します。Cognito へのアクセスには amplify-jsというライブラリを使用します。さらに React の Context.Provider という機能を使うことで認証に関連する処理をカスタムフックに集約する方法を考察します。

本記事で実装されたアプリケーションは以下のような動作をします。ログイン、ログアウト、サインアップ、確認メールなど。

完成するアプリケーション

本アプリケーションは Vercel にデプロイされています。
https://task-app.geeawa.vercel.app/login

また、以下の GitHub リポジトリにホストしています。
https://github.com/daisuke-awaji/task-app

amplify-js でも React Hooks を使いたい

先週は React アプリに Auth0 でシュッと認証を組み込んで Vercel に爆速デプロイするという記事を書きました。Auth0 のクライアントライブラリは非常に使い勝手がよく、<Auth0Provider>という Provider で包むだけで useAuth0フックを使用できるようになります。

index.tsx
importReactfrom"react";importReactDOMfrom"react-dom";import{Auth0Provider}from"@auth0/auth0-react";import"bootstrap/dist/css/bootstrap.min.css";import{App}from"./App";ReactDOM.render(<Auth0Providerdomain={process.env.REACT_APP_AUTH0_DOMAIN!}clientId={process.env.REACT_APP_AUTH0_CLIENT_ID!}redirectUri={window.location.origin}><App/></Auth0Provider>,document.querySelector("#root"));

一方で amplify-js にはこのような機能はありません。認証系処理のメソッドは Authモジュールから取り出して使う必要があります。以下はサインアップするメソッドです。参考: 公式 Sign up, Sign in & Sign out

import{Auth}from"aws-amplify";asyncfunctionsignUp(){try{constuser=awaitAuth.signUp({username,password,attributes:{email,phone_number,},});console.log({user});}catch(error){console.log("error signing up:",error);}}

メソッドしか用意されておらず、ログインユーザの情報などを React アプリでグローバルに保持する仕組みは自分で用意する必要があります。amplify-js でも Auth0 のような使いやすい DX(開発者体験)にしたい! ということが本記事のモチベーションです。つまり、以下のように使用したいわけです。

index.tsx
importReactfrom"react";importReactDOMfrom"react-dom";importAppfrom"./App";import*asserviceWorkerfrom"./serviceWorker";import"./index.css";importCognitoAuthProviderfrom"./cognito/CognitoAuthProvider";ReactDOM.render(<CognitoAuthProvider><App/></CognitoAuthProvider>,document.getElementById("root"));

<App/>コンポーネントを <CognitoAuthProvider>でラップするだけで、認証系の処理やログインユーザのステートを取り出す useAuthフックが使えるようにしていきます。

LogoutButton.tsx
importReactfrom"react";import{useAuth}from"../../cognito/CognitoAuthProvider";exportdefaultfunctionLogoutButton(props:any){const{isAuthenticated,signOut}=useAuth();if(!isAuthenticated)returnnull;return<ButtononClick={()=>signOut()}{...props}/>;}

React.Context とは

React の Contextは配下の子コンポーネントにデータを渡すための便利な方法です。従来は propsを使用することで、子コンポーネントにデータを渡していましたが、コンポーネントのネストが深くなると非常に面倒で複雑になります。 Context を使用することで 認証UI テーマなど多くのコンポーネントが使用する情報を共有して保持・取得できます。

context.provider.png

React.createContext

Context オブジェクトを作成します。React がこの Context オブジェクトが登録されているコンポーネントをレンダーする場合、ツリー内の最も近い上位の一致する Provider から現在の Context の値を読み取ります。

constMyContext=React.createContext(defaultValue);

Context.Provider

全ての Context オジェクトには Context.Provider コンポーネントが付属しています。これにより Context.Consumer コンポーネントは Context の変更を購読できます。実際のユースケースでは Consumer ではなく、useContext フックを使用することが多いでしょう。

<MyContext.Providervalue={/* 何らかの値 */}>

useContext

Context オブジェクトを受け取り、その Context の value を返します。<MyContext.Provider/>が更新されると、このフックは MyContext.Providerに渡された value を使用してコンポーネントを再レンダーします。

constvalue=useContext(MyContext);

認証情報を Context に集約する

さて、認証情報として以下のようなメソッドとステートを保持する Context を作っていきます。これらの値があればログイン、ログアウト、サインアップ、確認コード入力の一連の流れが実装できます。

項目概要
isAuthenticatedログインしているか
isLoadingローディング中か(画面制御で使用)
userログインしているユーザの情報
errorログイン処理、サインアップ処理などでエラーがあれば詰める
signInサインインする。
signUpサインアップする。
confirmSignUpサインアップ確認コードを入力する
signOutサインアウトする。

State

Context が保持するステートの定義(インタフェース)を作成します。

import{CognitoUser}from"amazon-cognito-identity-js";exportinterfaceAuthState{isAuthenticated:boolean;isLoading:boolean;user?:CognitoUser;error?:any;}constinitialState:AuthState={isAuthenticated:false,isLoading:false,};conststub=():never=>{thrownewError("You forgot to wrap your component in <CognitoAuthProvider>.");};exportconstinitialContext={...initialState,signIn:stub,signUp:stub,confirmSignUp:stub,signOut:stub,};

Context

Context オブジェクトを作成します。各コンポーネントから取り出すためのカスタムフック useAuth()を合わせて作成しておきます。

importReact,{useContext}from"react";import{SignUpParams}from"@aws-amplify/auth/lib-esm/types";import{CognitoUser}from"amazon-cognito-identity-js";import{AuthState,initialContext}from"./AuthState";import{LoginOption}from"./CognitoAuthProvider";interfaceIAuthContextextendsAuthState{signIn:(signInOption:LoginOption)=>Promise<void>;signUp:(params:SignUpParams)=>Promise<CognitoUser|undefined>;confirmSignUp:(params:any)=>Promise<void>;signOut:()=>void;}exportconstAuthContext=React.createContext<IAuthContext>(initialContext);exportconstuseAuth=()=>useContext(AuthContext);

Provider

最後に Provider には Cognito とやりとりする処理と、認証情報を保持する処理を実装します。

importReactfrom"react";import{useState,useEffect}from"react";import{SignUpParams}from"@aws-amplify/auth/lib-esm/types";import{CognitoUser}from"amazon-cognito-identity-js";import{Auth}from"aws-amplify";importAmplifyfrom"aws-amplify";import{AuthContext}from"./AuthContext";exporttypeLoginOption={username:string;password:string;};interfaceICognitoAuthProviderParams{amplifyConfig:{aws_project_region:string;aws_cognito_identity_pool_id:string;aws_cognito_region:string;aws_user_pools_id:string;aws_user_pools_web_client_id:string;oauth:{domain:string;scope:string[];redirectSignIn:string;redirectSignOut:string;responseType:string;};federationTarget:string;};children:any;}exportdefaultfunctionCognitoAuthProvider(props:ICognitoAuthProviderParams){Amplify.configure(props.amplifyConfig);const[isAuthenticated,setIsAuthenticated]=useState(false);const[isLoading,setIsLoading]=useState(true);const[error,setError]=useState(null);const[user,setUser]=useState<CognitoUser>();useEffect(()=>{checkAuthenticated();currentAuthenticatedUser();},[]);constcheckAuthenticated=()=>{setIsLoading(true);Auth.currentSession().then((data)=>{if(data)setIsAuthenticated(true);}).catch((err)=>console.log("current session error",err)).finally(()=>{setIsLoading(false);});};constcurrentAuthenticatedUser=async():Promise<void>=>{constuser:CognitoUser=awaitAuth.currentAuthenticatedUser();setUser(user);};constsignIn=async({username,password}:LoginOption):Promise<void>=>{setIsLoading(true);try{awaitAuth.signIn(username,password);setIsAuthenticated(true);}catch(error){console.log("error signing in",error);setError(error);setIsAuthenticated(false);}setIsLoading(false);};constsignUp=async(param:SignUpParams):Promise<CognitoUser|undefined>=>{setIsLoading(true);letresult;try{result=awaitAuth.signUp(param);setUser(result.user);}catch(error){console.log("error signing up",error);setError(error);}setIsLoading(false);returnresult?.user;};constconfirmSignUp=async({username,code}:any):Promise<void>=>{setIsLoading(true);try{awaitAuth.confirmSignUp(username,code);setIsAuthenticated(true);}catch(error){console.log("error confirming sign up",error);setError(error);}setIsLoading(false);};constsignOut=()=>{setIsLoading(true);Auth.signOut().then(()=>{setIsAuthenticated(false);}).catch((err)=>console.log("error signing out: ",err)).finally(()=>{setIsLoading(false);});};return(<AuthContext.Providervalue={{isAuthenticated,isLoading,signIn,signUp,confirmSignUp,signOut,user,error,}}>{props.children}</AuthContext.Provider>);}

使用方法

ここまで準備ができれば使用する側はこの CognitoAuthProviderでコンポーネントをラップすることで useAuth()フック経由で各種ステートの値またはメソッドを使用できます。

amplifyConfig として設定値は外部ファイルで保持しています。

importReactfrom"react";importReactDOMfrom"react-dom";importAppfrom"./App";import"./index.css";importCognitoAuthProviderfrom"./cognito/CognitoAuthProvider";importawsconfigfrom"./aws-exports";ReactDOM.render(<CognitoAuthProvideramplifyConfig={awsconfig}><App/></CognitoAuthProvider>,document.getElementById("root"));

amplifyConfig は以下のようなファイルになります。

constamplifyConfig={aws_project_region:"ap-northeast-1",aws_cognito_identity_pool_id:"ap-northeast-1:12345678909876543234567890",aws_cognito_region:"ap-northeast-1",aws_user_pools_id:"ap-northeast-1_xxxxxxxx",aws_user_pools_web_client_id:"xxxxxxxxxxxxxxx",oauth:{domain:"mydomain.auth.ap-northeast-1.amazoncognito.com",scope:["phone","email","openid","profile","aws.cognito.signin.user.admin",],redirectSignIn:"http://localhost:3000/",redirectSignOut:"http://localhost:3000/logout/",responseType:"code",},federationTarget:"COGNITO_USER_POOLS",};exportdefaultamplifyConfig;

ログアウトボタンのコンポーネントです。コードベースをシンプルにできました。

LogoutButton.tsx
importReactfrom"react";import{useAuth}from"../../cognito/CognitoAuthProvider";exportdefaultfunctionLogoutButton(props:any){const{isAuthenticated,signOut}=useAuth();if(!isAuthenticated)returnnull;return<ButtononClick={()=>signOut()}{...props}/>;}

さいごに

React の Context を使用することで、認証情報などのグローバルな値を一元的に管理できるようになります。
ただ、 Context は多くのコンポーネントからアクセスされる場合に使用することとしましょう。
Context はコンポーネントの再利用をより難しくする為、慎重に利用してください。

本記事で紹介した React.Context を使用したカスタムフックを使用するという発想はそのうち amplify-jsに PullRequest しようと思います。Cognito ユーザ(または Amplify ユーザ)が個別にこのような実装をしなくとも、ライブラリとして提供し、すぐに簡単なインタフェースで認証処理を実現できるようにしていきたいですね。

AWS Lambda(Node.js)においてmiddyを使ってAmazon RDSに接続する方法

$
0
0

AWS Lambda(Node.js)において、middyを使ってAmazon RDSに接続する方法を紹介します。

はじめに

データベースエンジン

この記事ではデータベースエンジンとしてAurora MySQLを使用している場合の例を示しますが、他のデータベースエンジンの場合も基本的な実装は変わりません。

プログラミング言語

この記事ではソースコードをTypeScriptで書いているので型定義をインストールしたり型アノテーションを記述したりしていますが、JavaScriptで書く場合は不要です。

ミドルウェア

middyでRDSに接続するために、@middy/db-managerを使います。
@middy/db-managerでは、データベースクライアントとしてKnex.jsが使われます。

接続情報をAWS Secrets Managerで管理していない場合

接続情報をAWS Secrets Managerで管理していない場合、ミドルウェアとして@middy/db-managerのみを使います。

パッケージをインストール

middyを使うために、@middy/coreパッケージをインストールします。

$ npm i -d @middy/core

middyでRDSに接続するために、@middy/db-managerパッケージをインストールします。

$ npm i -d @middy/db-manager

また、データベースエンジンに応じたパッケージをインストールします。
今回はAurora MySQLなので、mysqlパッケージをインストールします。

$ npm i -d mysql

Lambdaの型定義として、@types/aws-lambdaパッケージをインストールします。

$ npm i -D @types/aws-lambda

ハンドラーを作成

middyを用いて、ハンドラーをつくります。

src/index.ts
importmiddyfrom'@middy/core'importdbManagerfrom'@middy/db-manager'importlambdafrom'aws-lambda'importKnexfrom'knex'interfaceContextextendslambda.Context{db:Knex}consthandler=middy(async(event:any,context:Context,callback:lambda.Callback<any>):Promise<void>=>{constusers=awaitcontext.db.select('*').from('users')console.log(users)})handler.use(dbManager({config:{client:'mysql',connection:{host:'127.0.0.1',port:3306,user:'your_database_user',password:'your_database_password',database:'myapp_test'}}}))export{handler}

詳しく解説していきます。

interfaceContextextendslambda.Context{db:Knex}

@middy/db-managerを使うとコンテキストのdbプロパティにKnexインスタンスが割り当てられるので、dbプロパティを持つContextを定義しています。

consthandler=middy(async(event:any,context:Context,callback:lambda.Callback<any>):Promise<void>=>{constusers=awaitcontext.db.select('*').from('users')console.log(users)})

ハンドラーをmiddy関数でラップすることで、middyfy(middy化)しています。
ハンドラー内では、context.dbからKnex.jsのAPIを用いてデータベース操作が行なえます。

handler.use(dbManager({config:{client:'mysql',connection:{host:'127.0.0.1',port:3306,user:'your_database_user',password:'your_database_password',database:'myapp_test'}}}))

ハンドラーで、ミドルウェアとしてdbManagerを使っています。
configには、Knex.jsの設定オブジェクトを渡します。
ここでは例示のために接続情報をべた書きしていますが、実際には環境変数やAWS Secrets Managerで管理すべきでしょう。

接続情報をAWS Secrets Managerで管理している場合

接続情報をAWS Secrets Managerで管理している場合、ミドルウェアとして@middy/db-manager@middy/secrets-managerを使います。

パッケージをインストール

middyを使うために、@middy/coreパッケージをインストールします。

$ npm i -d @middy/core

middyでRDSに接続するために、@middy/db-managerパッケージをインストールします。

$ npm i -d @middy/db-manager

また、データベースエンジンに応じたパッケージをインストールします。
今回はAurora MySQLなので、mysqlパッケージをインストールします。

$ npm i -d mysql

middyでSecrets Managerに接続するために、@middy/secrets-managerパッケージをインストールします。

$ npm i -d @middy/secrets-manager

Lambdaの型定義として、@types/aws-lambdaパッケージをインストールします。

$ npm i -D @types/aws-lambda

ハンドラーを作成

middyを用いて、ハンドラーをつくります。

src/index.ts
importmiddyfrom'@middy/core'importdbManagerfrom'@middy/db-manager'importsecretsManagerfrom'@middy/secrets-manager'importlambdafrom'aws-lambda'importKnexfrom'knex'interfaceContextextendslambda.Context{db:Knex}constMIDDY_RDS_SECRET_KEY='MIDDY_RDS_SECRET'consthandler=middy(async(event:any,context:Context,callback:lambda.Callback<any>):Promise<void>=>{constusers=awaitcontext.db.select('*').from('users')console.log(users)})handler.use(secretsManager({secrets:{[MIDDY_RDS_SECRET_KEY]:'secret_name'},cache:true,throwOnFailedCall:true}))handler.use({before:(handler,next)=>{interfaceSecret{host:stringport:numberusername:stringpassword:stringdbname:string}interfaceConnection{host:stringport:numberuser:stringpassword:stringdatabase:string}interfaceContextextendslambda.Context{[MIDDY_RDS_SECRET_KEY]:Secret|Connection}constcontext=handler.contextasContextconstsecret=context[MIDDY_RDS_SECRET_KEY]asSecretcontext[MIDDY_RDS_SECRET_KEY]={host:secret.host,port:secret.port,user:secret.username,password:secret.password,database:secret.dbname}returnnext()}})handler.use(dbManager({config:{client:'mysql'},secretsPath:MIDDY_RDS_SECRET_KEY,removeSecrets:true}))export{handler}

詳しく解説していきます。

interfaceContextextendslambda.Context{db:Knex}

@middy/db-managerを使うとコンテキストのdbプロパティにKnexインスタンスが割り当てられるので、dbプロパティを持つContextを定義しています。

consthandler=middy(async(event:any,context:Context,callback:lambda.Callback<any>):Promise<void>=>{constusers=awaitcontext.db.select('*').from('users')console.log(users)})

ハンドラーをmiddy関数でラップすることで、middyfy(middy化)しています。
ハンドラー内では、context.dbからKnex.jsのAPIを用いてデータベース操作が行なえます。

handler.use(secretsManager({secrets:{[MIDDY_RDS_SECRET_KEY]:'secret_name'},cache:true,throwOnFailedCall:true}))

ハンドラーで、ミドルウェアとしてsecretsManagerを使っています。
secretsでは、コンテキストのどのプロパティに対してSecrets Managerのどのシークレットを割り当てるかを指定します。
cachetrueにすることで、キャッシュを有効化しています。
throwOnFailedCalltrueにすることで、シークレットの取得に失敗した場合にエラーをスローするようにしています。

handler.use({before:(handler,next)=>{interfaceSecret{host:stringport:numberusername:stringpassword:stringdbname:string}interfaceConnection{host:stringport:numberuser:stringpassword:stringdatabase:string}interfaceContextextendslambda.Context{[MIDDY_RDS_SECRET_KEY]:Secret|Connection}constcontext=handler.contextasContextconstsecret=context[MIDDY_RDS_SECRET_KEY]asSecretcontext[MIDDY_RDS_SECRET_KEY]={host:secret.host,port:secret.port,user:secret.username,password:secret.password,database:secret.dbname}returnnext()}})

Secrets Managerに保存されているシークレットを、Knex.jsに渡す形式に変換しています。
シークレットの形式は、データベースエンジンによって異なります。1

handler.use(dbManager({config:{client:'mysql'},secretsPath:MIDDY_RDS_SECRET_KEY,removeSecrets:true}))

ハンドラーで、ミドルウェアとしてdbManagerを使っています。
secretsPathには、Secrets Managerのシークレットが割り当てられているコンテキストのプロパティを指定します。
removeSecretstrueにすることで、データベースに接続後にコンテキストからシークレットを削除しています。


node.jsでsleep処理をasync awaitを使って書く

$
0
0

sleep処理を書くときはsetTimeoutを使うのが一般的だと思いますが、callbackを書きたくないので今風にasync awaitで書きます。

配列にある会社idの情報を1秒ごとに順番にリクエストする処理を書くと以下のようになります。

index.js
asyncfunctionsleep(ms){returnnewPromise(r=>setTimeout(r,ms));}asyncfunctionrequest(id){// 割愛}asyncfunctionexec(){constcompanyIdList=[1,2,3,4,5];for(constidofcompanyIdList){awaitsleep(1000);awaitrequest(id);}}exec();

Bitbucketからコンテナサービスにアプリをデプロイする方法

$
0
0

今回のチュートリアルでは、BitbucketからAlibaba Cloudコンテナサービスにアプリケーションをデプロイし、Let's Encrypt SSL/TLS 2019でセキュリティを確保します。

本ブログは英語版からの翻訳です。オリジナルはこちらからご確認いただけます。一部機械翻訳を使用しております。翻訳の間違いがありましたら、ご指摘いただけると幸いです。

Docker

Dockerは、コンテナを使ってアプリケーションを作成、デプロイ、実行するために設計されたオープンソース・ソフトウェアです。コンテナを利用することで、開発者はアプリケーションに必要なライブラリやその他の依存関係などをすべてパッケージ化し、1つのパッケージとして出荷することができます。そうすることで、コンテナのおかげで、開発者は、コードを書いたりテストしたりするために使用するマシンとは異なるカスタマイズされた設定があっても、他の Linux マシン上でアプリケーションを実行することができるので、安心してください。

コンテナは仮想マシンに少し似ています。しかし、仮想マシンとは異なり、仮想オペレーティングシステム全体を作成するのではなく、Dockerではアプリケーションは実行中のシステムと同じLinuxカーネルを使用することができ、アプリケーションはホストコンピュータ上で実行されていないものと一緒に出荷される必要があるだけです。これにより、パフォーマンスが大幅に向上し、アプリケーションのサイズを小さくすることができます。

このチュートリアルでは、Dockerを使ってアプリケーションをコンテナ化し、Alibaba Cloud Container Service上でデプロイして実行できるようにします。

Bitbucket

Bitbucketは、プロのチーム向けに設計されたGitバージョン管理リポジトリ管理ソリューションです。gitリポジトリの管理、ソースコードのコラボレーション、開発フローのガイドなどを一元的に行うことができます。最大5人の協力者と無制限に無料のプライベートリポジトリを作成することができます。これは、プロプライエタリなコードをホストするのに最適な場所になります。

主な機能は以下の通りです。

1、ソースコードへのアクセスを制限するアクセスコントロール。
2、プロジェクトやチームのワークフローを強制するワークフローコントロール。
3、コードレビューでの共同作業のためのインラインコメント付きのプルリクエスト。
4、開発の完全なトレーサビリティのための Jira 統合
5、マーケットプレイスから利用可能な機能がない場合、ワークフローに合わせたカスタム機能を構築するためのFull Rest API
このチュートリアルでは、Bitbucketを使用してアプリケーションのコードのリポジトリをホストし、CI/CDパイプラインを作成してAlibaba Cloud Container Serviceにアプリケーションをデプロイします。

Alibaba Cloud Container Registry

Alibaba Cloud Container Registry (ACR)は、コンテナ化されたイメージのライフサイクル管理を提供する安全なイメージホスティングプラットフォームです。ACRを使用すると、保存されたイメージを完全にコントロールすることができます。ACRには、GitHubやBitbucketなどとの統合を含む多くの機能があります。また、ソースコードからアプリケーションへのコンパイルやテスト後に自動的に新しいイメージを構築することもできます。画像レジストリの作成やメンテナンスを簡素化し、複数のリージョンでの画像管理をサポートします。Container Serviceなどの他のクラウドサービスと組み合わせることで、Container RegistryはクラウドでDockerを利用するための最適なソリューションを提供します。

このチュートリアルでは、DockerイメージのホスティングにACRを使用して、後からAlibaba Cloud Container Service上にアプリケーションをデプロイするために使用します。

Alibaba Cloud Container Service

Alibaba Cloud Container Service(ACS)は、DockerとKubernetesを使用してコンテナ化されたアプリケーションのライフサイクルを管理できる、高性能で拡張性の高いコンテナアプリケーション管理サービスです。

Container Serviceは、さまざまなアプリケーションの公開方法や継続的なデリバリー機能を提供し、マイクロサービスアーキテクチャをサポートしています。

Container Serviceはコンテナ管理クラスタの構築を簡素化し、アリババクラウドの仮想化、ストレージ、ネットワーク、セキュリティ機能を統合して、クラウド上に最適なコンテナ実行環境を構築します。

このチュートリアルでは、Alibaba Cloud Container Serviceを使用して、マイクロサービスをホストしてデプロイする方法を説明します。

Node.js

Node.jsは非同期イベント駆動型のJavaScriptランタイム環境です。これはスケーラブルなネットワークアプリケーションを構築するために設計されています。Node ランタイム環境には、JavaScript で書かれたプログラムをスタンドアロンアプリケーションとしてマシン上で実行するために必要なものがすべて含まれています。Node.js は V8 JavaScript ランタイムエンジン上で動作します。このエンジンはJavaScriptのコードを受け取り、より高速なマシンコードに変換します。マシンコードとは、コンピュータが最初に解釈しなくても実行できる低レベルのコードのことです。

このチュートリアルでは、Node.js を使ってシンプルな "Hello World" アプリケーションを作成し、ACS をデプロイしてみます。

SSL/TLSを暗号化してみよう

Let's Encryptは世界的な認証局(CA)です。彼らは、世界中の人々や組織が SSL/TLS 証明書を取得、更新、管理できるようにしています。これらの証明書は、ウェブサイトが安全な HTTPS 接続を可能にするために使用することができます。Let's Encrypt は、ドメイン検証(DV)証明書のみを提供しています。これらの証明書に手数料はかかりません。Let's Encrypt は、HTTPS の普及を促進することで、より安全でプライバシーを尊重した Web を構築することを使命とする非営利団体である。彼らのサービスは無料で簡単に利用できるので、どんなウェブサイトでも HTTPS を導入することができます。

このチュートリアルでは、Let's Encrypt SSL/TLSを使用してアプリケーションのセキュリティを確保します。

前提条件

まず最初に、アプリケーションのコードのプライベートリポジトリをBitbucket上に作成します。アカウントを持っていない場合は、アカウントにサインアップすることができます。

次に、Alibaba Cloud Servicesを使ってDockerイメージを作成し、コンテナ化されたアプリケーションをデプロイします。そのため、Alibaba Cloudのアカウントも設定しておく必要があります。アカウントを持っていない場合は、このリンクから無料のアカウントにサインアップすることができます。

パートⅠ:Bitbucket Gitリポジトリにアプリを追加する

このセクションでは、Bitbucket Gitリポジトリを作成し、そこにアプリケーションとDockerの設定を追加していきます。

ステップ1: Bitbucketリポジトリの作成

まず、Bitbucketアカウントにログインし、左メニューバーにある「+」ボタンをクリックし、オプションから「リポジトリ」を選択し、「新しいリポジトリの作成」フォームで、リポジトリの名前を入力します。そして、「リポジトリの作成」ボタンを押します。

image.png

ステップ2: アプリケーションファイルとDockerファイルをリポジトリに追加する

リポジトリの作成に成功したので、アプリケーションとDockerのファイルをリポジトリに追加していきます。

まずはローカルディレクトリに git を初期化します。まず、ローカルマシン上に空のディレクトリを作成し、"sample-app-for-alibaba-cloud "と名付け、"Git Bash "またはお好みのCLIで開きます。そして、その中でgit initcommandを実行します。

では、そのディレクトリにファイルを追加していきます。まず、以下のような内容のアプリケーションファイル "app.js "を追加します。

const http = require('http');
const hostname = '127.0.0.1';
const port = 3000;
const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello World\n');
});
server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});

これで、ポート3000のNode.jsが組み込まれたサーバ上で動作するシンプルな「Hello World」アプリが作成されます。

ここでは、Dockerの設定ファイル「Dockerfile」を追加し、以下の内容で作成します。

# Use Node version 10.15.1
FROM node:10.15.1
# Create a directory where our app will be placed
RUN mkdir -p /usr/src/app
# Change directory so that our commands run inside this new directory
WORKDIR /usr/src/app
# Get all the code needed to run the app
COPY . /usr/src/app
# Expose the port the app runs in
EXPOSE 3000
# Serve the app
CMD node app.js

ファイル内のコメントは、各行が何をするのかを説明する必要があります。このファイルで設定した内容をもとに、アプリケーションのコンテナイメージをどのように作成するかを決定します。

これで、これらのファイルを先ほど作成した Bitbucket リポジトリにプッシュする準備が整いました。

git add .
git commit -m "app.js and dockerfile added"
git remote add origin https://your-username@bitbucket.org/your-username/the-repo-name.git
git push -u origin master

パートII: BitbucketとACRの統合

最初に行う必要があるのは、Alibaba Cloud Container Registryを有効化することです。これは、Alibaba Cloud Consoleの「Elastic Computing」セクションの下にあります。

Container Registry Consoleに移動し、サービスを構成してデプロイすることができます。その際に、dockerクライアントのパスワードの入力を求められることがあるので、「dockerにログインする際には、先ほど設定したユーザー名とパスワードをAlibaba Cloudアカウントで使用する」と覚えておきましょう。

では、新しい ACR Namespace を作成します。 通常はコンテナを使ってマイクロサービスを作成します。 今回のアプリケーションでは単一のサービスのみを使用していますが、現実世界では通常そうはいきません。 そこで、ACR Namespacesの出番です。名前空間はレポジトリーのコレクションであり、リポジトリはイメージのコレクションです。 各アプリケーションに1つの名前空間を作成し、各サービスイメージに1つのリポジトリを作成することをお勧めします。

新しいネームスペースを作成するには、ACRコンソールのNamespacesタブに移動し、右上の "Create Namespace "ボタンをクリックします。 その後、名前空間の名前を入力し、"Confirm "ボタンをクリックします。

image.png

ここで、BitbucketアカウントをACRにバインドします。これを行うには、メインACRコンソールの "Code Source "タブに移動し、そこからBitbucketの横にある "Bind Account "ボタンをクリックしてください。

image.png

これでBitbucketに移動し、Alibaba Cloudへのアクセスを許可するように要求されます。そこで「アクセスを許可する」ボタンをクリックして次に進みます。

image.png

これで、ACR リポジトリを作成する準備ができました。リポジトリの詳細を入力するフォームが表示されますので、地域、名前空間、リポジトリ名、概要などを入力してください。

image.png

必要事項を記入した後、「次へ」ボタンをクリックしてください。次のページでは、「コードソース」オプションを選択するフォームが表示されます。このページでは、Bitbucket リポジトリを ACR リポジトリと統合します。Code Source "タブから "Bitbucket "を選択し、先ほど作成したBitbucket Repositoryをクリックします。

継続的インテグレーションの設定を支援するために、「イメージを自動的にビルドする」にチェックを入れてください。これを選択すると、コードを送信した後にイメージが自動的にビルドされ、手動でビルドを開始する必要はありません。最後に「リポジトリの作成」ボタンをクリックして、ACRリポジトリを作成し、Bitbucketリポジトリとの統合を行います。

image.png

パート III: アプリケーションのデプロイ

まず、VPC を作成する必要があります。そのためには、ネットワークの下の仮想プライベートクラウドに移動するだけです。VPC コンソールに移動し、"Create VPC" ボタンをクリックして VPC と VSwitch を作成します。ボタンをクリックすると、新しいVPCのオプションを選択するための小さなフォームが表示されますが、ここではデフォルトの設定を使用して名前を入力し、「OK」ボタンをクリックして続行します。

image.png

Alibaba Cloud Cloud Serviceを使い始めるには、まず、コンテナサービスのダッシュボードに移動します。このダッシュボードは、Alibaba Cloud ConsoleのElastic Computingセクションの下にあります。

最初に試してみると、「コンテナサービスはデフォルトのロールを作成する必要があります」という警告が表示されることがあります。これは、クラスタをリクエストすると、Alibaba Cloudがクラスタの実行に必要なリソースのプロビジョニングを行うためです。これには、ロードバランサー、仮想マシン、ルーターなどが含まれます。基本的には、自分のDockerクラスタを動かすために必要なものはすべて含まれています。そして、ロードバランサは動作する前にRAM(Resource Access Management)サービスを設定する必要があります...。これに対処するには、"Go to RAM console "ボタンをクリックし、リダイレクトされたら "Confirm Authorization Policy "ボタンをクリックします。

Container Service Overviewのページが表示されますので、左上の "Container Serv-Swarm "を選択し、"Create Cluster "ボタンをクリックします。

image.png

これでクラスタ作成ウィザードに移動します。名前、地域、ゾーン、VPCなど、クラスタを構成するために必要なすべてのオプションが表示されます。

image.png

大部分はデフォルト値を使用しますが、このデモの目的のために、"Instance Configuration "の "Instance Type "を "1 Core(s) 1 G ( ecs.n1.tiny ) "に変更します。また、このクラスタのために作成されるECSインスタンスのログインを設定する必要があります。これを設定した後、"Create "ボタンをクリックして続行します。その後、作成されたクラスタのすべての詳細を示すダイアログが表示されますので、すべてが正しいことを確認し、"OK "ボタンをクリックして続行します。

image.png

ここで、Bitbucket上にソースコード用のTag(release)を作成します。これを行うには、まず、先ほど作成したBitbucketリポジトリのコミットページに移動し、そこにプッシュした最新のコミットを選択します。そのページの右側に "タグ "オプションがあり、その横に "+"ボタンがありますので、それをクリックすると、新しいタグを作成するためのダイアログが開きます。

image.png

次に、ACR コンソールのリポジトリに戻り、先ほど作成した ACR リポジトリの横にある「管理」ボタンをクリックします。

image.png

リポジトリの詳細ページに移動し、リポジトリの名前、地域、タイプ、コードリポジトリリンク、インターネットアドレス、VPCアドレスなどの詳細が表示されます。そこから「ビルド」タブに移動し、「ビルドログ」の下に先ほど作成したタグのビルドが表示されます。ビルドのステータスが "Successful "になったら、右上の "Deploy Application "ボタンをクリックします。Container Cluster "オプションで "Swarm "を選択し、"Deploy "ボタンをクリックします。

image.png

これにより、ACSアプリケーション作成ウィザードに移動します。そこでは、「基本情報」タブで、名前、バージョン、クラスタ、更新、説明などのオプションが用意されています。名前を入力し、先ほど作成したクラスタを選択し、「Pull Docker Image」にチェックを入れて「Next」ボタンをクリックして次に進みます。

image.png

これで作成ウィザードの "Configuration "タブに移動します。ここではDockerコンテナの「Image Name」と「Image Version」を選択し、「Port Mapping」の「host Port」に「3000」を使用します。次に、「Web routing」の下にある「Port」と「Domain」を追加します。このページには他にも多くの設定オプションがありますが、ここではデフォルト値を使用します。ここで「作成」ボタンをクリックして、いよいよアプリケーションを作成します。

image.png

完了ページで "View Application List "リンクをクリックしてアプリケーションの一覧ページに進みます。次に「ルート」タブをクリックして、アプリケーションのアクセスエンドポイントを取得します。

image.png

これで、コンテナ化されたアプリケーションのデプロイに成功しました。さて、最後のパートに入りましょう。

パートIV: SSL/TLSを暗号化してアプリケーションを保護しよう

これを始めるには、まず、John HanleyによるAlibaba Cloud上でのLet's Encrypt ACMEの構成に関するこのガイドに従うべきである。さて、すべての設定を行い、証明書ファイルを作成したら、前述の手順に進みます。

サーバーのロードバランサー層ではHTTPSに対応しているので、HTTPSに対応しているかどうかを確認します。HTTPS をサポートするには、Server Load Balancer 証明書を作成する必要があります。これを行うには、ネットワーキングの下のサーバーロードバランサーコンソールに移動し、左パネルの「Certificates」リンクをクリックして「Certificates」ページに進みます。証明書の作成」ボタンをクリックし、「サードパーティ証明書のアップロード」オプションを選択して「次へ」ボタンをクリックします。

image.png

これで「Upload Third-Party Certificate」に移行し、必要事項を記入して「次へ」ボタンをクリックします。

image.png

証明書が正常に作成されたら、サーバーロードバランサーのページに移動し、クラスタ作成時に割り当てられたサーバーロードバランサーのインスタンスを探してクリックします。インスタンスに移動したら、「リスナー」タブの下にある「リスナーの追加」ボタンをクリックします。

image.png

リスナーを追加するためのウィザードが開きます。そこから、このガイドに従って HTTP を HTTPS に変更してウィザードを記入します。これで、Let's Encrypt SSL/TLSで保護されたコンテナ化アプリケーションが完成しました。

出典:コンテナ化されたアプリケーションのデプロイ方法の基本的なガイド BitbucketからAlibaba Cloud Container Serviceにアプリケーションをデプロイし、Let's Encrypt SSL/TLSでセキュリティを確保する方法の基本ガイド2019

アリババクラウドは日本に2つのデータセンターを有し、世界で60を超えるアベラビリティーゾーンを有するアジア太平洋地域No.1(2019ガートナー)のクラウドインフラ事業者です。
アリババクラウドの詳細は、こちらからご覧ください。
アリババクラウドジャパン公式ページ

Twilioを使って、ブラウザで電話を受け取れるようにする

$
0
0

やること

Twilioのライブラリを利用して、電話の着信機能を作成します。
Twilioへの登録と、電話番号の取得が必要です。
以下がTwilio側とブラウザ側の処理の流れです。

:telephone_receiver:Twilio側の流れ

  1. Twilioが通話を受け取る
  2. Twilioが自動応答のシナリオ(TwiML)を返すAPIを呼び出す
  3. TwilioがTwiMLを読み取って、クライアント(ブラウザの着信者)を呼び出す

:desktop:ブラウザ側の流れ

  1. twilio-clientライブラリを使って、着信時の挙動を設定する
  2. クライアントのトークンを作成するAPIを呼び出す
  3. トークンを使って、着信を受け取れるようにする

APIのコードをかく:muscle:

node.jsで書きます。前もってtwilioパッケージをインストールしときましょう
npm i twilio

TwiMLを返すAPIを用意する

expressを使っています。ざっくり書きます。

import{twiml}from"twilio";importexpress=require("express");// expressを利用constapp=express();app.route("/incoming").post((req,res)=>{constvoice=newtwiml.VoiceResponse();// ここに設定していくvoice.say("hello");// 音声を再生voice.say({voice:"Polly.Mizuki",language:"ja-JP"},"日本語でおk");// 日本語で再生(voiceなどは設定可能)voice.pause({length:1});// 1秒待つ// クライアント名を指定して電話をかけるvoice.dial({callerId:req.body.From||"",ringTone:"jp"})// オプション.client("[クライアント名]");constresponse=voice.toString();// TwiML(XML)を作成res.type("text/xml");res.send(response);// レスポンスを返す});

クライアントのトークンを作って返すAPIを用意する

ざっくり書きます。

import{jwt}from"twilio";// capabilityに能力を足していくconstcapability=newjwt.ClientCapability({accountSid:"TwilioアカウントのSID",authToken:"Twilioアカウントのトークン",ttl:600,// トークンの有効期限。最大24H(86400)らしい});capability.addScope(newjwt.ClientCapability.IncomingClientScope("[クライアント名]"),);// 今は使わないが、外にかけるときに使う// capability.addScope(//   new jwt.ClientCapability.OutgoingClientScope({//     applicationSid: "TwilioAppのSID",//   }));consttoken=capability.toJwt();// これを返す

TwilioコンソールでAPIを登録する:two_men_holding_hands:

↓コンソールはこれです
https://jp.twilio.com/console/

電話番号 / 番号の管理 / アクティブな電話番号 /の「A CALL COMES IN」にTwiMLを返すAPIを指定します。

クライアント側のコードをかく:muscle:

js(typescript)で書きます。JSフレームワークは好きなの選択。

前もってtwilio-clientをインストールしときましょう
(間違ってnpm i twilioをしないように気をつけてください。こっちはサーバ向けです)

デバイスのセットアップ方法を、ざっくり書きます。

import{Device,Connection}from"twilio-client";constdevice=newDevice();// ここに処理をのっけるdevice.on("incoming",(connection:Connection)=>{constonAccept=()=>{connection.accept();// 受け取る};constonReject=()=>{connection.reject();// 拒否る};openIncomingCallModal(onAccept,onReject);// Viewの部分はお任せ});

その後、前に作った「クライアントのトークンを作って返すAPI」を呼び出して、トークンを取得します(コード略)。
トークンを受け取ったら、それを使ってデバイスをセットアップ。

device.setup(jwt);

これでOK:ok_hand:

ちなみに、こちらから通話を切るときはdisconnectAll()を呼びます。

device.disconnectAll();

まだ機能が少ないので、ドキュメントをみて、諸々の機能を足していくといいかもしれません。

Twitter で 自動フォロワー獲得ツール を作った話 ②

$
0
0

まえがき

本記事は、Twitter で 自動フォロワー獲得ツール を作った話の続編です。
前編をご覧になっていない方は、まずは上記のリンクへどうぞ。
また、本記事に記載されている全体のソースコードは、Githubからご覧いただけます。

あらすじ

Twitter のフォロワーを増やすための法則を確立したものの、
GAOGAOエンジニアたるもの、プログラミングで実装してみてはどうか」
との @tejitakのお言葉を受け、Node.js で自動化を試みるのでした。
当時は開発経験がまるでなかったため、初のアプリケーション作成になりました。

おさらい

今回の実装内容
どんなコンテンツでも、あるフォーマットに沿ってツイートすれば、ほぼ確実にライクとフォローが期待できる法則

毎日定時に決まったフォーマットでツイートする
自身のタイムラインの全ツイートを可能な限り早くライクする
自身のフォロワーで、フォロワー数の多いユーザーの、最新のツイートにライクしている全ユーザーをフォローする
③ でフォローしたユーザーの中から、フォローバックされていないユーザーのプロフィールに行き、最新のツイート 3件 にライク + ミュートする
一定期間後、フォローされていない、且つミュートになっているユーザーをリムる

⓪Twtter API の 利用

まず、Twitter Developersから API を登録しました。すると、

  • Consumer Key
  • Consumer Secret
  • Access Token
  • Access Token Secret

が発行されるので、環境変数に保存します。
これらを利用して、実際に Twitter にアクセスします。
以前は審査等なかったのですが、現在は英語での利用申請が必要です。

① 毎日定時に決まったフォーマットでツイートする

こちらは、手動でツイートしていたので、今回は割愛します。

② 自身のタイムラインの全ツイートを可能な限り早くライクする

こちらは、twitterFavメソッドにて実装しました。

masanao.js
asyncfunctiontwitterFav(){consttweetsCount=10constfavouritesCount=0constmyId=process.env.my_idtry{consttweets=awaittwitterApi.getHtimeline(client,tweetsCount)letarr=[]for(leti=0;i<tweets.length;i++){if(tweets[i].favorited!==true&&tweets[i].user.id_str!==myId){arr.push(tweets[i].id_str)}}letcheckedTweets=arr.join(',')constfavTweets=awaittwitterApi.getLookup(client,checkedTweets)for(vari=0;i<favTweets.length;i++){if(favTweets[i].favorite_count>=favouritesCount){awaittwitterApi.postFavoriting(client,favTweets[i].id_str)}}}catch(err){outLogger.warn(err)taskLogger.warn(err)}}twitterFav()
  • tweetsCount:タイムラインから取得するツイート数
  • favouritesCount:対象ツイートのライク数
  • process.env.my_id:実行者の TwitterID

上記を設定し、エンドポイントをまとめたtwitterApiファイルから適宜メソッドを呼び出しています。

まず、自身のタイムラインからtweetsCount分のツイートを取得。
繰り返し処理にて、ライク済みのツイートと、自分のツイート以外を配列に格納。
配列内のfavouritesCountで設定したライク数を超えているツイートにライクを実行。
という流れになっています。

favouritesCountがあることで、誰もライクしていないツイートには触れないといった、ややテクニカルなプレーが可能になります。

③ 自身のフォロワーで、フォロワー数の多いユーザーの、最新のツイートにライクしている全ユーザーをフォローする

こちらは、twitterBegメソッドにて実装しました。

hajime.js
constasyncFunc=(targetTweet)=>{returnnewPromise((resolve,reject)=>{request(`https://twitter.com/i/activity/favorited_popup?id=${targetTweet}`,(err,response,body)=>{resolve(body)})})}asyncfunctiontwitterBeg(){constmyId=process.env.my_id;constinfluencerId=''tweetCount=1try{consttweets=awaittwitterApi.getUtimeline(client,influencerId,tweetCount)constarr=[]for(leti=0;i<tweets.length;i++){arr.push({id:tweets[i].id_str,favorite_count:tweets[i].favorite_count})}arr.sort((a,b)=>{constcountA=a.favorite_countconstcountB=b.favorite_countif(countA>countB){return-1}elseif(countA<countB){return1}return0})consttargetTweet=arr[0].idconstbody=awaitasyncFunc(targetTweet)constjson=JSON.parse(body)constcontent=json.htmlUsersconstregex=/data-user-id=\"(\d+)\"/gletset=newSet()while((m=regex.exec(content))!==null){set.add(m[1])}constuserIds=Array.from(set)for(vari=0;i<userIds.length;i++){try{constfriendships=awaittwitterApi.getRelationship(client,myId,userIds[i])if(friendships.relationship.source.following!==true&&friendships.relationship.source.muting!==true){awaittwitterApi.postFollowing(client,userIds[i])}}catch(err){outLogger.warn(err)taskLogger.warn(err)}awaittwitterApi.sleep(5000)}}catch(err){outLogger.warn(err)taskLogger.warn(err)}}twitterBeg()
  • myId:実行者の TwitterID
  • influencerId:対象ユーザーの TwitterID
  • tweetCount:ユーザータイムラインから取得するツイート数

まず、influencerIdのユーザータイムラインから、tweetCount分のツイートを取得。
その上でツイートのIDと、ライク数を抽出し、配列に格納。ライク数順にソート。
最もライク数の多いツイートの、https://twitter.com/i/activity/favorited_popup?id=${targetTweet}にアクセスし、ライクしている全ユーザーのIDをスクレイピングで取得。(*現在、Twitter の仕様が変わってしまった様で、上記サンプルコード実行できませんでした。折を見て修正予定です。)
繰り返し処理にて、取得したユーザーの中からフォロー中、ミュート中のユーザーを省き、フォローを実行。
という流れになっています。

ミュート中のユーザーを省く意図については、以前⑤でフォロー解除したユーザーへの再フォローを防止する施策になります。

④ ③ でフォローしたユーザーの中から、フォローバックされていないユーザーのプロフィールに行き、最新のツイート 3件 にライク + ミュートする

こちらは、twitterMuteメソッドにて実装しました。

tejitak.js
asyncfunctiontwitterMute(){constuserId=process.env.my_idconstfriendsCount=1consttweetsCount=20constfavoritedlimit=3try{constfriends=awaittwitterApi.getFriendsIds(client,userId,friendsCount)consttargetIds=friends.idsfor(vari=0;i<targetIds.length;i++){try{constfriendships=awaittwitterApi.getRelationship(client,userId,targetIds[i])if(friendships.relationship.target.following!==true&&friendships.relationship.source.muting!==true){constfriendsTweets=awaittwitterApi.getFriendstimeline(client,targetIds[i],tweetsCount)letfavoritedCount=0for(varj=0;j<friendsTweets.length;j++){if(friendsTweets[j].favorited!==true&&favoritedCount<favoritedlimit){awaittwitterApi.postFavoriting(client,friendsTweets[j].id_str)awaittwitterApi.sleep(1000)favoritedCount++}}awaittwitterApi.sleep(1000)awaittwitterApi.postMute(client,targetIds[i])}}catch(err){outLogger.warn(err)taskLogger.warn(err)}awaittwitterApi.sleep(2000)}}catch(err){outLogger.warn(err)taskLogger.warn(err)}}twitterMute()
  • userId:実行者の TwitterID
  • friendsCount:取得するフォロー中のユーザー数
  • tweetsCount:ユーザータイムラインから取得するツイート数
  • favoritedlimit:ライクするツイート数

まず、実行者のフォロー中のユーザーをfriendsCount分取得。
繰り返し処理にて、フォローされておらず、未だミュートしていないユーザーを抽出。
該当ユーザーのユーザータイムラインからtweetsCount分のツイートを取得。
favoritedlimitに達するまで、未だライクしていないツイートにライクを実行。
リクエスト制限対策としてsleep関数にて実行を停止した後に、フラグ用のミュートを実行。
という流れになっています。

我ながら、ミュート大好き過ぎますね。。

⑤ 一定期間後、フォローされていない、且つミュートになっているユーザーをリムる

最後に、twitterUnfollowメソッドの実装になります。

hideyoshi.js
asyncfunctiontwitterUnfollow(){constuserId=process.env.my_idconstfriendsCount=25try{constfriends=awaittwitterApi.getFriendsIds(client,userId,friendsCount)consttargetIds=friends.idsfor(vari=0;i<targetIds.length;i++){try{constfriendships=awaittwitterApi.getRelationship(client,userId,targetIds[i])if(friendships.relationship.target.following!==true&&friendships.relationship.source.muting===true){try{awaittwitterApi.postUnfollowing(client,targetIds[i])}catch(err){outLogger.warn(err)taskLogger.warn(err)}}}catch(err){outLogger.warn(err)taskLogger.warn(err)}awaittwitterApi.sleep(3000)}}catch(err){outLogger.warn(err)taskLogger.warn(err)}}twitterUnfollow()
  • userId:実行者の TwitterID
  • friendsCount:取得するフォロー中のユーザー数

まず、実行者のフォロー中のユーザーをfriendsCount分取得。
繰り返し処理にて、フォローされておらず、既にミュート済みのユーザーを抽出。
フォロー解除を実行。
という流れになっています。

以上 ① - ⑤ をターミナルから定期実行していました。

まとめ

じきに @masumi_sugaeくんと一緒にプロダクト化してみようという話にもなったのですが、程なくして Twitter API 規制があり、頓挫してしまいました。
機会があれば調整して実用化したいとも思うので、ご興味のある方はぜひ。

また無知により、リーダブルコード信仰の代わりに、アニミズム信仰を持ち出して、ファイル名に自身や、身近な人の名前をつけていました。(ex. masanao.js)
今となっては完全に謎です。

今回、Twitter のフォロワーを増やす方法と題して、二週にわたって執筆して参りましたが、
この方法で獲得したフォロワーは、数字上の意味しか持たないことをご留意ください。
よって、FF比率をカモフラージュするために徒らなフォロー解除を繰り返すと、ほぼ100%相手もリムってきます。

結論:フォロワー数を増やしたいのであれば、インフルエンサーに絡もう

-次回 (8/10)
「Webサイト案件における初学者のジレンマ」
*次回は Noteでの記載を予定しております。

Node.jsよりODBCでOracle, Salesforceなどに1ドライバで接続

$
0
0

Connect to Oracle, Salesforce and others using ODBC drivers from Node.js

はじめに

Node.jsアプリケーションからODBCでSQL Server、Oracle、DB2、Postgres、MongoDBなどのDBや、Salesforce、Eloqua、Oracle Sales Cloud、Oracle Service CloudなどのSaaSアプリケーションに接続する必要性があるならば、DataDirect ODBCドライバを利用すると楽勝です。

この記事では、DataDirect ODBC ドライバを使用して Node.js から Oracle データベースに接続してみます。

事前準備

Node.js

DataDirect Oracle ODBC ドライバのダウンロード&インストール

1,ここからOracleデータベース用の64ビットODBCドライバをダウンロードします。

2,パッケージを展開し、インストーラーを実行してODBCドライバをインストールします。

3,ODBC Administrator(64ビット)を開き、新しいデータソースを追加するには、[追加]をクリックします。

image.png

4,ドライバに「DataDirect 8.0 Oracle Wire Protocol」を選択します。

image.png

5,ここで、ホスト名、ポート、SID/サービス名などの接続情報を入力し、Test Connectをクリックして接続の詳細を確認します。

image.png

Node.js ODBC パッケージのインストール

1,Node.jsでODBCドライバを使用するために、node-odbcパッケージをインストールします。

2,以下を実行し、パッケージをインストールします。

npm i odbc

Node.jsよりOralceに接続

1,以下はDataDirect ODBC ドライバを使用してOracle に接続し、テーブルにクエリを実行するサンプル コードです。

var db = require('odbc')()
    , cn = "Dsn=Oracle;UID=saikrishnabobba;PWD=<password>;"
//open the connection
db.open(cn, function (err) {
    if (err) {
      return console.log(err);    }
// Run a sample query
     db.query("select * from COUNTRIES", function (err, rows, moreResultSets) {

      if (err) {
        return console.log(err);      }

      console.log(rows);     });  });

2,DSN、ユーザー名、パスワード、クエリを要件に応じて変更してください。上記のコードを実行すると、テーブルのデータをコンソールに出力します。

簡単ですね!
他のRBD、SaaS、NoSQLも手順は同様です。適切なデータソース用の DataDirect ODBC ドライバで、お好みのデータソースに接続おできます。

Viewing all 9086 articles
Browse latest View live