20210914のNode.jsに関する記事は7件です。

WindowsなどからLinuxサーバのログを気軽に参照するサーバ

UbuntuなどのLinuxサーバにあるログを参照するためにわざわざSSHでログインするのが面倒なので、ブラウザから参照できるようにした。 適当につくったものなので、セキュリティも何もない。自己責任でお願いします。 画面はこんな感じ。一応行番号も付くようにしています。 ログの先頭や末尾をサクッと見れたり、前ページ・後ページに移動できたりします。 なんならファイルダウンロードもできます。 一応、APIKeyで守ってますが、適当実装なので、ローカルネットワークでのみ使てください。 ソースコードもろもろはGitHubに上げておきました。 poruruba/SimpleLogViewer ソースコード(サーバ) そのままソースコード載せておきます。 api/controllers/tail-file/index.js 'use strict'; const HELPER_BASE = process.env.HELPER_BASE || '../../helpers/'; const Response = require(HELPER_BASE + 'response'); const BinResponse = require(HELPER_BASE + 'binresponse'); const TextResponse = require(HELPER_BASE + 'textresponse'); const APIKEY = "【お好きなAPIKey】"; const logfile_list = [ // 参照したいログファイル名の配列 ]; const { exec } = require('child_process'); const streamBuffers = require('stream-buffers'); const archiver = require('archiver'); const path = require('path'); exports.handler = async (event, context, callback) => { if( event.path == '/tail-view-file'){ if (!event.requestContext.apikeyAuth || event.requestContext.apikeyAuth.apikey != APIKEY ) throw "wrong apikey"; var body = JSON.parse(event.body); console.log(body); if( logfile_list.indexOf(body.fname) < 0 ) throw 'not allowed'; var num = Number(body.num); var start = Number(body.start); return new Promise((resolve, reject) =>{ var exec_batch; if (body.order == 'head'){ exec_batch = `cat -n ${body.fname} | head -n ${start - 1 + num} | tail -n ${num} | sed -r "s/\\x1B\\[([0-9]{1,2}(;[0-9]{1,2})*)?m//g" | col -bx`; } else if (body.order == 'tail'){ exec_batch = `cat -n ${body.fname} | tail -n ${start - 1 + num} | head -n ${num} | sed -r "s/\\x1B\\[([0-9]{1,2}(;[0-9]{1,2})*)?m//g" | col -bx`; }else{ reject('unknown order'); } exec(exec_batch, (err, stdout, stderr) => { if (err) { reject(err); return; } resolve(new TextResponse("text/plain", stdout)); }); }); }else if( event.path == '/tail-get-file'){ if (!event.requestContext.apikeyAuth || event.requestContext.apikeyAuth.apikey != APIKEY ) throw "wrong apikey"; var body = JSON.parse(event.body); console.log(body); if( logfile_list.indexOf(body.fname) < 0 ) throw 'not allowed'; return new Promise((resolve, reject) =>{ var dest_stream = new streamBuffers.WritableStreamBuffer(); const archive = archiver('zip', { zlib: { level: 9 } }); dest_stream.on('finish', () => { console.log('stream finish'); var response = new BinResponse('application/zip', dest_stream.getContents()); response.set_filename(path.basename(body.fname) + '.zip'); resolve(response); }); archive.pipe(dest_stream); archive.on('error', (err) => { reject(err); }); archive.file(body.fname, { name: path.basename(body.fname) }); archive.finalize(); }); }else if( event.path == '/tail-list' ){ return new Response({ list: logfile_list }); } }; 要は、以下のようなコマンドを実行しているだけです。 行番号付けたり、画面制御コードを省いたりしています。 cat -n ${body.fname} | tail -n ${start - 1 + num} | head -n ${num} | sed -r "s/\\x1B\\[([0-9]{1,2}(;[0-9]{1,2})*)?m//g" | col -bx ソースコード(クライアント) クライアント側ソースコード。 特に難しいことはしていません。Vueのおかげで。 public/log_viwer/js/start.js 'use strict'; //const vConsole = new VConsole(); //window.datgui = new dat.GUI(); const base_url = "http://【本サーバのURL】"; var vue_options = { el: "#top", mixins: [mixins_bootstrap], data: { apikey: '', file_list: [], num_of_col: 1, select_file: ["", ""], log_data: ['', ''], select_order: ["tail", "tail"], start_line: [1, 1], get_line: [30, 30], top_line: [-1, -1] }, computed: { class_row: function () { return "col-sm-" + Math.floor(12 / this.num_of_col); } }, methods: { check_top_line: function (index, log_data) { try { var i = 0; for (; ; i++) if (log_data.charAt(i) != ' ' || !log_data.charAt(i)) break; var j = i; for (; ; j++) if (log_data.charAt(j) == ' ' || !log_data.charAt(j)) break; if (i < j) this.top_line[index] = Number(log_data.substring(i, j)); else this.top_line[index] = -1; } catch (error) { console.log(error); } }, add_num: function (index, target, num) { if (target == 'start_line') { var line = this.start_line[index] + num; if (line < 1) line = 1; this.$set(this.start_line, index, line); } else if (target == 'get_line') { var line = this.get_line[index] + num; if (line < 1) line = 1; this.$set(this.get_line, index, line); } }, log_get_file: async function (index) { try { this.progress_open(); var param = { fname: this.select_file[index], }; var blob = await do_post_blob_with_apikey(base_url + "/tail-get-file", param, this.apikey); Cookies.set('tail-apikey', this.apikey, { expires: 3650 }); var url = window.URL.createObjectURL(blob); var a = document.createElement('a'); a.href = url; a.target = '_blank'; a.download = "download.zip"; a.click(); window.URL.revokeObjectURL(url); } catch (error) { console.error(error); alert(error); } finally { this.progress_close(); } }, log_view_file: async function (index) { var param = { fname: this.select_file[index], order: this.select_order[index], start: this.start_line[index], num: this.get_line[index], }; try { var log_data = await do_post_text_with_apikey(base_url + "/tail-view-file", param, this.apikey); if (!log_data) return; this.check_top_line(index, log_data); this.$set(this.log_data, index, log_data); Cookies.set('tail-apikey', this.apikey, { expires: 3650 }); } catch (error) { console.error(error); alert(error); } }, log_next: async function (index) { if (this.top_line[index] < 0) return; var start; if (this.select_order[index] == 'tail') { start = this.top_line[index] - this.get_line[index]; if (start < 1) start = 1; } else if (this.select_order[index] == 'head') { start = this.top_line[index] + this.get_line[index]; } var param = { fname: this.select_file[index], order: 'head', start: start, num: this.get_line[index], }; try { var log_data = await do_post_text_with_apikey(base_url + "/tail-view-file", param, this.apikey); if (!log_data) return; this.check_top_line(index, log_data); this.$set(this.log_data, index, log_data); } catch (error) { console.error(error); alert(error); } }, log_prev: async function (index) { if (this.top_line[index] < 0) return; var start; if (this.select_order[index] == 'tail') { start = this.top_line[index] + this.get_line[index]; } else if (this.select_order[index] == 'head') { start = this.top_line[index] - this.get_line[index]; if (start < 1) start = 1; } var param = { fname: this.select_file[index], order: 'head', start: start, num: this.get_line[index], }; try { var log_data = await do_post_text_with_apikey(base_url + "/tail-view-file", param, this.apikey); if (!log_data) return; this.check_top_line(index, log_data); this.$set(this.log_data, index, log_data); } catch (error) { console.error(error); alert(error); } }, }, created: function () { }, mounted: async function () { proc_load(); this.apikey = Cookies.get('tail-apikey'); try { var result = await do_post(base_url + "/tail-list", {}); this.file_list = result.list; this.select_file[0] = this.file_list[0]; this.select_file[1] = this.file_list[0]; } catch (error) { console.error(error); alert(error); } } }; vue_add_data(vue_options, { progress_title: '' }); // for progress-dialog vue_add_global_components(components_bootstrap); vue_add_global_components(components_utils); /* add additional components */ window.vue = new Vue(vue_options); function do_post_text_with_apikey(url, body, apikey) { const headers = new Headers({ "Content-Type": "application/json; charset=utf-8", "X-API-KEY": apikey }); return fetch(url, { method: 'POST', body: JSON.stringify(body), headers: headers }) .then((response) => { if (!response.ok) throw 'status is not 200'; // return response.json(); return response.text(); // return response.blob(); // return response.arrayBuffer(); }); } function do_post_blob_with_apikey(url, body, apikey) { const headers = new Headers({ "Content-Type": "application/json; charset=utf-8", "X-API-KEY": apikey }); return fetch(url, { method: 'POST', body: JSON.stringify(body), headers: headers }) .then((response) => { if (!response.ok) throw 'status is not 200'; // return response.json(); // return response.text(); return response.blob(); // return response.arrayBuffer(); }); } 以上
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ブラウザからLinuxサーバのログを気軽に参照するWebサーバ

UbuntuなどのLinuxサーバにあるログを参照するためにわざわざSSHでログインするのが面倒なので、ブラウザから参照できるようにした。 適当につくったものなので、セキュリティも何もない。自己責任でお願いします。 画面はこんな感じ。一応行番号も付くようにしています。 ログの先頭や末尾をサクッと見れたり、前ページ・後ページに移動できたりします。 なんならファイルダウンロードもできます。 一応、APIKeyで守ってますが、適当実装なので、ローカルネットワークでのみ使てください。 ソースコードもろもろはGitHubに上げておきました。 poruruba/SimpleLogViewer ソースコード(サーバ) そのままソースコード載せておきます。 api/controllers/tail-file/index.js 'use strict'; const HELPER_BASE = process.env.HELPER_BASE || '../../helpers/'; const Response = require(HELPER_BASE + 'response'); const BinResponse = require(HELPER_BASE + 'binresponse'); const TextResponse = require(HELPER_BASE + 'textresponse'); const APIKEY = "【お好きなAPIKey】"; const logfile_list = [ // 参照したいログファイル名の配列 ]; const { exec } = require('child_process'); const streamBuffers = require('stream-buffers'); const archiver = require('archiver'); const path = require('path'); exports.handler = async (event, context, callback) => { if( event.path == '/tail-view-file'){ if (!event.requestContext.apikeyAuth || event.requestContext.apikeyAuth.apikey != APIKEY ) throw "wrong apikey"; var body = JSON.parse(event.body); console.log(body); if( logfile_list.indexOf(body.fname) < 0 ) throw 'not allowed'; var num = Number(body.num); var start = Number(body.start); return new Promise((resolve, reject) =>{ var exec_batch; if (body.order == 'head'){ exec_batch = `cat -n ${body.fname} | head -n ${start - 1 + num} | tail -n ${num} | sed -r "s/\\x1B\\[([0-9]{1,2}(;[0-9]{1,2})*)?m//g" | col -bx`; } else if (body.order == 'tail'){ exec_batch = `cat -n ${body.fname} | tail -n ${start - 1 + num} | head -n ${num} | sed -r "s/\\x1B\\[([0-9]{1,2}(;[0-9]{1,2})*)?m//g" | col -bx`; }else{ reject('unknown order'); } exec(exec_batch, (err, stdout, stderr) => { if (err) { reject(err); return; } resolve(new TextResponse("text/plain", stdout)); }); }); }else if( event.path == '/tail-get-file'){ if (!event.requestContext.apikeyAuth || event.requestContext.apikeyAuth.apikey != APIKEY ) throw "wrong apikey"; var body = JSON.parse(event.body); console.log(body); if( logfile_list.indexOf(body.fname) < 0 ) throw 'not allowed'; return new Promise((resolve, reject) =>{ var dest_stream = new streamBuffers.WritableStreamBuffer(); const archive = archiver('zip', { zlib: { level: 9 } }); dest_stream.on('finish', () => { console.log('stream finish'); var response = new BinResponse('application/zip', dest_stream.getContents()); response.set_filename(path.basename(body.fname) + '.zip'); resolve(response); }); archive.pipe(dest_stream); archive.on('error', (err) => { reject(err); }); archive.file(body.fname, { name: path.basename(body.fname) }); archive.finalize(); }); }else if( event.path == '/tail-list' ){ return new Response({ list: logfile_list }); } }; 要は、以下のようなコマンドを実行しているだけです。 行番号付けたり、画面制御コードを省いたりしています。 cat -n ${body.fname} | tail -n ${start - 1 + num} | head -n ${num} | sed -r "s/\\x1B\\[([0-9]{1,2}(;[0-9]{1,2})*)?m//g" | col -bx ソースコード(クライアント) クライアント側ソースコード。 特に難しいことはしていません。Vueのおかげで。 public/log_viwer/js/start.js 'use strict'; //const vConsole = new VConsole(); //window.datgui = new dat.GUI(); const base_url = "http://【本サーバのURL】"; var vue_options = { el: "#top", mixins: [mixins_bootstrap], data: { apikey: '', file_list: [], num_of_col: 1, select_file: ["", ""], log_data: ['', ''], select_order: ["tail", "tail"], start_line: [1, 1], get_line: [30, 30], top_line: [-1, -1] }, computed: { class_row: function () { return "col-sm-" + Math.floor(12 / this.num_of_col); } }, methods: { check_top_line: function (index, log_data) { try { var i = 0; for (; ; i++) if (log_data.charAt(i) != ' ' || !log_data.charAt(i)) break; var j = i; for (; ; j++) if (log_data.charAt(j) == ' ' || !log_data.charAt(j)) break; if (i < j) this.top_line[index] = Number(log_data.substring(i, j)); else this.top_line[index] = -1; } catch (error) { console.log(error); } }, add_num: function (index, target, num) { if (target == 'start_line') { var line = this.start_line[index] + num; if (line < 1) line = 1; this.$set(this.start_line, index, line); } else if (target == 'get_line') { var line = this.get_line[index] + num; if (line < 1) line = 1; this.$set(this.get_line, index, line); } }, log_get_file: async function (index) { try { this.progress_open(); var param = { fname: this.select_file[index], }; var blob = await do_post_blob_with_apikey(base_url + "/tail-get-file", param, this.apikey); Cookies.set('tail-apikey', this.apikey, { expires: 3650 }); var url = window.URL.createObjectURL(blob); var a = document.createElement('a'); a.href = url; a.target = '_blank'; a.download = "download.zip"; a.click(); window.URL.revokeObjectURL(url); } catch (error) { console.error(error); alert(error); } finally { this.progress_close(); } }, log_view_file: async function (index) { var param = { fname: this.select_file[index], order: this.select_order[index], start: this.start_line[index], num: this.get_line[index], }; try { var log_data = await do_post_text_with_apikey(base_url + "/tail-view-file", param, this.apikey); if (!log_data) return; this.check_top_line(index, log_data); this.$set(this.log_data, index, log_data); Cookies.set('tail-apikey', this.apikey, { expires: 3650 }); } catch (error) { console.error(error); alert(error); } }, log_next: async function (index) { if (this.top_line[index] < 0) return; var start; if (this.select_order[index] == 'tail') { start = this.top_line[index] - this.get_line[index]; if (start < 1) start = 1; } else if (this.select_order[index] == 'head') { start = this.top_line[index] + this.get_line[index]; } var param = { fname: this.select_file[index], order: 'head', start: start, num: this.get_line[index], }; try { var log_data = await do_post_text_with_apikey(base_url + "/tail-view-file", param, this.apikey); if (!log_data) return; this.check_top_line(index, log_data); this.$set(this.log_data, index, log_data); } catch (error) { console.error(error); alert(error); } }, log_prev: async function (index) { if (this.top_line[index] < 0) return; var start; if (this.select_order[index] == 'tail') { start = this.top_line[index] + this.get_line[index]; } else if (this.select_order[index] == 'head') { start = this.top_line[index] - this.get_line[index]; if (start < 1) start = 1; } var param = { fname: this.select_file[index], order: 'head', start: start, num: this.get_line[index], }; try { var log_data = await do_post_text_with_apikey(base_url + "/tail-view-file", param, this.apikey); if (!log_data) return; this.check_top_line(index, log_data); this.$set(this.log_data, index, log_data); } catch (error) { console.error(error); alert(error); } }, }, created: function () { }, mounted: async function () { proc_load(); this.apikey = Cookies.get('tail-apikey'); try { var result = await do_post(base_url + "/tail-list", {}); this.file_list = result.list; this.select_file[0] = this.file_list[0]; this.select_file[1] = this.file_list[0]; } catch (error) { console.error(error); alert(error); } } }; vue_add_data(vue_options, { progress_title: '' }); // for progress-dialog vue_add_global_components(components_bootstrap); vue_add_global_components(components_utils); /* add additional components */ window.vue = new Vue(vue_options); function do_post_text_with_apikey(url, body, apikey) { const headers = new Headers({ "Content-Type": "application/json; charset=utf-8", "X-API-KEY": apikey }); return fetch(url, { method: 'POST', body: JSON.stringify(body), headers: headers }) .then((response) => { if (!response.ok) throw 'status is not 200'; // return response.json(); return response.text(); // return response.blob(); // return response.arrayBuffer(); }); } function do_post_blob_with_apikey(url, body, apikey) { const headers = new Headers({ "Content-Type": "application/json; charset=utf-8", "X-API-KEY": apikey }); return fetch(url, { method: 'POST', body: JSON.stringify(body), headers: headers }) .then((response) => { if (!response.ok) throw 'status is not 200'; // return response.json(); // return response.text(); return response.blob(); // return response.arrayBuffer(); }); } 以上
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

英単語を覚えるための語呂作成マシーンを作ってみた

はじめに 英単語の覚え方は色々ありますが、個人的には語呂合わせで覚えるのが一番忘れにくい気がします。 英単語の語呂合わせの市販本は多数あるのですが、すべての単語をカバーしている訳でもなく、自分がなかなか覚えられない単語の語呂がそうそう見つかる訳でもありません。 ただ、多くの語呂を見てみるといくつかのパターンがあるように思え、それを組み合わせれば語呂候補を機械的に作成して、語呂文を作るヒントとできないかと思い、自作してみました。 英単語語呂作成マシーンの使い方 最新版を英単語語呂作成マシーンとして公開しています。 入力BOXに英単語を入れて、語呂作成ボタンを押してもらえば、下に語呂の候補が出力されると思います。 例えば、「abrupt」の英単語だと下図のように文字候補と語呂候補が出力されます。 語呂候補の「()」で囲まれたひらがな部分が読みです。 この語呂候補をヒントに、例えば「abrupt(意味:突然)」の語呂合わせ文を    「油ぷっと 突然 噴出」 などとご自分で完成させてください。 なお、「あば」の語呂候補として「(あば)暴(く)」みたいに動詞の活用語尾である「(く)」が含まれてしまうことがあります。そこは声には出さずに暗黙読みをするものだと思ってください(語呂合わせあるある)。 以降で、処理の詳細をご説明いたします。 (ソースコードがクイックハックであまりきれいなコードでないこともありますが、処理として特に難しいこともしていないため、主に処理の流れをご説明します)。 ソースコード一式はGitHubで公開しております。 詳細処理 処理方針 おおまかには以下の手順で処理することを考えました。 英単語を入力する。 英単語を発音記号に変換する。 各発音記号に対して日本語読みの候補(複数)を作成する。 日本語読みの候補をつなげた部分文字列に対して、日本語辞書に当てはまるものがあれば、語呂要素の候補とする。 語呂要素候補をつなげて1文の語呂とし、語呂の完成度のポイントを計算する。 各語呂のポイントでソートして、上位からN件を提示する。 処理2:英単語を発音記号に変換する 発音記号を取得できるフリーの辞書を探してみました。 それなりの規模のフリーの英和辞書や英英辞書はいくつかあるのですが、意外と発音記号を取得できるものは少なく、唯一使えそうなのがThe CMU Pronouncing Dictionaryでした。 ソースコードはGitHubに移っているようで、その内のcmudict.dictファイルを使いました(執筆時で13.5万語近くありました。ライセンスは二条項BSDライセンスに近い感じのものです)。 このデータはプレーンテキストで、1行に英単語とその発音記号が並んでいる単純なものです。下記例はデータの一部です。 ABRUPT AH B R AH P T この辞書を使うことで英単語を発音記号文字列に変換できますが、発音記号が英和辞書で見慣れているものと違います。我々が見慣れているものは「International Phonetic Alphabet(IPA)」と呼ばれる形式で、上記は「ARPABET」と呼ばれる形式だそうです。 見慣れたIPA形式の方が日本語読みの候補を考えやすいので、Wikipediaの変換表を使って置換し、英単語からIPA形式の発音記号文字列を変換できるようにしました。 単語から発音記号への変換例 ABRUPT -> AH B R AH P T -> ʌbrʌpt (なお、単語・発音記号データをデータベースに取り込むなり、アプリに組み込んでしまうなりすればよいのですが、できるだけGCPの無料の範囲で動かしたいので、Cloud SQLは使いたくないです。データをアプリに組み込んでもよいのですが、取り急ぎ、配布元のCMUの検索ページを使わせてもらうことにしました。) (データを無料公開しているので検索結果を使ってもそうそう怒られはしないでしょうが、望ましいことではないので、おいおいアプリに組み込むようにしようと思います。) 処理3:各発音記号に対して日本語読みの候補(複数)を作成する 語呂合わせが多くのっている英単語語呂合わせというページも参考にしながら、英語から日本語読みへの語呂のパターンを分析・抽出してみました。 母音のパターン 例えば発音記号の「a」は日本語読みは「あ」と単純ですが、発音記号によっては下記のように発音的に複数の日本語読みが可能なものがあります。 複数の日本語読みを持つ母音の発音記号 'ɪ': 「い」 or 「え」 'æ': 「あ」 or 「え」 'ɒ': 「あ」 or 「お」 'ə': 「あ」 or 「い」 or 「う」 or 「え」 or 「お」 子音のパターン ローマ字で使われている子音は、そのまま日本語読みで使えます。例えば「k」は母音と組み合わせて「か行」の文字になります。 ローマ字では使われていないけど、比較的単純に日本語の行に当てはめられるものとして下記があります(厳密には「ふぁ行」などの言い方は存在しないかもしれませんが)。 比較的単純に日本語行に当てはめられる子音の例 'ʃ': さ行 'j': や行 'l': ら行 'v': ば行 'θ': さ行 'ð': ざ行 'f': ふぁ行 'ʃ': しゃ行 'ʒ': じゃ行 'gj': ぎゃ行 'ɡj': ぎゃ行 'dʒ': じゃ行 'bj': びゃ行 'pj': ぴゃ行 'tʃ': ちゃ行 'kw': くあ行 また、母音が続かない子音の場合、英語発音的には「う段」や「お段」ですが、語呂の読み候補ではどの段にも無理やり当てはめることがされます。 母音が続かない子音の読み例 'k': か行 's': す行 'ʃ': しゃ行 or さ行 't': た行 下記は特殊な読み例です。 特殊な読み例 'm': 「ん」 or ま行 'n': 「ん」 'ŋ': 「ん」 発音記号の「:」は日本語読みの長音に当てはまります。ただし、語呂の読み候補では長音を削除することがあります。 長音の読み 'ː': 「ー」 or 無し 「p」などの破裂音の子音の直前は、日本語読みの促音になりえます。ただし、語呂の読み候補では促音を削除することがあります。 促音の読みとなりうる子音の例 'k', 'ʃ', 't', 'g', 'ɡ', 'd', 'p', 'ʒ', 'dʒ', 'tʃ' 例えば、英単語「abrupt」の発音記号「ʌbrʌpt」に対する日本語読み候補としては以下になります。「' '」は文字無しと解釈してください。 abruptの日本語読み候補例 あ' ば' or 'び' or 'ぶ' or 'べ' or 'ぼ' ら' っ' or ' ' ぱ' or 'ぴ' or 'ぶ' or 'ぺ' or 'ぽ' or 'ぷ' っ' or ' ' と' 処理4:日本語読みの候補をつなげた部分文字列に対して、日本語辞書に当てはまるものがあれば、語呂要素の候補とする 辞書 日本語読みの候補の組み合わせに対して、対応する日本語が存在するかどうかを辞書を使って判定する必要があります。 フリーの日本語辞書データはいくつかありますが、データの内容・量・使いやすさを考えて、JUMAN Ver.7.01を使うことにしました。 辞書データはRDBを使わずにJavaScriptの連想配列として読み込ませています。動詞など活用形があるものは、とりあえずすべての活用形を登録しています。 JUMANの形式で記載すれば、自分で辞書を追加できるようにしてあります。(my_juman.dicファイル) また、JUMANの辞書以外にもオノマトペのデータを収集し、辞書に追加しています。 読み候補の組み合わせ 日本語読みの各文字は複数の候補がありますが、現状ではそれらの組み合わせの文字列をすべて作成します。 上記のabruptの例では下記のような組み合わせ文字列が作成されます abruptの組み合わせ文字列例 あばらっぱっと あばらっぱと あばらっぴっと あばらっぴと 、、、 そして、各組み合わせ文字列の先頭から長さ1 - Nの部分文字列を作成し、それらの部分文字列が辞書にヒットするかどうかを調べます。ヒットしたら、ヒットした部分を除いた残りの文字列に対して、同様に先頭から長さ1 - Mの部分文字列を作成し、それらの部分文字列が辞書にヒットするかどうかを調べる、ということを繰り返します。 「あぶらぷっと」のヒット例 あばらぷっと -> ヒットしないので1文字短くする あばらぷっ -> ヒットしないので1文字短くする あばら -> 「(あぶら)油,脂,焙ら」がヒットしたので、残りの文字列を使う ぷっと -> 「ぷっと」がヒット 処理5:語呂要素候補をつなげて1文の語呂とし、語呂の完成度のポイントを計算する すべての部分文字列がヒットしたら、その組み合わせ文字列のポイントを計算します。 部分文字列毎にポイントを計算します。辞書にヒットした部分文字列が長いほどポイントを高くしています(ポイントの与え方は試行錯誤してバランスを取るようにしています)。 たとえば「あぶ(虻)」より「あぶら(油)」の方がポイントは高いです。 すべての部分文字列のポイントの総和をその組み合わせ文字列のポイントとしています。 処理6:各語呂のポイントでソートして、上位からN件を提示する すべての組み合わせ文字列のポイントが出たら、ソートして表示します。 システム構成 英単語文字列からソートした組み合わせ文字列を返す処理はnode.jsで記述し、Cloud Functionsとして登録しています。 ローカルPCでnode.jsの処理をコマンドラインで呼び出す場合は、import文を使えるのですが、現状のCloud Functionsはimport文が使えず、require文に変更する必要があります。 そこの切り替えが面倒だったので、Webpackで一つのJavaScriptにまとめてからCloud Functionsにデプロイする形にしています。 ブラウザアプリのUI層はVue.jsで作成しています。入力された英単語をCloud Functionsに渡し、レスポンスとして返ってきたソートされた組み合わせ文字列を表示します。 所感 ジャストアイデアで試作してみたが、思ったよりも使えそうな結果が出力されているように思えます。 英語の語呂合わせは結構強引な読み方をさせることが多く、そのパターンをいかにうまく抽出して適用するかが語呂作成のポイントとなりそうです。 今回の処理でいくと、日本語読み候補の組み合わせを作るところです。 最初はディープラーニングでパターンを学習させようかとも思いましたが、英語の語呂合わせに関して膨大な数のサンプルデータを準備することは難しく、少ないサンプルデータで効率よく学習させるには日本語辞書をうまく使う必要があると思われ、そう簡単では無さそうです(私のディープラーニングの知識と経験では)。 そのため、語呂合わせのパターンを手動で抽出してコード化する地道なやりかたで実装し、なんとか形になりました。 今後の改善点としては、例えば以下を考えています。 語呂合わせのパターンを増やす 現状は適用しやすいパターンだけを選んでコード化した。まだコード化できていないパターンは多数ある。 組み合わせ文字列の末尾を語幹に含む日本語辞書の適用 例えば、組み合わせ文字列が「、、ばすた」で終わっていて、「ばす」までが前の語として辞書ヒットしているとすると、現状では「た」の完全一致しか辞書単語を探せない。「たのむ」など一部の文字を末尾に含む形の候補が現状では出力できない。 JavaScriptの連想配列を使っているため、前方一致検索ができない(やろうとするとパフォーマンスが落ちる)。前方一致をヒットさせるには、連想配列を作るときに前方文字列だけのものを登録するとか、RDBを使うとか、が必要か。 文法的、日本語的に意味がありそうな候補のポイントを高くする きちんとやるのは大変だが、品詞の並びを見るぐらいなら比較的やりやすいか。 ディープラーニングでの実装
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Firebaseの開発環境(local)を作ってみるよ

経緯 ・firebase-toolsのバージョンが上がってCloud StorageのEmulatorが実装されたみたい ・Node.jsバージョン16がCloudFunctionsでサポートされたみたい 必要なもの、必要な知識 ・Firebaseとは何か勉強しておいて ・Terminal操作ができるようにしておいて(コマンドコピペできる程度で) ・Homebrew インストールしておいて ・Googleアカウントは作っておいて ・GitHubのアカウントは作っておいて ・Node.jsはインストールしておいて(記事内ではVer.16+でやるよ) ・エディタはVisual Studio Code使うよ ・Gitは使うからインストールしといて 環境とか ・MacBookPro '13 (2017) BigSur ・Homebrew 3.2.11 ・Visual Studio Code 1.16.0 ・Node.js 16.9.1 事前に作成しておくと良いこと ・Googleのアカウント作成とfirebaseのプロジェクト作成、firestoreの作成 ・GitHubのアカウントと作ったプロジェクトをcommitするようのリポジトリ  ※privateリポジトリにしておいたほうが良いかも。 Let's Try!!! 作業用ディレクトリの作成・移動 $ mkdir ~/Documents/Sample/firebase-local $ cd ~/Documents/Sample/firebase-local git初期化(いつものやつね) $ git init .gitignore作成(自動生成させちゃうよ) giboがインストールされてない場合は、brew install gibo オプションはmacosだけでよさそうだけど、まとめて全部ガツンと行っちゃえ! $ gibo dump macos linux windows node > .gitignore (念の為)ちゃんと出来てるか確認しておこう $ ls -a . .. .git .gitignor よさげね。 (ここで?)Node.jsとnpmのバージョン確認しておこうか( ・`ω・´) $ node -v v16.9.1 $ npm -v 7.21.1 FirebaseCLIが必要だからインストールするんだぜ $ npm install -g firebase-tools firebaseのバージョンを確認しておこう! $ firebase --version 9.18.0 firebaseにログインする $ firebase login ブラウザが自動的に開いてログイン。 流れに沿ってfirebaseCLIへのアクセス許可をする。 Firebase CLI Login Successful が表示されるとOK!(画面は閉じてOK) Firebase Consoleにログインしてプロジェクトを作成! アナリティクスはOFFに 完成! firebaseのプロジェクトディレクトリとして初期化するよ ココからはTerminalに戻って作業続行するよ $ firebase init ######## #### ######## ######## ######## ### ###### ######## ## ## ## ## ## ## ## ## ## ## ## ###### ## ######## ###### ######## ######### ###### ###### ## ## ## ## ## ## ## ## ## ## ## ## #### ## ## ######## ######## ## ## ###### ######## You're about to initialize a Firebase project in this directory: /Users/UserName/Documents/Sample/firebase-local ? Which Firebase features do you want to set up for this directory? Press Space to select features, then Enter to confirm your choices. (Press <space> to select, <a> to toggle all, <i> to invert selection) ❯◯ Realtime Database: Configure a security rules file for Realtime Database and (optionally) provision default instance ◯ Firestore: Configure security rules and indexes files for Firestore ◯ Functions: Configure a Cloud Functions directory and its files ◯ Hosting: Configure files for Firebase Hosting and (optionally) set up GitHub Action deploys ◯ Hosting: Set up GitHub Action deploys ◯ Storage: Configure a security rules file for Cloud Storage ◯ Emulators: Set up local emulators for Firebase products (Move up and down to reveal more choices) せっかくなので、全部チェック入れちゃえ! ↑↓で移動して<space>キーでチェックを入れるんだ! 選択後。。。 ◉ Realtime Database: Configure a security rules file for Realtime Database and (optionally) provision default instance ◉ Firestore: Configure security rules and indexes files for Firestore ◉ Functions: Configure a Cloud Functions directory and its files ❯◉ Hosting: Configure files for Firebase Hosting and (optionally) set up GitHub Action deploys ◉ Hosting: Set up GitHub Action deploys ◉ Storage: Configure a security rules file for Cloud Storage ◉ Emulators: Set up local emulators for Firebase products 今回は新規プロジェクトとして作成Create a new projectを選択して <Enter> ? Which Firebase features do you want to set up for this directory? Press Space to select features, then Enter to confirm your choices. Realtime Database: Configure a security rules file for Realtime Database and (optionally) provision de fault instance, Firestore: Configure security rules and indexes files for Firestore, Functions: Configure a Cloud Functions directory and its files, Hosting: Configure files for Firebase Hosting and (optionally) set up GitHub Action deplo ys, Hosting: Set up GitHub Action deploys, Storage: Configure a security rules file for Cloud Storage, Emulators: Set up local emulators for Firebase products, Remote Config: Configure a template file for Remote Config === Project Setup First, let's associate this project directory with a Firebase project. You can create multiple project aliases by running firebase use --add, but for now we'll just set up a default project. ? Please select an option: ❯ Use an existing project   Create a new project Add Firebase to an existing Google Cloud Platform project Don't set up a default project Use an existing projectを選択して<Enter> ? Select a default Firebase project for this directory: (Use arrow keys) ❯ fb-tool-sample (fb-tool-sample) 先程画面から作成したfb-tool-sampleを選択して<Enter> === Database Setup i database: ensuring required API firebasedatabase.googleapis.com is enabled... ⚠ database: missing required API firebasedatabase.googleapis.com. Enabling now... ✔ database: required API firebasedatabase.googleapis.com is enabled ? It seems like you haven’t initialized Realtime Database in your project yet. Do you want to set it up? Yes ← "Y" で回答 ? Please choose the location for your default Realtime Database instance: asia-southeast1 ← "asia-southeast1"を選択 ✔ Creating your default Realtime Database instance: fb-tool-sample-default-rtdb Firebase Realtime Database Security Rules allow you to define how your data should be structured and when your data can be read from and written to. ? What file should be used for Realtime Database Security Rules? database.rules.json ← デフォルトのまま<Enter> ✔ Database Rules for fb-tool-sample-default-rtdb have been written to database.rules.json. Future modifications to database.rules.json will update Realtime Database Security Rules when you run firebase deploy. === Firestore Setup Error: It looks like you haven't used Cloud Firestore in this project before. Go to https://console.firebase.google.com/project/fb-tool-sample/firestore to create your Cloud Firestore database. あれ?エラーなった。。。 なに?なに? 要約するとあなたはFirestore使ったことがないみたいだから、URL踏んで作ってきて的な感じ。 言われたとおり、画面にログインしてプロジェクトを選択後、firestoreのデータベースを作成する。 画面キャプチャは省略〜テストモードでasia-noatheast3で作ってみた。 firebase init ######## #### ######## ######## ######## ### ###### ######## ## ## ## ## ## ## ## ## ## ## ## ###### ## ######## ###### ######## ######### ###### ###### ## ## ## ## ## ## ## ## ## ## ## ## #### ## ## ######## ######## ## ## ###### ######## You're about to initialize a Firebase project in this directory: /Users/UserName/Documents/Sample/firebase-local ? Which Firebase features do you want to set up for this directory? Press Space to select features, then Enter to confirm your choices. Realtime Database: Configure a security rules file for Realtime Database and (optionally) provision de fault instance, Firestore: Configure security rules and indexes files for Firestore, Functions: Configure a Cloud Functions directory and its files, Hosting: Configure files for Firebase Hosting and (optionally) set up GitHub Action deplo ys, Hosting: Set up GitHub Action deploys, Storage: Configure a security rules file for Cloud Storage, Emulators: Set up local emulators for Firebase products, Remote Config: Configure a template file for Remote Config === Project Setup First, let's associate this project directory with a Firebase project. You can create multiple project aliases by running firebase use --add, but for now we'll just set up a default project. ? Please select an option: Use an existing project ? Select a default Firebase project for this directory: fb-tool-sample (fb-tool-sample) i Using project fb-tool-sample (fb-tool-sample) === Database Setup i database: ensuring required API firebasedatabase.googleapis.com is enabled... ✔ database: required API firebasedatabase.googleapis.com is enabled Firebase Realtime Database Security Rules allow you to define how your data should be structured and when your data can be read from and written to. ? What file should be used for Realtime Database Security Rules? database.rules.json ? File database.rules.json already exists. Do you want to overwrite it with the Realtime Database Security Rules for fb-tool-sample-default-rtdb from the Firebase console? No Skipping overwrite of Realtime Database Security Rules. The security rules defined in database.rules.json will be published when you run firebase deploy. === Firestore Setup Firestore Security Rules allow you to define how and when to allow requests. You can keep these rules in your project directory and publish them with firebase deploy. ? What file should be used for Firestore Rules? firestore.rules ? File firestore.rules already exists. Do you want to overwrite it with the Firestore Rules from the Firebase Console? No Firestore indexes allow you to perform complex queries while maintaining performance that scales with the size of the result set. You can keep index definitions in your project directory and publish them with firebase deploy. ? What file should be used for Firestore indexes? firestore.indexes.json ? File firestore.indexes.json already exists. Do you want to overwrite it with the Firestore Indexes from the Firebase Console? No === Functions Setup A functions directory will be created in your project with sample code pre-configured. Functions can be deployed with firebase deploy. ? What language would you like to use to write Cloud Functions? JavaScript ? Do you want to use ESLint to catch probable bugs and enforce style? No ? File functions/package.json already exists. Overwrite? No i Skipping write of functions/package.json ? File functions/index.js already exists. Overwrite? No i Skipping write of functions/index.js ? File functions/.gitignore already exists. Overwrite? No i Skipping write of functions/.gitignore ? Do you want to install dependencies with npm now? Yes npm WARN EBADENGINE Unsupported engine { npm WARN EBADENGINE package: undefined, npm WARN EBADENGINE required: { node: '14' }, npm WARN EBADENGINE current: { node: 'v16.9.1', npm: '7.21.1' } npm WARN EBADENGINE } up to date, audited 323 packages in 3s 24 packages are looking for funding run `npm fund` for details found 0 vulnerabilities === Hosting Setup Your public directory is the folder (relative to your project directory) that will contain Hosting assets to be uploaded with firebase deploy. If you have a build process for your assets, use your build's output directory. ? What do you want to use as your public directory? public ? Configure as a single-page app (rewrite all urls to /index.html)? No ? Set up automatic builds and deploys with GitHub? No ✔ Wrote public/404.html ? File public/index.html already exists. Overwrite? No i Skipping write of public/index.html === Hosting:github Setup i Detected a .git folder at /Users/UserName/Documents/Sample/firebase-local i Authorizing with GitHub to upload your service account to a GitHub repository's secrets store. Visit this URL on this device to log in: https://github.com/login/oauth/authorize?client_id=xxxxxxxxxxxxxxxxxxxxxxxxxxxx&state=xxxxxxxxxxxxxxx&redirect_uri=http%3A%2F%2Flocalhost%3A9005&scope=read%3Auser%20repo%20public_repo Waiting for authentication... ✔ Success! Logged into GitHub as UserName ? For which GitHub repository would you like to set up a GitHub workflow? (format: user/repository) UserName/fb-tool-sample ✔ Created service account github-action-406020156 with Firebase Hosting admin permissions. ✔ Uploaded service account JSON to GitHub as secret FIREBASE_SERVICE_ACCOUNT_FB_TOOL_SAMPLE. i You can manage your secrets at https://github.com/UserName/fb-tool-sample/settings/secrets. ? Set up the workflow to run a build script before every deploy? No ✔ Created workflow file /Users/UserName/Documents/Sample/firebase-local/.github/workflows/firebase-hosting-pull-request.yml ? Set up automatic deployment to your site's live channel when a PR is merged? Yes ? What is the name of the GitHub branch associated with your site's live channel? main ✔ Created workflow file /Users/UserName/Documents/Sample/firebase-local/.github/workflows/firebase-hosting-merge.yml i Action required: Visit this URL to revoke authorization for the Firebase CLI GitHub OAuth App: https://github.com/settings/connections/applications/89cf50f02ac6aaed3484 i Action required: Push any new workflow file(s) to your repo === Storage Setup Firebase Storage Security Rules allow you to define how and when to allow uploads and downloads. You can keep these rules in your project directory and publish them with firebase deploy. ? What file should be used for Storage Rules? storage.rules ✔ Wrote storage.rules === Emulators Setup ? Which Firebase emulators do you want to set up? Press Space to select emulators, then Enter to confirm your choices. Authentication Emulator, Functions Emulator, Firestore Emulator, Database Emulator, Hosting Emulator, Pub/Sub Emulator, Storage Emulator ? Which port do you want to use for the auth emulator? 9099 ? Which port do you want to use for the functions emulator? 5001 ? Which port do you want to use for the firestore emulator? 8080 ? Which port do you want to use for the database emulator? 9000 ? Which port do you want to use for the hosting emulator? 5000 ? Which port do you want to use for the pubsub emulator? 8085 ? Which port do you want to use for the storage emulator? 9199 ? Would you like to enable the Emulator UI? Yes ? Which port do you want to use for the Emulator UI (leave empty to use any available port)? ? Would you like to download the emulators now? No === Remoteconfig Setup ? What file should be used for your Remote Config template? remoteconfig.template.json i Writing configuration info to firebase.json... i Writing project information to .firebaserc... i Writing gitignore file to .gitignore... ✔ Firebase initialization complete! Emulatorの初期化(インストール) エミュレータは全部選択。 ポートは全部デフォルト。 $ firebase init emulators ######## #### ######## ######## ######## ### ###### ######## ## ## ## ## ## ## ## ## ## ## ## ###### ## ######## ###### ######## ######### ###### ###### ## ## ## ## ## ## ## ## ## ## ## ## #### ## ## ######## ######## ## ## ###### ######## You're about to initialize a Firebase project in this directory: /Users/UserName/Documents/Sample/firebase-local Before we get started, keep in mind: * You are initializing within an existing Firebase project directory === Project Setup First, let's associate this project directory with a Firebase project. You can create multiple project aliases by running firebase use --add, but for now we'll just set up a default project. i .firebaserc already has a default project, using fb-tool-sample. === Emulators Setup ? Which Firebase emulators do you want to set up? Press Space to select emulators, then Enter to confirm your choices. (Press <space> to select, <a> to toggle all, <i> to invert selection)Authentication Emulator, Functions Emulator, Firestore Emulator, Database Emulator, Hosting Emulator, Pub/Sub Emulator, Storage Emulator i Port for auth already configured: 9099 i Port for functions already configured: 5001 i Port for firestore already configured: 8080 i Port for database already configured: 9000 i Port for hosting already configured: 5000 i Port for pubsub already configured: 8085 i Port for storage already configured: 9199 i Emulator UI already enabled with port: (automatic) ? Would you like to download the emulators now? No i Writing configuration info to firebase.json... i Writing project information to .firebaserc... ✔ Firebase initialization complete! エミュレータ起動じゃぁ! $ firebase emulators:start i emulators: Starting emulators: auth, functions, firestore, database, hosting, pubsub, storage ⚠ Your requested "node" version "14" doesn't match your global version "16" i firestore: downloading cloud-firestore-emulator-v1.13.1.jar... Progress: =====================================> (100% of 61MB) i firestore: Removing outdated emulator files: cloud-firestore-emulator-v1.11.15.jar i firestore: Firestore Emulator logging to firestore-debug.log i emulators: Shutting down emulators. i functions: Stopping Functions Emulator i firestore: Stopping Firestore Emulator i hub: Stopping emulator hub ⚠ database: Port 9000 is not open on localhost, could not start Database Emulator. ⚠ database: To select a different host/port, specify that host/port in a firebase.json config file: { // ... "emulators": { "database": { "host": "HOST", "port": "PORT" } } } i emulators: Shutting down emulators. Error: Could not start Database Emulator, port taken. あ。。。エラーです。 一旦、firebase.jsonを確認する cat firebase.json { "functions": { "source": "functions" }, "database": { "rules": "database.rules.json" }, "firestore": { "rules": "firestore.rules", "indexes": "firestore.indexes.json" }, "hosting": { "public": "public", "ignore": [ "firebase.json", "**/.*", "**/node_modules/**" ] }, "storage": { "rules": "storage.rules" }, "emulators": { "auth": { "port": 9099 }, "functions": { "port": 5001 }, "firestore": { "port": 8080 }, "database": { "port": 9000 }, "hosting": { "port": 5000 }, "pubsub": { "port": 8085 }, "storage": { "port": 9199 }, "ui": { "enabled": true } }, "remoteconfig": { "template": "remoteconfig.template.json" } } ホストの指定が足りないの?ポートがBusyなの? ポートから当たってみますか。。。 $ lsof -i :9000 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME php-fpm 67668 UserName 8u IPv4 0x615724fb70f47db1 0t0 TCP localhost:cslistener (LISTEN) php-fpm 67669 UserName 9u IPv4 0x615724fb70f47db1 0t0 TCP localhost:cslistener (LISTEN) php-fpm 67670 UserName 9u IPv4 0x615724fb70f47db1 0t0 TCP localhost:cslistener (LISTEN) やっぱり。。。ごめんなさい。ごめんなさい。ごめんなさいぃぃぃぃ。 じゃあ、別ポート使えばいいじゃん! 念の為、ポートが使われてないことを確認。 $ lsof -i :9001 結果が標準出力が何もなければ使われてない。 でもって、firebase.configを修正する。 もう、抜粋でいいよね。 "database": { "port": 9001 }, これで完璧のはずだ! $ firebase emulators:start i emulators: Starting emulators: auth, functions, firestore, database, hosting, pubsub, storage ⚠ Your requested "node" version "14" doesn't match your global version "16" i firestore: Firestore Emulator logging to firestore-debug.log i database: Database Emulator logging to database-debug.log i pubsub: Pub/Sub Emulator logging to pubsub-debug.log i hosting: Serving hosting files from: public ✔ hosting: Local server: http://localhost:5000 i ui: Emulator UI logging to ui-debug.log i functions: Watching "/Users/UserName/Documents/Sample/firebase-local/functions" for Cloud Functions... ┌─────────────────────────────────────────────────────────────┐ │ ✔ All emulators ready! It is now safe to connect your app. │ │ i View Emulator UI at http://localhost:4000 │ └─────────────────────────────────────────────────────────────┘ ┌────────────────┬────────────────┬─────────────────────────────────┐ │ Emulator │ Host:Port │ View in Emulator UI │ ├────────────────┼────────────────┼─────────────────────────────────┤ │ Authentication │ localhost:9099 │ http://localhost:4000/auth │ ├────────────────┼────────────────┼─────────────────────────────────┤ │ Functions │ localhost:5001 │ http://localhost:4000/functions │ ├────────────────┼────────────────┼─────────────────────────────────┤ │ Firestore │ localhost:8080 │ http://localhost:4000/firestore │ ├────────────────┼────────────────┼─────────────────────────────────┤ │ Database │ localhost:9001 │ http://localhost:4000/database │ ├────────────────┼────────────────┼─────────────────────────────────┤ │ Hosting │ localhost:5000 │ n/a │ ├────────────────┼────────────────┼─────────────────────────────────┤ │ Pub/Sub │ localhost:8085 │ n/a │ ├────────────────┼────────────────┼─────────────────────────────────┤ │ Storage │ localhost:9199 │ http://localhost:4000/storage │ └────────────────┴────────────────┴─────────────────────────────────┘ Emulator Hub running at localhost:4400 Other reserved ports: 4500 Issues? Report them at https://github.com/firebase/firebase-tools/issues and attach the *-debug.log files. ちゃんと起動した????? エミュレータの起動確認 ブラウザから、http://localhost:4000/にアクセスする。 全部起動してる! 終了するときは、TerminalでCtrl + cで終了します。 まとめ。所感とか。 最初から環境構築をする手順をまとめとくと良かったといつも思う。 今回は、その反省も踏まえてまとめてみた。 失敗したことやエラーになったこともちゃんと残しておくのは大事だと思う。 firebase init実行時の選択オプションなんかは、詳しく書かなかった(メンドウダッタ) 使いたい環境によって読み替えて設定すればいいと思う。TypeScript/JavaScriptの選択は好みや要件が絡むところだろうから。 基本的には、デフォルトで<Enter>バンバン叩いて勧めていけば問題ない感じだった。 試行錯誤しながらハンズオンでやってみたから、手順が変なところとかあるかもしれないので、指摘があれば修正していきたいと思います。 LGTM10件来たら続編でも書こうかな。 参考 Local Emulator Suite のインストール、構成、統合 Firebase CLI リファレンス 改訂履歴 2021.09.14 初版
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Node.jsでJsonを操作する

Nodebrewで入れたNode.js 14.xです。 できるだけ環境に依存しないような標準のモジュールで頑張ろうと思ってる。 NodeでJson(文字列)から特定の値を取り出す方法 以下はstatusキーの200を文字列として取り出す方法。 jsonobj.js //API Response sample let apiresult = '{"header": {"status": "200","error_message": ""},"response": {}}' let json_obj = JSON.parse(apiresult) console.info("オブジェクトすべて:", json_obj) // パターン1 // 型チェック console.info(typeof(json_obj.header.status)); // 出力 console.info(json_obj.header.status); // パターン2 // 型チェック console.info(typeof(json_obj["header"]["status"])); // 出力 console.info(json_obj["header"]["status"]); $ node jsonobj.js オブジェクトすべて: { header: { status: '200', error_message: '' }, response: {} } string 200 string 200 Nodeって難しいですね。 書き方色々あるし、いまいち基本的な使い方が解らない。 なのでそういうナレッジを書き溜めていこう
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Node.js/Express+TypeScriptでWeb API作成

はじめに Node.js + Express + TypeScript を使用して Web API を作成する。Express を扱うのは始めてのため、公式サイトを参考に進める。実行環境は以下。 Node.js: 14.17.6 npm: 6.14.15 Express: 4.17.1 TypeScript: 4.4.3 TypeScript 環境の作成 まず TypeScript 環境を作成する。 $ npm init -y $ npm install -D typescript @types/node ts-node $ npx tsc --init Express のインストール つづいて Express とその型定義をインストールする。 $ npm install express $ npm install -D @types/express Hello World Example 以下コードで簡単な実行を試すことができる。 app.ts import express from 'express'; const app = express(); const port = 3000; app.get('/', (req, res) => { res.send('Hello World!'); }) app.listen(port, () => { console.log(`Example app listening at http://localhost:${port}`) }) 実行確認 上記で作成した app.ts を実行する。 $ npx ts-node app.ts Example app listening at http://localhost:3000 別のターミナルを立ち上げて以下コマンドを実行すると、Hello, World! が返ってくることが確認できる。 $ curl http://localhost:3000 おわりに 公式チュートリアルにしたがって、Express の実行を確認した。より詳細な理解を行うため、上記で作成したプログラムではなく、すでにひな形として準備されている express-generator や TypeScript 用に作成されている express-generator-typescript も試してみたい。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Node.js+Express+TypeScriptでWeb API作成

はじめに Node.js + Express + TypeScript を使用して Web API を作成する。Express を扱うのは始めてのため、公式サイトを参考に進める。実行環境は以下。 Node.js: 14.17.6 npm: 6.14.15 Express: 4.17.1 TypeScript: 4.4.3 TypeScript 環境の作成 まず TypeScript 環境を作成する。 $ npm init -y $ npm install -D typescript @types/node ts-node $ npx tsc --init Express のインストール つづいて Express とその型定義をインストールする。 $ npm install express $ npm install -D @types/express Hello World Example 以下コードで簡単な実行を試すことができる。 app.ts import express from 'express'; const app = express(); const port = 3000; app.get('/', (req, res) => { res.send('Hello World!'); }) app.listen(port, () => { console.log(`Example app listening at http://localhost:${port}`) }) 実行確認 上記で作成した app.ts を実行する。 $ npx ts-node app.ts Example app listening at http://localhost:3000 別のターミナルを立ち上げて以下コマンドを実行すると、Hello, World! が返ってくることが確認できる。 $ curl http://localhost:3000 おわりに 公式チュートリアルにしたがって、Express の実行を確認した。より詳細な理解を行うため、上記で作成したプログラムではなく、すでにひな形として準備されている express-generator や TypeScript 用に作成されている express-generator-typescript も試してみたい。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む