- 投稿日:2019-06-25T23:31:16+09:00
next.jsで@hapi/joiを使う
nextjsを使い始めたはいいが、SSRで結構詰まることがありますよね。
今回はバリデーションのメジャーどころ、@hapi/joiをnextjsで利用する方法を記載しておきます。バリデーションはvalidatejsなど様々なものがありますが、
汎用性、スター数、コミュニティなどで考えると、@hapi/joiが有力候補です。joi-browserを導入する
joiは主にnodejs用に作られており、nextjsではブラウザもレンダリングできなければ利用できません。
そこで、joi-browserを利用します。まずは必要なライブラリを導入します。
$ yarn add @hapi/joi joi-browser
nodejs側では @hapi/joi、 front側では joi-browserを参照するためにwebpackの設定を変更します。
next.config.jsmodule.exports = { webpack: (config, { isServer }) => { if(isServer) { config.resolve.alias["@hapi/joi"] = "joi-browser" } return config } }こうすることで、フロント側でも同じ名前でインポートすることができます。
import * as Joi from '@hapi/joi' // define your schema const schema = {...} const inputs = {...} Joi.validate(inputs, schema)フロントの場合は、nodejsのcoreモジュールを利用するバリデーションが使えなかったりするので、お気をつけください。
- 投稿日:2019-06-25T21:50:07+09:00
node.jsで文字列のバイト数を簡単に把握する関数
- 投稿日:2019-06-25T21:27:50+09:00
[初心者向け]VueとExpressを使ってパラメーターの受け渡しをしてみよう
はじめまして、PMをやっているtatsukenと申します。はじめまして
研修の一環でvue.js、expressを書くことがあったので、そのことを中心にまとめていきたいと思います.はじめに
Vue.jsでaxiosを使ってパラメーターをpostしexpressで作った、apiで受取るというところまでやりたいと思います。
必要な環境
Vue.jsの実装
- まずこちらの記事を参考にしてサーバーを立ち上げてください。
- http://localhost:8080にアクセスして、このような画面が表示されれば成功です。
![]()
- 次にaxiosをインストール
npm install axios -s
画面を作る
- 次に
src/components/HelloWorld.vue
をいかのように書き換えてくださいHelloWorld.vue<template> <div class="hello"> <form action> <input type="text" placeholder="text" v-model="text"> <input type="submit" value="decide" @click="submitClick"> </form> </div> </template>HelloWorld.vueの
</template>
以下に<script>
を追加HelloWorld.vue<script> import Axios from "axios"; export default { name: "HelloWorld", data() { return { text: null }; }, methods: { submitClick() { alert(this.text) } } }; </script>このようにinputに文字を入力してその文字がalertに表示されれば成功です
<script></script>
を書き換え次に
<script></script>
を以下のように書き換えてくださいHelloWorld.vue<script> import Axios from "axios"; export default { name: "HelloWorld", data() { return { text: null }; }, methods: { submitClick() { const body = { text: this.text }; Axios.get(`http://localhost:3000/hoge/${this.text}`) .then(res => { console.log(res.data.name) }) .catch(err => { console.log(err); }); } } }; </script>Expressの実装
- まずこちらの記事を参考にサーバーを立ち上げてください
- http://localhost:3000/にアクセスして以下のようになれば成功です
![]()
簡単なapiを作ってみる
アプリ名/app.js
に以下のことを追記しましょうapp.jsapp.get('/hoge', (req, res) => { res.json({ name: "hoge" }) });
- そしてhttp://localhost:3000/hogeにアクセスして以下のように表示されれば成功です。
apiを書き換える
app.jsapp.get('/hoge/:name', (req, res) => { let data = req.params console.log(data.name); res.json({ name: data.name }) });これでコードを書く部分はすべて終了です。
動かしてみる
http://localhost:8080にアクセスし任意の文字を入力し送信してください。
最後に
初心者向けにVue.jsからExpressのapiへのパラメーターの受け渡しを行いました。
これから始める方の手助けになれば幸いです。
なにか間違いがあればご連絡ください。
- 投稿日:2019-06-25T19:03:02+09:00
macosx の node 環境がぐちゃぐちゃになったので再インストール
備忘録で。
二つのプロジェクトでnodeを使っているのですが、'npm start'がよくわからないエラーで走らない! (先日何か brewで更新したような・・) どうも小手先の修正で直せそうなエラーではないので nodeごと再インストールしました。 (最悪 macosxのクリーンインストールまで考えたけど、macはnodeでできてるわけじゃないからそこまで必要ないだろうと)
# 一応やってみるけどこれだけでは不足だった brew uninstall --ignore-dependencies node # そもそも /usr/localより下を rootにならずに書き換えられるようにしておく必要あり。私のはそうなっていなかった。 sudo chown -R your_login_name:wheel /usr/local # なんかunlinkできないとかlinkできないとかいわれるのでみつけたnode関連のフォルダは消す! sudo rm -rf /usr/local/share/doc/node sudo rm -rf /usr/local/include/node sudo rm -rf /usr/local/Cellar/node/ #これでやっとクリーンインストールが通りました。 brew install node
- 投稿日:2019-06-25T18:13:09+09:00
[node.js]Expressでパスワードのハッシュ化を行おう
はじめまして、PMをやっているtatsukenと申します。はじめまして
研修の一環でvue.js、expressを書くことがあったので、そのことを中心にまとめていきたいと思いますはじめに
パスワードをデータベースに保存するときにはその情報をハッシュ化する必要があります。
なぜパスワードをハッシュ化しないといけないかなどを詳しく知りたい方はこちらを参考にしてみてください。
今回はbcryptというライブラリを使ってデータのハッシュ化を行って行きたいと思います。実装
インストール
npm install bcrypt -s
パスワードのハッシュ化
まずExpress内の自分がデータをハッシュ化したいファイル内で以下のように記述してください
hash.jsconst bcrypt = require('bcrypt'); const password = "hoge" let hashed_password = bcrypt.hashSync(data, 10); console.log(hash_data)
"hoge"
という文字列がランダムな文字に変換されています。ハッシュ化されたパスワードの照合
ハッシュ化されたパスワードと自分の知っているパスワードの照合を行っていきます
hash_confirm.jsconst bcrypt = require('bcrypt'); bcrypt.compareSync(hash_password, "hoge") // =>ture bcrypt.compareSync(hash_password, "fake_hoge") // =>false
hash_data
はもともと"hoge"
をハッシュ化したものなので、bcrypt.compareSync(hash_data, "hoge")
はtrueとなります。bcrypt.compareSync(hash_data, "fake_hoge")
はハッシュ化される前のデータと比較されているデータ(今回は"hoge"
とface_hoge
を比較している)が異なるのでfalseとなる。最後に
今回は非常に簡単なhash化の方法をご紹介しました。
もっと詳しいことが知りたい場合はこちらを参照ください。callbackやasync/awaitでエラーハンドリングを行うことも出来ます。
なにか間違いなどありましたら指摘していただけると幸いです。
- 投稿日:2019-06-25T18:13:09+09:00
[node.js]Expressでデータのハッシュ化を行おう
はじめまして、PMをやっているtatsukenと申します。はじめまして
研修の一環でvue.js、expressを書くことがあったので、そのことを中心にまとめていきたいと思いますはじめに
パスワードなどの重要な情報をデータベースに保存するときにはその情報をハッシュ化する必要があります。
なぜ重要な情報をハッシュ化しないといけないかなどを詳しく知りたい方はこちらを参考にしてみてください。
今回はbcryptというライブラリを使ってデータのハッシュ化を行って行きたいと思います。実装
インストール
npm install bcrypt -s
データのハッシュ化
まずExpress内の自分がデータをハッシュ化したいファイル内で以下のように記述してください
hash.jsconst bcrypt = require('bcrypt'); const data = "hoge" let hash_data = bcrypt.hashSync(data, 10); console.log(hash_data)
"hoge"
という文字列がランダムな文字に変換されています。ハッシュ化されたデータの照合
ハッシュ化されたデータと自分の知っているデータの照合を行っていきます
hash_confirm.jsconst bcrypt = require('bcrypt'); bcrypt.compareSync(hash_data, "hoge") // =>ture bcrypt.compareSync(hash_data, "fake_hoge") // =>false
hash_data
はもともと"hoge"
をハッシュ化したものなので、bcrypt.compareSync(hash_data, "hoge")
はtrueとなります。bcrypt.compareSync(hash_data, "fake_hoge")
はハッシュ化される前のデータと比較されているデータ(今回は"hoge"
とface_hoge
を比較している)が異なるのでfalseとなる。最後に
今回は非常に簡単なhash化の方法をご紹介しました。
もっと詳しいことが知りたい場合はこちらを参照ください。callbackやasync/awaitでエラーハンドリングを行うことも出来ます。
なにか間違いなどありましたら指摘していただけると幸いです。
- 投稿日:2019-06-25T17:55:20+09:00
nodejsでWeb予約サイトを構築した備忘録
仕事でイベント予約のためにWebサイトを構築したので備忘録。
予約フロー自体はいたってシンプルに特に工夫した点はない。
単純にnodejsでの構築手順。要件
- Linux上に構築。
- nodeはインストール済みである。
- mySQLをインストール済みである。
nodeプロジェクト生成
任意な場所で以下のコマンドをたたく。
express --view=pug [プロジェクト名]プロジェクト名でフォルダが作られてnodejsプロジェクトのひな形ファイルが出来上がる。
次に必要なパッケージをインストール。npm installこの時点で既に実行ができる。
npm startこれで起動してブラウザで起動ポートにアクセスするとダミー画面が表示される。
起動するポートは以下で定義。bin/www#!/usr/bin/env node /** * Module dependencies. */ var app = require('../app'); var debug = require('debug')('myqpp:server'); var http = require('http'); /** * Get port from environment and store in Express. */ var port = normalizePort(process.env.PORT || '8089'); app.set('port', port); /** * Create HTTP server. */ var server = http.createServer(app); (以下略)の「process.env.PORT」がそうだ。
後は目的に合わせて修正していく。
プロジェクトの構造
├── app.js ├── bin │ └── www ├── node_modules │ ├── package.json ├── public │ ├── images │ ├── javascripts │ └── stylesheets │ └── style.css ├── routes │ ├── index.js │ └── users.js └── views ├── error.pug ├── index.pug └── layout.pug
リソース名 内容 app.js アプリケーション定義 bin/www npm start で実行されるスクリプト。ルーティングなどを定義する node_modules npm installでインストールされる依存パッケージ群 package.json 使用する依存パッケージとバージョンの定義 public サイトリソース。 publicフォルダがドキュメントルートとなる。 routes バックエンドスクリプト。app.jsで宣言したURIにアクセスした時の挙動を記述する。 views HTMLテンプレート。routesスクリプトからこの中のテンプレートを指定してレンダリング指示する。好みに合わせて pug/ejs/jade の形式から選べる。 ここからは実装内容を備忘録
app.js
実装した点は以下。
1. ルーティング
2. HTMLエンジンをejsに変更。app.jsvar createError = require('http-errors'); var express = require('express'); var path = require('path'); var cookieParser = require('cookie-parser'); var logger = require('morgan'); var indexRouter = require('./routes/index'); var fkdeventRouter = require('./routes/fkdevent'); var workshopRouter = require('./routes/workshop'); var resultRouter = require('./routes/result'); var checkinRouter = require('./routes/checkin'); var manageRouter = require('./routes/manage'); var app = express(); // view engine setup app.set('views', path.join(__dirname, 'views')); -app.set('view engine', 'jade'); +app.set('view engine', 'ejs'); app.use(logger('dev')); app.use(express.json()); app.use(express.urlencoded({ extended: false })); app.use(cookieParser()); app.use(express.static(path.join(__dirname, 'public'))); //ルーティング +app.use('/workshop', workshopRouter); +app.use('/yoyaku-result', resultRouter); +app.use('/yoyaku-checkin', checkinRouter); +app.use('/yoyaku-manage', manageRouter); // catch 404 and forward to error handler app.use(function(req, res, next) { res.status(err.status || 404); res.render('err404', { error: err }); }); // error handler app.use(function(err, req, res, next) { res.status(err.status || 500); res.render('err500', { error: err }); }); module.exports = app;bin/www
実装した点は以下。
1. ルーティング
2. HTMLエンジンをejsに変更。
3. https有効。
4. ポートを外部定義化。bin/www#!/usr/bin/env node /** * Module dependencies. */ var app = require('../app'); +var define = require('../define'); var debug = require('debug')('reserve-app:server'); +var https = require('https'); var fs = require('fs'); +var ssl_server_key = '/etc/letsencrypt/live/fan-technology.com/privkey.pem'; +var ssl_server_crt = '/etc/letsencrypt/live/fan-technology.com/cert.pem'; +var options = { + key: fs.readFileSync(ssl_server_key), + cert: fs.readFileSync(ssl_server_crt) +}; /** * Get port from environment and store in Express. */ -var port = normalizePort(process.env.PORT || '3000'); +var port = normalizePort(process.env.PORT || define.PORT); app.set('port', port); /** * Create HTTP server. */ var server = https.createServer(options, app); (以下略)define.js
以下の形で宣言すると外部から読み込んで参照できる。
統一したい部分を外出しする。module.exports = Object.freeze({ PORT: 8089 });public以下
特にnodeだからでやることはない。いつも通り実装していく。
routes
実装したのは以下。
1. 画面ごとにmysqlと連携させる。
2. HTMLテンプレートを指定して画面表示。
3. メールを飛ばす。以下要点だけ記載。
API宣言
GETならこう
router.get('/', function(req, res, next) { ・・・ }POSTならこう
router.post('/', function(req, res, next) { ・・・ { 最終結果の返却 res.end(); } とりあえず200を返却 res.status(200); }※注意点はPOSTの場合は非同期で最終結果を返す場合でも、いったん同期的には200を返して
非同期の最終結果でres.end()でクローズする。mysqlとの連携
dbコネクション
var connection = mysql.createConnection({ host : 'localhost', user : 'xxxxxxx', password : 'yyyyyyy', database : 'zzzzzzz' }); connection.connect(function(err) { if (err) { console.error('error connecting: ' + err.stack); return; } console.log('connected as id ' + connection.threadId); }); ・・・ここで処理を行う。 用が済んだらコネクション切断 connection.end(function() {});select
connection.query({ sql: 'select * from table', timeout: 40000, // 40s }, function (error, rows, fields) { value = rows[0]['value']+1; });update
connection.query({ sql: 'update table set columns = true where x=?', timeout: 40000, // 40s values: [req.body.hash] }, function (error, rows, fields) { if(error) { console.log(error); } else { console.log('done.'); } });insert
var data = { column1: value1, column2: value2 }; connection.query("insert into table set ?", data, function(error,results,fields) { connection.end(function() {}); if(error) { console.lo } }db処理の同期
クエリーが非同期で実行されるので複数の実行終了後に行いたい場合はPromiseで。
var dbTask1 = new Promise(function(resolve, reject) { connection.query({ sql: 'select * from table1', timeout: 40000, // 40s }, function (error, rows, fields) { value1 = rows[0]['value']+1; resolve(); }); }); var dbTask2 = new Promise(function(resolve, reject) { connection.query({ sql: 'select * from table2', timeout: 40000, // 40s }, function (error, rows, fields) { value2 = rows[0]['value']+1; resolve(); }); }); Promise.all([dbTask1, dbTask2]).then(function () { ここに実行する処理をかく。 }画面レンダリング
必要な情報をjsonで渡してHTMLテンプレートを指定する。
var data = { data1: value1, data2: value2 }; res.render('[テンプレートファイル名].ejs', data);テンプレートの中身
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=device-width,initial-scale=1"> <meta property="og:url" content="https://www.your-domain.com/your-page.html" /> <meta property="og:type" content="website" /> <meta property="og:title" content="Your Website Title" /> <meta property="og:description" content="Your description" /> <meta property="og:image" content="https://www.your-domain.com/path/image.jpg" /> <link rel="icon" href="/images/favicon.ico" /> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"> <link rel="stylesheet" href="/css/common.css" crossorigin="anonymous"> <link href="https://cdnjs.cloudflare.com/ajax/libs/lightbox2/2.7.1/css/lightbox.css" rel="stylesheet"> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script> <script src="/js/lib/jquery.js"></script> <script src="/js/index.js" type="text/javascript"></script> <title>イベントサイトトップぺージ</title> </head> <body> <input type="hidden" id="fkdeveurl" value="<%= fkdeveurl %>" /> <input type="hidden" id="workshopurl" value="<%= workshopurl %>" /> <div id="fb-root"></div> <script async defer crossorigin="anonymous" src="https://connect.facebook.net/ja_JP/sdk.js#xfbml=1&version=v3.2"></script> </body> </html>テンプレート上で置き換えるのは以下のようにかく。
<input type="hidden" id="fkdeveurl" value="<%= fkdeveurl %>" />if文
<% if(item2.reserve_id>0) { %> <td style="min-width: 350px;"><span class="<%= item2.checkin %>"><a href="<%= item2.url %>" target="_blank"><%= item2.p_name %>(<%= item2.p_furi %>)様<br /><%= item2.c_name %>(<%= item2.c_furi %>)さん(<%= item2.gakunen %>)</a></span> </td> <% } else { %> <td class="none">(空き)</td> <% } %>for文
<% cad_time.forEach(function (item, key) { %> <label class="control control--radio wd100"> <span class="<%= item.attr %> time-label"><%- item.label %></span> <span class="zanseki"><%= item.zanseki %></span> <input type="radio" name="class_time" value="<%= item.value %>" <%= item.attr %> /> <div class="control__indicator <%= item.attr %>"></div> </label> <% }); %>javascript側で画面遷移
routesのバックエンドAPIの戻り価でURLが含まれたJSONを返す。
JavaScript側でURLを変える。バックエンド側最終的に返すもののみ。 res.json({status: 0, next_url: nexturl}); res.end();javascript側$.ajax({ type:"post", url:"/fukudaya-event", data:JSON.stringify(data), contentType: 'application/json', dataType: "json", success: function(json_data) { location.href=json_data.next_url; }, error: function() { }, complete: function() { } });または画面レンダー時にhiddenにあらかじめ隠しセットしておきJavaScriptで取得してそのまま遷移させる方法もとれる。これだと通信が1往復減らせる。
メール送信
//メールテンプレートを開く var mailtemplate = fs.readFileSync(require("app-root-path")+"/data/mail.txt", {encoding: "utf-8"}); var nexturl = '/yoyaku-result?key='+hash; let time = class_time.filter(function(item, index){if (item.value == req.body.class_time) return true;})[0]; var mailbody = mailtemplate.replace("$p_name$", req.body.p_name) .replace("$p_kana$", req.body.p_kana) .replace("$c_name$", req.body.c_name) .replace("$c_kana$", req.body.c_kana) .replace("$class_name$", time.label2) .replace("$class_time$", time.label3) .replace("$url$", 'https://fan-technology.com:'+define.PORT+nexturl) //メールの内容 var mailOptions = { from: 'xxxxxxx', to: req.body.email, subject: 'cccccccc', text: mailbody }; //SMTPの接続 var smtp = mailer.createTransport({ host: 'localhost', port: 587, ssl: false, tls:{ rejectUnauthorized: false }, use_authentication: true, user: "aaaaaa", pass: "bbbbbb" }); //メールの送信 smtp.sendMail(mailOptions, function(err, res){ if(err){ console.log(err); }else{ console.log('Message sent: ' + res.message); } smtp.close(); });メールテンプレートは普通のテキストファイル。可変部分を置換して作成して送信する。
qrコード生成
var qr = require('qr-image'); var fs = require('fs'); (中略) var sha512 = crypt.createHash('sha512'); sha512.update('[シード]'); var hash = sha512.digest('hex'); var code = qr.image(url+hash, { type: 'png'}); var qrfile = require('crypto').randomBytes(8).toString('hex')+'.png'; code.pipe(fs.createWriteStream(require("app-root-path")+'/public/qr/'+qrfile));ここでは受付用の予約ごとのユニークなURLを生成するのにハッシュ生成も行っている。
QRコードに埋め込む情報ができたら生成してファイルに出力して再利用できるようにここではしている。入力バリデーション
今回はjQueryのライブラリを使ってクライアント側で行った。
jQuery Validation Plugin専用のライブラリをインクルードする。
viewsファイル側<script src="/js/lib/jquery.validate.js"></script> ・・・細かいところはリファレンスを見てほしいが、ざっくり以下の感じ。
submitするフォームに対してバリデートオブジェクトを当てはめる。
オブジェクトにはチェックする項目と内容、エラー時のメッセージ、
成功時のハンドラ、エラー時の表示処理を実装する。バリデート処理$('#mainform').validate({ errorElement: "span", errorClass: "validate-err", rules: { item: { required: true, maxlength: 50 }, ・・・ }, messages: { item: { required: "保護者のお名前を入力してください。", maxlength: "50文字以内で入力してください。" }, ・・・ }, submitHandler: function() { submitReserve(); }, invalidHandler: function(event, validator) { for(key in validator.errorMap) { console.log(key); $('#'+key).addClass('validate-err-input'); } }, errorPlacement: function (err, element) { switch(element.attr("name")) { case "item": err.insertAfter("#time-label"); break; default: element.after(err); break; } } });スマートなアラートウィンドウ
デザインとアニメーションに心打たれて、これを使った。
sweetalert専用のライブラリをインクルードする。
viewsファイル側<script src="https://unpkg.com/sweetalert/dist/sweetalert.min.js"></script> ・・・アラート表示処理成功時ダイアログ swal("受付完了", json_data.message, "success"); エラー時アラート swal("エラー発生", "申し訳ありません。時間をおいてから再度行ってください。", "error");以上です。
- 投稿日:2019-06-25T16:24:23+09:00
Vue.jsとExpressを使って画像アップロードをしてみる
はじめまして、PMをやっているtatsukenと申します。はじめまして
研修の一環でvue.js、expressを書くことがあったので、そのことを中心にまとめていきたいと思いますはじめに
Vue.jsからリクエストを投げてExpressのpublicディレクトリに画像をアップロードしてみたいとおもいます。
今回画像アップロードにはmulterというライブラリを使って行きます。実装
使用するライブラリのインストール
- axios
npm install axios -s
Vueプロジェクトの中でinstallしてください- multer
npm install multer -s
Expressプロジェクトの中でinstallしてくださいVueの実装
まず一つvueファイルを作ってください
Uplord.vue<template> <div> <form> <input type="file" id="file" v-on:change="onFileChange"> <input type="submit" value="decide" @click="submitClick"> </form> </div> </template> <script> import Axios from "axios"; export default { data() { return { imageFile: null }; }, methods: { //画像が選択されたとき呼ばれる onFileChange(e) { this.imageFile = e.target.files || e.dataTransfer.files; }, //submitされたときに呼ばれる async submitClick() { try { const formData = new FormData(); formData.append("file", this.imageFile[0]); const config = { headers: { "content-type": "multipart/form-data", } }; let res = await Axios.post("/image", formData, config); console.log(res); if (res.data.status === "error") { alert(res.data.error); } else { alert("登録完了") } } catch (error) { alert("画像の送信に失敗しました"); } } } } </script>
- ここではformDataを使って画像をpostしています。
formData.append("key",value)
を使うことでkey:valueを対応させる形で任意のデータを乗せることが出来ます。注意点
- valueにはオブジェクトなどは乗せることが出来ません。基本的にkeyとvalueを一つずつ載せてください。
- formDataに載せたvalueはStingになってしまいます。
Expressの実装
src/index.jsに以下を書いていきましょう
index.jsconst express = require('express') const multer = require('multer'); const app = express() const storage = multer.diskStorage({ // ファイルの保存先を指定(今回はsrc/public/image以下に画像を保存) destination: function (req, file, cb) { cb(null, 'src/public/image') }, // ファイル名を指定(オリジナルのファイル名を指定) filename: function (req, file, cb) { // Math.random().toString(36).slice(-9)で乱数を生成 const imageName = `${Math.random().toString(36).slice(-9)}_${Date.now()}.png` cb(null, imageName) } }) const upload = multer({ storage: storage }).single('file') app.post('/image', (req, res) => { upload(req, res, (err) => { if (err) { //アップロード失敗した場合 res.json({ status: "error", error: "fail to uplord image" }) } else { //アップロード成功した場合 res.json({ status: "sucess", // ファイル名を返す path: res.req.file.filename }) } }) });
const storage
で画像の保存先をファイル名を指定- ここではファイル名を乱数と時間を組み合わせていますがこれではユニークとは言えません。ユニークにしたい方はuserIdと時間を組み合わせるなどしてください。
/image
のエンドポイントでuplordを呼び出しています。ただアップロードするだけでしたらもっと簡単に出来ます。(詳しくはこちら)しかしここではアップロード出来たかどうかのエラーハンドリングを行いたいのでこのような形になっています。- アップロードされた画像のファイル名は
res.req.file.filename
で取得する事ができます。- 無事フロントで
res.data.status
がsucsess
になっていれば登録完了です。最後に
multerとaxiosでお手軽にVueからExpressに画像をアップロードすることが出来ました。
機会があれば是非試してみてください
なにか間違いなどありましたら教えていただけると幸いです。
- 投稿日:2019-06-25T14:22:39+09:00
[node.js] Express,Mysql2/promiseでqueryをsaync await使って簡潔に書く
はじめまして、普段はproduct managerをやっているtatsukenと申します。
研修の一環でvue.js、expressを書くことがあったので、そのことを中心にまとめていきたいと思いますはじめに
ExpressでMysqlを使用する際Mysql2をしようすることがあると思いますが、関連するテーブルが多くなれば多くなるほどqueryの中にqueryを書くと言うようにネストが深くなっていき、どんどん可読性が下がってしまいます。
そのような問題を解決するためにMysql2/promiseが用意されています。Mysql2/promise使わないずコールバックを使用する場合
sample.jsconst express = require('express') const mysql = require('mysql2'); const app = express() const db_setting = { host: 'db', user: 'hoge', password: 'hoge', database: 'hoge', } app.get('/', (req, res) => { const connection = mysql.createConnection(db_setting) connection.connect() connection.beginTransaction(function (err) { if (err) { connection.rollback(function () { throw err; }); res.json({ status: "error", error: "fail to uplord data" }) connection.end() return } const hoge1 = { name: "hoge1", } connection.query('insert into table_name set ?', hoge1, (err, rows) => { if (err) { connection.rollback(function () { throw err; }); res.json({ status: "error", error: "fail to uplord data" }) connection.end() } else { const hoge2 = { name: "hoge2" insertId: rows.insertId, } connection.query('insert into table_name set ?', hoge2, (err, rows,) => { if (err) { connection.rollback(function () { throw err; }); res.json({ status: "error", error: "fail to uplord data" }) connection.end() } else { connection.commit(function (err) { if (err) { connection.rollback(function () { throw err; }); connection.end() return } res.json({ status: "success", id: rows.insertId }) connection.end() }); } }) } }) }) });ここではコールバックを使用しているのでネストの深さやコード量のせいで可読性が下がっています。
解決策
Mysql2/promiseを使いasync awaitで書く
sample.jsconst express = require('express'); const app = express.Router(); const mysql = require('mysql2/promise'); app.get('/', (req, res) => { const hoge1 = { name: "hoge1", } let connection try { connection = await mysql.createConnection(db_setting) await connection.beginTransaction(); const [row1] = await connection.query('insert into table_name set ?', hoge1); const hoge2 = { name: "hoge2" insertId: row1.insertId, } const [row2] = await connection.query('insert into table_name set ?', hoge2); await connection.commit(); res.json({ status: "success", id: row2.insertId }); } catch (err) { await connection.rollback(); res.json({ status: "error", error: "fail to uplord data" }) } finally { connection.end() return } });このようにasync awaitを使うことで大幅にコード量を減らすことができ、ネストが深くなるという問題も解決する事が出来ました。
注意点
mysql2
をインポートするだけではasync await
を使うことは出来ません。mysql2/promise
を使う際はasync await
を使ってください。mysql2/promise
を使うとコールバックは認識されなくなります。最後に
mysql2/promise
を使うと非常に簡潔にコードを書くことが出来ます。機会があればぜひ使って見てください。
なにか間違いなどあれば、教えていただけると幸いです。
- 投稿日:2019-06-25T14:22:39+09:00
[node.js] Express,Mysql2/promiseでqueryをasync/await使って簡潔に書く
はじめまして、普段はPMをやっているtatsukenと申します。はじめまして
研修の一環でvue.js、expressを書くことがあったので、そのことを中心にまとめていきたいと思いますはじめに
ExpressでMysqlを使用する際Mysql2をしようすることがあると思いますが、関連するテーブルが多くなれば多くなるほどqueryの中にqueryを書くと言うようにネストが深くなっていき、どんどん可読性が下がってしまいます。
そのような問題を解決するためにMysql2/promiseが用意されています。Mysql2/promise使わないずコールバックを使用する場合
sample.jsconst express = require('express') const mysql = require('mysql2'); const app = express() const db_setting = { host: 'db', user: 'hoge', password: 'hoge', database: 'hoge', } app.get('/', (req, res) => { const connection = mysql.createConnection(db_setting) connection.connect() connection.beginTransaction(function (err) { if (err) { connection.rollback(function () { throw err; }); res.json({ status: "error", error: "fail to uplord data" }) connection.end() return } const hoge1 = { name: "hoge1", } connection.query('insert into table_name set ?', hoge1, (err, rows) => { if (err) { connection.rollback(function () { throw err; }); res.json({ status: "error", error: "fail to uplord data" }) connection.end() } else { const hoge2 = { name: "hoge2" insertId: rows.insertId, } connection.query('insert into table_name set ?', hoge2, (err, rows,) => { if (err) { connection.rollback(function () { throw err; }); res.json({ status: "error", error: "fail to uplord data" }) connection.end() } else { connection.commit(function (err) { if (err) { connection.rollback(function () { throw err; }); connection.end() return } res.json({ status: "success", id: rows.insertId }) connection.end() }); } }) } }) }) });ここではコールバックを使用しているのでネストの深さやコード量のせいで可読性が下がっています。
解決策
Mysql2/promiseを使いasync/awaitで書く
sample.jsconst express = require('express'); const app = express.Router(); const mysql = require('mysql2/promise'); app.get('/', (req, res) => { const hoge1 = { name: "hoge1", } let connection try { connection = await mysql.createConnection(db_setting) await connection.beginTransaction(); const [row1] = await connection.query('insert into table_name set ?', hoge1); const hoge2 = { name: "hoge2" insertId: row1.insertId, } const [row2] = await connection.query('insert into table_name set ?', hoge2); await connection.commit(); res.json({ status: "success", id: row2.insertId }); } catch (err) { await connection.rollback(); res.json({ status: "error", error: "fail to uplord data" }) } finally { connection.end() return } });このようにasync awaitを使うことで大幅にコード量を減らすことができ、ネストが深くなるという問題も解決する事が出来ました。
注意点
mysql2
をインポートするだけではasync/await
を使うことは出来ません。mysql2/promise
を使う際はasync/await
を使ってください。mysql2/promise
を使うとコールバックは認識されなくなります。最後に
mysql2/promise
を使うと非常に簡潔にコードを書くことが出来ます。機会があればぜひ使って見てください。
なにか間違いなどあれば、教えていただけると幸いです。
- 投稿日:2019-06-25T14:22:39+09:00
[node.js] Express,mysql2/promiseでqueryをasync/await使って簡潔に書く
はじめまして、普段はPMをやっているtatsukenと申します。はじめまして
研修の一環でvue.js、expressを書くことがあったので、そのことを中心にまとめていきたいと思いますはじめに
ExpressでMysqlを使用する際Mysql2をしようすることがあると思いますが、関連するテーブルが多くなれば多くなるほどqueryの中にqueryを書くと言うようにネストが深くなっていき、どんどん可読性が下がってしまいます。
そのような問題を解決するためにMysql2/promiseが用意されています。Mysql2/promise使わずコールバックを使用する場合
sample.jsconst express = require('express') const mysql = require('mysql2'); const app = express() const db_setting = { host: 'db', user: 'hoge', password: 'hoge', database: 'hoge', } app.get('/', (req, res) => { const connection = mysql.createConnection(db_setting) connection.connect() connection.beginTransaction(function (err) { if (err) { connection.rollback(function () { throw err; }); res.json({ status: "error", error: "fail to uplord data" }) connection.end() return } const hoge1 = { name: "hoge1", } connection.query('insert into table_name set ?', hoge1, (err, rows) => { if (err) { connection.rollback(function () { throw err; }); res.json({ status: "error", error: "fail to uplord data" }) connection.end() } else { const hoge2 = { name: "hoge2" insertId: rows.insertId, } connection.query('insert into table_name set ?', hoge2, (err, rows,) => { if (err) { connection.rollback(function () { throw err; }); res.json({ status: "error", error: "fail to uplord data" }) connection.end() } else { connection.commit(function (err) { if (err) { connection.rollback(function () { throw err; }); connection.end() return } res.json({ status: "success", id: rows.insertId }) connection.end() }); } }) } }) }) });ここではコールバックを使用しているのでネストの深さやコード量のせいで可読性が下がっています。
解決策
Mysql2/promiseを使いasync/awaitで書く
sample.jsconst express = require('express'); const app = express.Router(); const mysql = require('mysql2/promise'); app.get('/', (req, res) => { const hoge1 = { name: "hoge1", } let connection try { connection = await mysql.createConnection(db_setting) await connection.beginTransaction(); const [row1] = await connection.query('insert into table_name set ?', hoge1); const hoge2 = { name: "hoge2" insertId: row1.insertId, } const [row2] = await connection.query('insert into table_name set ?', hoge2); await connection.commit(); res.json({ status: "success", id: row2.insertId }); } catch (err) { await connection.rollback(); res.json({ status: "error", error: "fail to uplord data" }) } finally { connection.end() return } });このようにasync awaitを使うことで大幅にコード量を減らすことができ、ネストが深くなるという問題も解決する事が出来ました。
注意点
mysql2
をインポートするだけではasync/await
を使うことは出来ません。mysql2/promise
を使う際はasync/await
を使ってください。mysql2/promise
を使うとコールバックは認識されなくなります。最後に
mysql2/promise
を使うと非常に簡潔にコードを書くことが出来ます。機会があればぜひ使って見てください。
なにか間違いなどあれば、教えていただけると幸いです。
- 投稿日:2019-06-25T14:22:39+09:00
[node.js] Express,mysql2/promiseでasync/await使って簡潔に書く
はじめまして、普段はPMをやっているtatsukenと申します。はじめまして
研修の一環でvue.js、expressを書くことがあったので、そのことを中心にまとめていきたいと思いますはじめに
ExpressでMysqlを使用する際Mysql2をしようすることがあると思いますが、関連するテーブルが多くなれば多くなるほどqueryの中にqueryを書くと言うようにネストが深くなっていき、どんどん可読性が下がってしまいます。
そのような問題を解決するためにMysql2/promiseが用意されています。Mysql2/promise使わずコールバックを使用する場合
sample.jsconst express = require('express') const mysql = require('mysql2'); const app = express() const db_setting = { host: 'db', user: 'hoge', password: 'hoge', database: 'hoge', } app.get('/', (req, res) => { const connection = mysql.createConnection(db_setting) connection.connect() connection.beginTransaction(function (err) { if (err) { connection.rollback(function () { throw err; }); res.json({ status: "error", error: "fail to uplord data" }) connection.end() return } const hoge1 = { name: "hoge1", } connection.query('insert into table_name set ?', hoge1, (err, rows) => { if (err) { connection.rollback(function () { throw err; }); res.json({ status: "error", error: "fail to uplord data" }) connection.end() } else { const hoge2 = { name: "hoge2" insertId: rows.insertId, } connection.query('insert into table_name set ?', hoge2, (err, rows,) => { if (err) { connection.rollback(function () { throw err; }); res.json({ status: "error", error: "fail to uplord data" }) connection.end() } else { connection.commit(function (err) { if (err) { connection.rollback(function () { throw err; }); connection.end() return } res.json({ status: "success", id: rows.insertId }) connection.end() }); } }) } }) }) });ここではコールバックを使用しているのでネストの深さやコード量のせいで可読性が下がっています。
解決策
Mysql2/promiseを使いasync/awaitで書く
sample.jsconst express = require('express'); const app = express.Router(); const mysql = require('mysql2/promise'); app.get('/', (req, res) => { const hoge1 = { name: "hoge1", } let connection try { connection = await mysql.createConnection(db_setting) await connection.beginTransaction(); const [row1] = await connection.query('insert into table_name set ?', hoge1); const hoge2 = { name: "hoge2" insertId: row1.insertId, } const [row2] = await connection.query('insert into table_name set ?', hoge2); await connection.commit(); res.json({ status: "success", id: row2.insertId }); } catch (err) { await connection.rollback(); res.json({ status: "error", error: "fail to uplord data" }) } finally { connection.end() return } });このようにasync awaitを使うことで大幅にコード量を減らすことができ、ネストが深くなるという問題も解決する事が出来ました。
注意点
mysql2
をインポートするだけではasync/await
を使うことは出来ません。mysql2/promise
を使う際はasync/await
を使ってください。mysql2/promise
を使うとコールバックは認識されなくなります。最後に
mysql2/promise
を使うと非常に簡潔にコードを書くことが出来ます。機会があればぜひ使って見てください。
なにか間違いなどあれば、教えていただけると幸いです。
- 投稿日:2019-06-25T10:54:34+09:00
Node.jsでCLIツールを作る
Firebase initとかcreate-react-appとかめちゃくちゃ便利だなーと思ったのと、CLIツールってどうやって作られてるのか知らなかったため今日はcreate-react-appを参考にCLIで自分のやりたいことができるようになったのでその共有です。
create-react-appというReactの開発環境を一瞬で作れるCLIツールが有るのですがそちらのpackage.jsonを見てみます。
create-react-app
この中のパッケージを使うっているのかな(puppeteerもあるんだけどこれどこで使ってるんだろう)
meow
https://github.com/sindresorhus/meow
CLIとのやり取りをするときに便利になるツールreact-dev-utilsというものがよく使われていて、基本的なCLIとのやり取りはここで行われていそうreact-dev-utilsはlernaでモノレポ管理されているみたいCLIとのやり取りはこのcross-spawnでやり取りしてそう
node-cross-spawn
https://github.com/moxystudio/node-cross-spawn
firebase-tools
firebase init
promptからrequireしたソースコードを使用している、60行ほどのソースコードでpromptクラスとpromptOnceという関数の2つをエクスポートしている
Inquirer
https://github.com/SBoudrias/Inquirer.js/
対話型のシェルを作るためのライブラリ
- 投稿日:2019-06-25T10:54:34+09:00
Node.jsでCLIツールを作る際に参考になるコード(create-react-appとfirebase-toolsから)
Firebase initとかcreate-react-appとかめちゃくちゃ便利だなーと思ったのと、CLIツールってどうやって作られてるのか知らなかったため今日はcreate-react-appを参考にCLIで自分のやりたいことができるようになったのでその共有です。
create-react-appというReactの開発環境を一瞬で作れるCLIツールが有るのですがそちらのpackage.jsonを見てみます。
create-react-app
この中のパッケージを使うっているのかな(puppeteerもあるんだけどこれどこで使ってるんだろう)
meow
https://github.com/sindresorhus/meow
CLIとのやり取りをするときに便利になるツールreact-dev-utilsというものがよく使われていて、基本的なCLIとのやり取りはここで行われていそうreact-dev-utilsはlernaでモノレポ管理されているみたいCLIとのやり取りはこのcross-spawnでやり取りしてそう
node-cross-spawn
https://github.com/moxystudio/node-cross-spawn
firebase-tools
firebase init
promptからrequireしたソースコードを使用している、60行ほどのソースコードでpromptクラスとpromptOnceという関数の2つをエクスポートしている
Inquirer
https://github.com/SBoudrias/Inquirer.js/
対話型のシェルを作るためのライブラリ
- 投稿日:2019-06-25T09:47:31+09:00
Gulpのプラグインを書いたらPlayCanvasでの開発がめちゃくちゃ便利になった。
@yushimatenjinです。PlayCanvasのコードエディターに付属されているリンターが、JSHintという古いものでなおかつカスタマイズができなかったので、
gulp-playcanvas
playcanvas-node
playcanvas-cli
と3つのライブラリを作りました。gulpのpluginを作ったのでその知見の共有をさせていただきます。問題
PlayCanvasを使う上で気になった点が付属のコードエディターのLintが古く具体的には
- constを使うと黄色くなる
- セミコロンを入れないと、赤くエラーが出る
この状況だったので自分のエディターを使って開発をしたい欲が出てきました。作ったプラグイン
npmのパッケージとして公開いたしましたので下記のコマンドでインストールできます。
yarn add gulp-playcanvasローカル側のgulpfile.jsは
watch
をして、トランスパイルしたものをPlayCanvasにREST APIを使用してアップロードしています。gulpfile.jsファイル
const gulp = require("gulp"); const playcanvas = require("gulp-playcanvas"); const pcOptions = require("./playcanvas.json"); const pug = require("gulp-pug"); const sass = require("gulp-sass"); gulp.task("pug", () => { return gulp .src(["src/**/*.pug", "!src/**/_*.pug"]) .pipe(pug()) .pipe(gulp.dest("dist/")) .pipe(playcanvas(pcOptions)); }); gulp.task("js", () => { return gulp .src(["src/**/*.js", "!src/**/_*.js"]) .pipe(gulp.dest("dist/")) .pipe(playcanvas(pcOptions)); }); gulp.task("sass", () => { return gulp .src("src/**/*.+(scss|sass)") .pipe(sass()) .pipe(gulp.dest("dist/")) .pipe(playcanvas(pcOptions)); }); gulp.task("watch", function() { gulp.watch(["src/**/*.pug", "!src/**/_*.pug"], gulp.task("pug")); gulp.watch(["src/**/*.js", "!src/**/_*.js"], gulp.task("js")); gulp.watch("src/**/*.+(scss|sass)", gulp.task("sass")); }); gulp.task("default", gulp.parallel("watch"));パッケージのインストール後、
playcanvas.json
という設定ファイルを同じディレクトリに置き、yarn gulp
コマンドで監視対象をwatch
しておきファイルの中身に変更があった場合に同期をする形で動きます。playcanvas.json{ "accessToken": "", "scenes": [], "projectId": "", "branchId": "", "projectName": "", "remotePath": "" }動かした結果
6月に2Dゲームを作るというハンズオンをやるのですが、その資料を触ってたらHMR的な動きができたという動画です。 x4倍 pic.twitter.com/vMHhrCkHkj
— はが (@Mxcn3) 2019年5月30日Hot Reload + ローカルの
eslint + prettier
で開発ができるのでかなり快適になりました。タスクは4つの動きをします。
1. 監視
コードの変更をwatch ソースコードの変更を検知しタスクを実行
gulp.task("watch", function() { gulp.watch(["src/**/*.pug", "!src/**/_*.pug"], gulp.task("pug")); gulp.watch(["src/**/*.js", "!src/**/_*.js"], gulp.task("js")); gulp.watch("src/**/*.+(scss|sass)", gulp.task("sass")); });この部分ですね、
pug
,js
,sass
に変更があった場合にそれぞれのタスクを実行します。2. ビルド & 3. アップロード
PugやSassなどをHTML, CSSへ変換し、REST APIを使用しビルドしたコードをアップロードします
const pug = require("gulp-pug"); /* gulp.task("pug", () => { return gulp .src(["src/**/*.pug", "!src/**/_*.pug"]) .pipe(pug()) .pipe(gulp.dest("dist/")) ------------自作のプラグインを実行------------------ .pipe(playcanvas(pcOptions)); ------------自作のプラグインを実行-------------------- }); */https://github.com/yushimatenjin/gulp-playcanvas/blob/master/index.js#L1
Gulpのプラグインの中身はストリームで流れてくるファイルの情報を使用してアップロードしています。
オプションの渡し方などは、gulp-ftpを参考にしました。const PlayCanvas = require("playcanvas-node").default; const through = require("through2"); const gutil = require("gulp-util"); const path = require("path"); module.exports = options => through.obj(function(file, enc, callback) { if (file.isNull()) { return callback(null, file); } if (file.isStream()) { return cb( new gutil.PluginError("gulp-playcanvas", "Streaming not supported") ); } const playcanvas = new PlayCanvas(options); playcanvas.updateAssets( options.remotePath, path.basename(file.path), file.path ); return callback(null, file); });1行目でplaycanvas-nodeという自作のライブラリを読み込んでいるのですが、このライブラリを作ったことで、APIとのやり取りをシンプルに書けてとてもよかた、REST APIのラッパーを書いておくのはめちゃめちゃ良いという知見がたまりました。
参考リンク
PlayCanvas.jp
PlayCanvas/Engine
playcanvas-node
playcanvas-gulp
- 投稿日:2019-06-25T09:47:31+09:00
Gulpのプラグインを書いたらPlayCanvasでの開発がめちゃくちゃ便利になった
@yushimatenjinです。PlayCanvasのコードエディターに付属されているリンターが、JSHintという古いものでなおかつカスタマイズができなかったので、
gulp-playcanvas
playcanvas-node
playcanvas-cli
と3つのライブラリを作りました。gulpのpluginを作ったのでその知見の共有をさせていただきます。問題
PlayCanvasを使う上で気になった点が付属のコードエディターのLintが古く具体的には
- constを使うと黄色くなる
- セミコロンを入れないと、赤くエラーが出る
この状況だったので自分のエディターを使って開発をしたい欲が出てきました。作ったプラグイン
npmのパッケージとして公開いたしましたので下記のコマンドでインストールできます。
yarn add gulp-playcanvasローカル側のgulpfile.jsは
watch
をして、トランスパイルしたものをPlayCanvasにREST APIを使用してアップロードしています。gulpfile.jsファイル
const gulp = require("gulp"); const playcanvas = require("gulp-playcanvas"); const pcOptions = require("./playcanvas.json"); const pug = require("gulp-pug"); const sass = require("gulp-sass"); gulp.task("pug", () => { return gulp .src(["src/**/*.pug", "!src/**/_*.pug"]) .pipe(pug()) .pipe(gulp.dest("dist/")) .pipe(playcanvas(pcOptions)); }); gulp.task("js", () => { return gulp .src(["src/**/*.js", "!src/**/_*.js"]) .pipe(gulp.dest("dist/")) .pipe(playcanvas(pcOptions)); }); gulp.task("sass", () => { return gulp .src("src/**/*.+(scss|sass)") .pipe(sass()) .pipe(gulp.dest("dist/")) .pipe(playcanvas(pcOptions)); }); gulp.task("watch", function() { gulp.watch(["src/**/*.pug", "!src/**/_*.pug"], gulp.task("pug")); gulp.watch(["src/**/*.js", "!src/**/_*.js"], gulp.task("js")); gulp.watch("src/**/*.+(scss|sass)", gulp.task("sass")); }); gulp.task("default", gulp.parallel("watch"));パッケージのインストール後、
playcanvas.json
という設定ファイルを同じディレクトリに置き、yarn gulp
コマンドで監視対象をwatch
しておきファイルの中身に変更があった場合に同期をする形で動きます。playcanvas.json{ "accessToken": "", "scenes": [], "projectId": "", "branchId": "", "projectName": "", "remotePath": "" }動かした結果
6月に2Dゲームを作るというハンズオンをやるのですが、その資料を触ってたらHMR的な動きができたという動画です。 x4倍 pic.twitter.com/vMHhrCkHkj
— はが (@Mxcn3) 2019年5月30日Hot Reload + ローカルの
eslint + prettier
で開発ができるのでかなり快適になりました。タスクは4つの動きをします。
1. 監視
コードの変更をwatch ソースコードの変更を検知しタスクを実行
gulp.task("watch", function() { gulp.watch(["src/**/*.pug", "!src/**/_*.pug"], gulp.task("pug")); gulp.watch(["src/**/*.js", "!src/**/_*.js"], gulp.task("js")); gulp.watch("src/**/*.+(scss|sass)", gulp.task("sass")); });この部分ですね、
pug
,js
,sass
に変更があった場合にそれぞれのタスクを実行します。2. ビルド & 3. アップロード
PugやSassなどをHTML, CSSへ変換し、REST APIを使用しビルドしたコードをアップロードします
const pug = require("gulp-pug"); /* gulp.task("pug", () => { return gulp .src(["src/**/*.pug", "!src/**/_*.pug"]) .pipe(pug()) .pipe(gulp.dest("dist/")) ------------自作のプラグインを実行------------------ .pipe(playcanvas(pcOptions)); ------------自作のプラグインを実行-------------------- }); */https://github.com/yushimatenjin/gulp-playcanvas/blob/master/index.js#L1
Gulpのプラグインの中身はストリームで流れてくるファイルの情報を使用してアップロードしています。
オプションの渡し方などは、gulp-ftpを参考にしました。const PlayCanvas = require("playcanvas-node").default; const through = require("through2"); const gutil = require("gulp-util"); const path = require("path"); module.exports = options => through.obj(function(file, enc, callback) { if (file.isNull()) { return callback(null, file); } if (file.isStream()) { return cb( new gutil.PluginError("gulp-playcanvas", "Streaming not supported") ); } const playcanvas = new PlayCanvas(options); playcanvas.updateAssets( options.remotePath, path.basename(file.path), file.path ); return callback(null, file); });1行目でplaycanvas-nodeという自作のライブラリを読み込んでいるのですが、このライブラリを作ったことで、APIとのやり取りをシンプルに書けてとてもよかた、REST APIのラッパーを書いておくのはめちゃめちゃ良いという知見がたまりました。
参考リンク
PlayCanvas.jp
PlayCanvas/Engine
playcanvas-node
playcanvas-gulp
- 投稿日:2019-06-25T08:56:07+09:00
WebSocket の検証では wscat が便利だった
WebSocket の動作確認に wscat が便利すぎる件
WebSocketに対応したCloudFrontをwscatで疎通確認してみた
既に記事がありましたが、自分メモ。
どんな時に使う?
WebSocket の接続試験を行う時に、いちいちサーバー・クライアント側でプログラムを書くの面倒だなーと思っていたら、調べてみた所
wscat
という便利な npm があったので使ってみました。インストール方法
Node.js が入っていれば
npm
コマンドでインストール可能。$npm install -g wscatNode.js がまだ入っていない場合には各 OS などに併せてインストールしておいて下さい
サーバー
-l
オプションを指定してコマンドを実行することで指定したポートで WebSocket を受け付ける(listen)事ができます。
以下は 8000番ポートで受付を行う際の例$wscat -l 8000クライアント
サーバー側が HTTP の場合、
ws
スキームを使い、HTTPS の場合にはwss
スキームを利用する必要があります。Difference between ws and wss?
# HTTP $wscat -c ws://example.com:8000 # HTTPS $wscat -c wss://example.com:443
- 投稿日:2019-06-25T02:06:43+09:00
【初心者向け】NPM・package.jsonを理解する
概要
npm を初めて扱うときは、パッケージをどうやってインストール・インポートするのか、package.json がどういう役割をもっているのかなど分からないことだらけであり、筆者も少しずつ調べては試すことを繰り返した記憶がある。これから Node.js を学ぼうという人には npm の使い方でつまづいてほしくないため、この記事では npm を使う上で必要な概念的知識を説明する。この記事をすべて読めばスムーズに開発が始められると思われる。
NPM とは
NPM と名のつくものは実は 2 つあり、ひとつはオンライン上のパッケージレジストリ、つまり世界中の開発者が作った Node.js のパッケージが集められた場所である。もう一つは Node.js に付属している、パッケージを操作するための CLI(コマンドラインインターフェイス; コマンドラインから実行できるプログラム)である。
以降、曖昧さを回避するためにパッケージレジストリの方を大文字のNPM、CLI の方を小文字のnpmで表記する。
NPM(レジストリ)
NPM は現時点で他の言語のものを含め世界最大のパッケージレジストリであり、ブラウザ用のライブラリ、Node.js 用のライブラリを含めて多種多様のものがある。NPM に登録されているパッケージは公式サイトでブラウジング、検索できる。
パッケージを利用する点で注意すべきなのは、誰でもパッケージを公開できるため安全であるという保証はないということである1。特に業務レベルでパッケージを使う際には、その dependency(そのパッケージが使用しているまた別のパッケージ)を含めて本当に信頼できるかを吟味する必要がある。しかし React などのある程度高機能なライブラリになると、dependency の dependency...とたどっていくと膨大なパッケージ数となり、とても手作業でチェックできるものではないという問題がある。
Note: ちなみにほとんどのパッケージは GitHub にソースコードが載せられているが、そのコードがそのまま NPM に登録されているという保証はどこにもなく、コードをチェックしたい場合は NPM から直接ダウンロードするべきである。
npm(CLI)
npm は Node.js プロジェクトの作成、dependency の追加・削除・アップデート、スクリプトの実行、パッケージの公開などを行うことができる。
パッケージ
パッケージとはプログラムのまとまりであり、NPM で公開されているほとんどのパッケージは外から使うためのライブラリである。世の中にあるパッケージを使えば自分で一からコードを書かなくとも高度な機能を実現することができる。
パッケージを利用するためには、直接パッケージをダウンロードして自分のプロジェクトに含めればよいのではないか、さらには Git のリポジトリに含めてよいのではないかと思うかもしれない。しかし、もしそのパッケージに新しいバージョンが出て、バグ修正や機能の追加がされたとき、自分のプロジェクト内のパッケージもアップデートしたくなるかもしれない。そうすると自分のプロジェクト内のファイルを手動で更新しなければならなくなる。シェルを使えば簡単に更新できるかもしれないが、全く同じコード(正確には同期されるべきコード)が複数の場所(今の場合 NPM と自分の Git リポジトリ)で管理されるというのは無駄であり、「本当に自分のプロジェクトに含まれているパッケージは世に公開されているパッケージと内容が一致しているのか」という懸念も生じる。
よって外部のパッケージは自分のプロジェクトに含めるのではなく、「このプロジェクトは NPM のこのパッケージに依存している」、という依存情報だけを「宣言」するのがよいということに落ち着く。このような依存先のパッケージをこの界隈の言葉で dependency(depend=依存する)と呼ぶ。NPM ではパッケージは別のパッケージに依存し、そのパッケージがまた別のパッケージに依存し...と、パッケージが dependency のネットワークを成すことになる。これは Maven、pip といった他の言語のパッケージレジストリについても同じである。例えば express というパッケージの dependency tree は下の図のようになっている。
プロジェクト=パッケージ
通常 Node.js のプロジェクトは npm のパッケージ 1 個に対応する。つまり、プロジェクトを 1 つ開発するということは、公開するしないにかかわらずパッケージを 1 つ作り上げるということに相当する。よって Node.js で開発するためには、パッケージというものがどういう構造になっているのかを理解する必要がある。
npm にとってパッケージというのは
package.json
というファイルの親ディレクトリに含まれるファイル群である。例えばディレクトリ~/projects/my-project/
にpackage.json
があれば、~/projects/my-project/
がそのプロジェクトのルートディレクトリ(一番根っこのディレクトリ)となる。npm のコマンドは常にルートディレクトリで実行することになる。依存パッケージを自分のパッケージに含めないと先に述べたが、実際には依存パッケージのファイルをローカルのどこかにダウンロードする必要がある。npm でパッケージをインストールすると、それらはルートディレクトリ直下の
node_modules
ディレクトリにあくまで仮置場としてダウンロードされる。よってこのディレクトリは.gitignore
で Git の repo から除外するのが普通であり、このディレクトリ内のファイルは編集してはいけない。以上のファイル、ディレクトリ以外についてはどのようにファイルをおいてもよい(当然、使用するフレームワークなどによってはファイルの配置に制約を受けることはある)。
パッケージの作成
Note: 以降、Node.js と npm(>=v6)が正常にインストールされておりそれぞれコマンドラインで
node
、npm
という名前でアクセスできる(PATH に登録されている)ことを前提とする。プロジェクト、すなわちパッケージを一から作成するにはまず
package.json
を作成することから始まる(例えば React のようにプロジェクトを生成する CLI パッケージが用意されている場合はそれを用いればよい)。以下を実行すれば、パッケージ名などがインタラクティブに質問されすべて答えるとpackage.json
が生成される。質問をすべてスキップするには-y
オプションをつければよい。# 現在のディレクトリに package.json を生成する npm init
package.json の中身
package.json
は例えば以下のようになっている。.json
という拡張子からもわかるように、このファイルは JSON と呼ばれる形式に従っている。{ "name": "my-package", "description": "my first package ever", "license": "MIT", "version": "1.0.0", "main": "index.js", "scripts": { "start": "node index.js" }, "dependencies": { "axios": "^0.18.0" }, "devDependencies": { "eslint": "^5.14.1" } }name, version, description, license などのデータは単なるパッケージのメタデータであり、パッケージを公開するつもりがないならばあまり気にする必要はない。機能的に重要なのは main, dependencies, devDependencies, scripts であり、以下でそれぞれ説明する。
main
main はパッケージを外からインポートするときにどの JavaScript ファイルが入り口であるかを指定するものである。誰かのパッケージを外から使うときに、そのパッケージをインポートするとは具体的にどのファイルをインポートするということなのかを確認するときに見ればよい。自分のパッケージの
package.json
の main は、そのパッケージを NPM で公開しない限り重要ではない。例えば、HTTP リクエストライブラリである request というパッケージを下のようにインポートしたいとする。Node.js では外部の JavaScript ファイルをインポートするとき
require
またはimport
を使う(後者については近年新しく導入された構文でありここでは触れない)。このとき、このreq
という変数には具体的に何が入るのかを調べたいとする。const req = require('request')ここで、request をインストールしたあとに
node_modules/request/package.json
を見ると、下のように main はindex.js
となっている。つまり、
index.js
がエクスポートしたものが変数req
に入ることになる。index.js
は以下のようになっている。
module.exports
にrequest
という変数の値が代入されていることがわかる。よって先の変数req
にはこの変数の値が入ることがわかる。dependencies & devDependencies
先述の通り、dependency とは自分のパッケージが依存する別のパッケージであり、
package.json
には dependency が使用するバージョンとともに書かれる。これらに変更を加える方法は後で説明する。dependencies と devDependencies の違いであるが、意味としては前者は実行に必要なパッケージ、後者は開発やテストにのみ必要なパッケージである。機能的には、外部のパッケージを dependency としてインストールするときにデフォルトではその dependencies はインストールされるが devDependencies はインストールされないという違いがある。
例えば JavaScript の Linter である eslint は開発のときにのみ必要で実行するときには不要なため、devDep としてインストールするのが適切である。しかしどちらに分類すべきか自明でない場合もある。例えば Webpack を用いてフロントエンド(ブラウザ)アプリを開発するときは、そもそもパッケージとして外から実行できるものではないため分類しがたい。この場合は、Webpack などのビルドツールは devDep、jQuery などのアプリに含まれるべきライブラリは dep として指定するのが普通である。
自分のパッケージを NPM で公開するつもりならば、両者の違いに気をつける必要がある。公開しないとしても、どのパッケージが実行に必要なのかを意識し dep と devDep を使い分けることは、dependency を整理する上でも良い習慣だと思われる。
package.json
では次のような形式で dependency が指定される。このときバージョンの先頭にキャレット^
またはチルダ~
がついていることが多い。"dependencies": { "express": "^4.17.1", "request": "~2.88.0" },そもそもバージョンは
.
で区切られた 3 つの数字から構成されているが、これはSemver(Semantic Versioning)という規則に則っており(無論、このルールに従うのは開発者の責任である)、下の図のようにそれぞれの数字に意味がある。キャレット^
をつけると「Major は一致し Minor と Patch は指定されたもの以上」という意味になり、チルダ~
をつけると「Major と Minor は一致し Patch は指定されたもの以上」という意味になる。例えば、^4.17.1
は4.17.10
や4.20.0
にマッチするが4.16.8
や3.20.4
には一致しない。~2.88.0
は2.88.5
にマッチするが2.90.3
にはマッチしない。バージョンを固定したいときは後述の
package-lock.json
を使えばよいため、わざわざキャレットやチルダを外す必要はない。scripts
scripts は簡単に言えばコマンドのエイリアスであり、任意のコマンド(i.e. コマンドラインのコマンド)に名前をつけることができる。例えば以下のような形である。
"scripts": { "start": "node index.js", "lint": "eslint" },ここに記載された script は
npm run <name>
で実行できる。例えば上のlint
はnpm run lint
で実行できる。ただしいくつかの名前は特別扱いされ、例えば
start
は普通プログラムを実行するコマンドを指定し、npm start
で実行できる。test
はテストを実行するコマンドを普通指定し、npm test
で実行できる。また、script 名の先頭にpre
がついていると、ついてない名前の script が実行される前に自動的に実行される。例えば scriptbuild
とprebuild
が存在するとき、npm run build
を実行すると、build
の前にprebuild
が自動的に実行される。逆にpost
を先頭につけると元の名前の script の後に実行される。
preinstall
とpostinstall
はそのパッケージをインストールする前後で自動的に実行されるものであり、何らかのパッケージのインストールが失敗するときはそのパッケージのpreinstall
やpostinstall
をチェックすると解決することもある。
npm run <name>
は簡単な task runner2として使えるため、何度も実行するコマンドは script として登録すると開発を効率化できる。また、scripts はプロジェクトのテンプレートに最初から含まれていることが多い。例えば、react のプロジェクトをcreate-react-app
で生成するとstart
やbuild
といった script が用意されており、すぐに開発やビルドができるようにセットアップされている。dependency の編集
すでに
npm install
ですべての dependency をインストールした状態で、dependency を追加・削除・アップデートしたいときは普通直接package.json
を編集せず、npm を通じて行う。npm コマンドで dependency を変更すると自動的にpackage.json
にも反映される。もしpackage.json
を直接編集した場合は再度npm install
を実行してnode_modules
内のファイルを更新する必要がある。Note: npm v4 以下では dependencies として追加するときに
--save
を指定しないとpackage.json
に反映されない。dependency の追加/バージョン変更
devDep として追加する場合は
-D
(--save-dev
のエイリアス)を指定する。# dependencies に追加 npm install <package> # devDependencies に追加 npm install -D <package>バージョンを指定したい場合は
@
の後に書けば良い。# react の v16.8.6 を追加 npm install react@16.8.6 # react の最新バージョンを追加 npm install react@latestdependency の削除
npm uninstall <package>package-lock.json
これは dependency のバージョンを lock(ロック、固定)するためのファイルであり、
npm install
の実行時に自動的に作成される。一般に npm パッケージは他のチームメンバーの開発マシン、テスト環境、本番環境など複数の環境で実行されるため、すべての環境で全く同じバージョンの dependency をインストールしたいと思うのは自然である(バージョンが同じでなければ「ある環境ではうまく実行でき、ある環境ではエラーが出る」といったことが起こりうる)。
実は、
package.json
だけではこれは実現できない。キャレット^
やチルダ~
を使わなければいいのではないかと思うかもしれないが、問題はそう単純ではない。例えば自分のパッケージの dependency として"A": "1.2.0"
と指定したとしよう。パッケージ A はまた別のパッケージ B に依存しており、"B": "^2.5.0"
が指定されている。いま、B の最新が v2.8.0 とすると、npm install
すれば A の v1.2.0、B の v2.8.0 がインストールされる。しばらくして B の新バージョン v2.9.0 がリリースされたとする。いま、別のマシンで自分のパッケージをnpm install
すると A の v1.2.0 と B のv2.9.0がインストールされる。このように、同じpackage.json
でもインストールされる dependency のバージョンが異なってしまうということが起こりうる。これを解決するために npm v5 以降で
package-lock.json
が導入された。このファイルには dependency、dependency の dependency...と間接的なものも含めすべての dependency のバージョン(とその integrity)が記録される。npm v6.9.0 時点では
npm install
の具体的な挙動は次のようになっている。
package-lock.json
が存在しないとき
package.json
に基づいて dependency がインストールされ、実際にインストールされたバージョンがpackage-lock.json
に書かれる。package-lock.json
が存在するとき
package-lock.json
に基づいてインストールされるが、package.json
指定されたバージョンとの矛盾があれば、package.json
が優先され、実際にインストールされたバージョンがpackage-lock.json
に書かれる。
package-lock.json
を優先したい場合はnpm ci
を実行すればよい。このコマンドは文字通り CI(Continuous Integration)用に使われることを意図したものであり、package-lock.json
に基づいて dependency をインストールし、もしpackage.json
との矛盾があればエラーを出力する。GitHub でパッケージのコードを見るときの注意点
NPM パッケージは高確率でソースコードが GitHub でも公開されており、ローカルにインストールすることなくコードを確認したいときに便利である。パッケージのページの Repository の欄をクリックすれば該当の repo に飛ぶことができる。
先に述べたように、repo にあるコードがそのまま NPM にアップロードされているとは限らない。何らかのビルド処理をしたあと必要なファイルだけアップロードされることもよくあり、
package.json
のscripts
を見ればどのようなビルド処理をしたのか推測することもできる。パッケージの repo はしばしば NPM でのバージョンに対応した tag がつけられており、特定のバージョンのソースコードを見たいときに便利である。
- 投稿日:2019-06-25T02:04:13+09:00
v8nを使ってvue.jsでバリデーションをかけてみた
はじめまして、普段はproduct managerをやっているtatsukenと申します。
研修の一環でvue.js、expressを書くことがあったので、そのことを中心にまとめていきたいと思います何をするのか
v8nというnode moduleを使用してvue.jsの入力フォームにバリデーションをかけていきたいと思います。
vue.jsの一般的なバリデーションライブラリとしてはvee-validateなどがありますが、
v8nは簡単にバリデーションをかけたいときやバリデーションのカスタマイズを行いたいときに便利です。使い方
install(vue project)
npm install v8n -s
Sample code
sample.vue<template> <form action> <input type="text" v-model="email"> <input type="password" v-model="password"> <input type="text" v-model="email"> <input type="submit" value="decide" @click="onClickSubmitButton"> </form> </template> <script> import v8n from "v8n"; export default { data() { return { email: null, password: null }; }, methods: { onClickSubmitButton() { const message = { null: "中身が空です", minLength: "文字数は4文字以上です", pattern: "emailを入力してください", maxLength: "文字数が多すぎます" }; try { v8n() .not.null() // 値がnullじゃないか .string() // 文字列 .minLength(4) // a@b.c を想定して最低5文字 .pattern(/[^\s@]+@[^\s@]+\.[^\s@]+/) .check(this.email); } catch (ex) { let rule = ex.rule.name; alert(`Emailに関して${message[rule]}`); return; } try { v8n() .not.null() .string() .minLength(4) .check(this.password); } catch (error) { let rule = error.rule.name; alert(`passwordに関して${message[rule]}`); return; } } } }; </script>説明
- まずv8nをimport
import v8n from "v8n";
v8n()
に様々な設定を加えていくことで様々なバリデーションを作ることが出来ます。
code rule null() nullかどうかを判定する not.null() null出ないかどうかを判定する string() stringかどうかを判定する minLength(4) 4文字以上かどうかを判定する pattern(/[^\s@]+@[^\s@]+.[^\s@]+/) patternに一致しているかどうかを判定する(今回はemailのpattern maching)
- 他にも様々なruleをつけることが出来ます(詳しくはこちらにまとまっていました)
- 加えた条件に対して
.check(変数名)
を行うことで任意の変数が自分の設定したruleに一致しているか判定することができます- あとはpromiseなりtry catchなりでerrorを受け取り、エラーハンドリングを行ってください
- errorの場合どのruleによってerrorになったかは
error.rule.name;
などで受け取ることが出来ます。参考にしたもの
最後に
ざっくりとv8nの使い方について説明しました。
初めての投稿なので間違いなどあれば指摘していただけると幸いです。
- 投稿日:2019-06-25T02:04:13+09:00
v8nを使ってVue.jsでバリデーションをかけてみた
はじめまして、普段はproduct managerをやっているtatsukenと申します。
研修の一環でvue.js、expressを書くことがあったので、そのことを中心にまとめていきたいと思います何をするのか
v8nというnode moduleを使用してvue.jsの入力フォームにバリデーションをかけていきたいと思います。
vue.jsの一般的なバリデーションライブラリとしてはvee-validateなどがありますが、
v8nは簡単にバリデーションをかけたいときやバリデーションのカスタマイズを行いたいときに便利です。使い方
install(vue project)
npm install v8n -s
Sample code
sample.vue<template> <form action> <input type="text" v-model="email"> <input type="password" v-model="password"> <input type="text" v-model="email"> <input type="submit" value="decide" @click="onClickSubmitButton"> </form> </template> <script> import v8n from "v8n"; export default { data() { return { email: null, password: null }; }, methods: { onClickSubmitButton() { const message = { null: "中身が空です", minLength: "文字数は4文字以上です", pattern: "emailを入力してください", maxLength: "文字数が多すぎます" }; try { v8n() .not.null() // 値がnullじゃないか .string() // 文字列 .minLength(4) // a@b.c を想定して最低5文字 .pattern(/[^\s@]+@[^\s@]+\.[^\s@]+/) .check(this.email); } catch (ex) { let rule = ex.rule.name; alert(`Emailに関して${message[rule]}`); return; } try { v8n() .not.null() .string() .minLength(4) .check(this.password); } catch (error) { let rule = error.rule.name; alert(`passwordに関して${message[rule]}`); return; } } } }; </script>説明
- まずv8nをimport
import v8n from "v8n";
v8n()
に様々な設定を加えていくことで様々なバリデーションを作ることが出来ます。
code rule null() nullかどうかを判定する not.null() null出ないかどうかを判定する string() stringかどうかを判定する minLength(4) 4文字以上かどうかを判定する pattern(/[^\s@]+@[^\s@]+.[^\s@]+/) patternに一致しているかどうかを判定する(今回はemailのpattern maching)
- 他にも様々なruleをつけることが出来ます(詳しくはこちらにまとまっていました)
- 加えた条件に対して
.check(変数名)
を行うことで任意の変数が自分の設定したruleに一致しているか判定することができます- あとはpromiseなりtry catchなりでerrorを受け取り、エラーハンドリングを行ってください
- errorの場合どのruleによってerrorになったかは
error.rule.name;
などで受け取ることが出来ます。参考にしたもの
最後に
ざっくりとv8nの使い方について説明しました。
初めての投稿なので間違いなどあれば指摘していただけると幸いです。
- 投稿日:2019-06-25T00:59:58+09:00
サービス終了後もガルラジ公式アプリを使うためのツールを作った
はじめに
ガルラジ公式アプリが2019年6月いっぱいでサービス終了を迎えます.アプリで配信されている「つぶやき」を保存しサービス終了後も閲覧するためのツールを作りました.
https://github.com/sienori/Garuradi-cache-serverガルラジとは
ガールズラジオデイズ,略してガルラジというコンテンツをご存知でしょうか.2018年12月から翌3月にかけてファーストシーズンが展開された,2次元のキャラクターたちによるラジオコンテンツです.
ラジオという媒体だからこそ醸し出されるキャラクターの実存感や,虚構と現実の境目がグラつくような不思議な感覚に魅了され,これまでも多くのファンによって怪文書が投稿されるなど大きな盛り上がりを見せてきました.
- ガルラジ-「ガールズ ラジオ デイズ」が面白い
- 声優ラジオが好きなオタクへ キャラクターによるラジオ「ガルラジ」を聴いてみませんか? #2019年はガルラジが来る
- ガルラジを今すぐ追いかけろ
- 『ガールズ ラジオ デイズ』––––周波数を合わせて
- 『ガールズ ラジオ デイズ(ガルラジ)』という異世界ラジオめいた奇妙なキャラクターラジオコンテンツを君は聞いたか
2019年7月からはセカンドシーズンの展開を控えており,今最も熱いコンテンツといえます. #2019年はガルラジ!
さて,ガルラジへの没入感を高めるための要素として,ラジオ本編の他にも公式アプリで配信されていたキャラクターたちの「つぶやき」というものがありました.
ラジオ外でのキャラクター達の会話がリアルタイムで更新されていく魅力的なコンテンツでしたが,コンテンツ展開の移動に伴い公式アプリのサービスは2019年7月で終了することが発表されています.
サービス終了後もつぶやきを見返したい!ということで,つぶやきを保存しサービス終了後もアプリで閲覧するためのツールを作りました.
仕組み
Charlesという通信のモニタリングや通信内容の上書きができるソフトがあります.これを使うと特定のアドレス宛の通信を自前のローカルサーバに送ることができます.アプリとAPIサーバの間にローカルサーバを経由させることでリクエストとレスポンスの内容を保存し,オフラインでも利用できるようにします.
Charlesでローカルサーバを経由させてリクエストとレスポンスを保存します.
一度保存した通信内容はキャッシュから取り出してアプリに渡すことで,サービスが終了したあとでも閲覧できるようにします.
そんなことして大丈夫なの?と思われるかもしれませんが,通信内容の保存は私的利用の範囲内でローカルにのみ行っており,サーバに対しても通常通りのリクエストしかしていないので問題はないと考えています.
導入
1. Androidエミュレータのインストール
ガルラジ公式アプリを動かすためのAndroidが必要です.
前述のCharlesを使えばスマホ実機の通信をキャプチャすることも出来るのですが,Android7以降の端末ではSSLの通信を覗けなくなりました.
参考: Android7からcharlesでssl通信が見れなくなった件この記事ではGenymotionというAndroidエミュレータを使用します.Genymotionをインストールし,Android6の端末をインストールしてください.
インストールした端末にガルラジ公式アプリをどうにかしてインストールしてください(Playストアでの配信はすでに終了しています).
2. Charlesのインストール
こちらの記事などを参考にCharlesをインストールし,セットアップします.
【開発支援ツール】Charlesの使い方【神ツール】ここで重要となるのがSSL証明書の設定です.
上記の記事を参考にPCとAndroidの両方に証明書をインストールしてください.3. ツールのダウンロード
https://github.com/sienori/Garuradi-cache-server
Githubから任意の場所にクローンしてください.
ツールの実行にはNode.jsが必要です.git clone https://github.com/sienori/Garuradi-cache-server.git cd Garuradi-cache-server npm install4. 追加の設定
ここまでのセットアップを終えると,AndroidとPC上の通信がCharlesでキャプチャされるようになっているはずです.
以下の設定を追加で行います.Recording Settings
関係のない通信を記録しないための設定です.
Proxy
>Recording Settings
を開き,Include
タブのAdd
ボタンから2つのロケーションを追加します.Map Remote
app.garuradi.jp
への通信をローカルサーバへ流すための設定です.
Tools
>Map Remote
を開き,Enable Map Remote
を有効にしてAdd
ボタンからマッピングを追加します.Map From:
https://app.garuradi.jp
Map To:http://localhost:3000
使い方
次のコマンドでツールを起動します.
npm run startアプリを起動し,つぶやきを読み込んだりするとコマンドプロンプトに次のようなログが表示されるはずです.
/login Request to server Save cache /login 0001 /term/timeline Request to server Save cache /term/timeline 0001 /term/timeline Response from cache /term/timeline 0001これらのログはAPIへのリクエストが行われるたびに行われる処理の内容を表しています.
つぶやきを読み込むとAPIサーバへリクエストが送信され(Request to server
),返ってきたレスポンスをキャッシュとしてローカルに保存します(Save cache
).
一度保存された内容は,2回目以降はキャッシュから読み出されます(Response from cache
).すべてのつぶやきを読み込んでローカルに保存しておけば,サービスが終了したあともアプリを通してつぶやきを閲覧することができるはずです.
既知の問題
読み込みを繰り返していると,通信エラーと表示されることがあります.
多くの場合は何度かリトライすると読み込めるようになりますが,ログに302 'https://app.garuradi.jp/login/error'
と表示されている場合はアプリの再起動が必要です.まとめ
ガルラジ公式アプリの「つぶやき」を保存するツールを作りました.サービス終了までに保存しておけば,いつでもつぶやきを見返すことができるはずです.
https://github.com/sienori/Garuradi-cache-server
ご利用の際は自己責任でお願いします.
- 投稿日:2019-06-25T00:44:42+09:00
sequelizeでモデルからENUMの値を取得する方法 [Node.js + Express]
はじめに
Node.js + Expressを使って開発を行った際に、sequelizeを使ってモデル内のカラムからENUMの値を取得した時のやり方をメモしておきます。
モデルの定義確認
以下のように簡易モデルを定義したとします。
sequelize.define('model', { animals: { type: Sequelize.ENUM, values: ['dog', 'cat', 'bird'] } })取得方法
Model.rawAttributes.states.values
でENUMの値を取得できます。var Model = sequelize.define('model', { states: { type: Sequelize.ENUM, values: ['dog', 'cat', 'bird'] } }); console.log(Model.rawAttributes.states.values); // logs ['dog', 'cat', 'bird'] in consoleカラムからENUMの値を取得する場合は、
model.column.rawAttributes.states.values
などのようにすると同様に取得可能です。