- 投稿日:2021-04-03T22:11:07+09:00
yarn(や〜ん)について
Railsチュートリアルで学んだyarnについて簡単にまとめてみました。 yarnとは何? 2016年にリリースされたNode.jsのパッケージマネージャです。 npmと同じくpackage.json、node_modules/で構成されています。 yarnの主な特徴・メリット ・インストールが速い ・セキュリティが高い ・npmの上位互換とされる ・npmと同じような感覚で使える ・npmと一緒に使える パッケージマネージャとは何? パッケージマネージャとは、コンピュータに何のソフトウェアがインストールされたかを記録し、新しいソフトウェアのインストールや新しいバージョンへのソフトウェアの更新、以前インストールした古いソフトウェアの削除を簡単に行えるようにするためのプログラムのことです。 パッケージというのは、どこかの誰かが作ってくれた便利ツールのことです。 アプリケーションを作るときには、便利なパッケージをいくつか組み合わせて開発をするというわけですね。 Node.jsの2大パッケージ管理ツールこそ、npmとyarnです。 yarnのインストール方法 $ npm install -g yarn このコマンドを入力し、バージョンを確認できたら、インストール完了です。 $ yarn -v 1.16.0 npmとyarnどちらを使うべき? 好きな方を使えばいいかと思います。 開発してるときに、パッケージマネージャーを使ってる時間なんてそう多くないし、全体の工程からすると、ほんの少しだけです。 インストールの速さとかも別に気にするポイントではないのかなと・・・。 チームで開発するときにも、ある人はnpm。 またある人はyarn。 これでOKだと思います。 ちなみに僕は、Railsチュートリアルで最初に学んだのがyarnだったので、や〜んながらyarnを使ってます。 ここまで偉そうに書いておきながら、npmは一度しか使ったことありません(笑)
- 投稿日:2021-04-03T22:09:27+09:00
Google Cloud TranslationのAPIをローカルのNode.jsから試してみる
Google Cloud TranslationのAPIをローカルのNode.jsからアクセスしてみました。 前回(といってももう1年半前ですが)Pythonで試しましたが、今回はNode.jsです。 Cloud Translation APIを有効化 Google Cloudのコンソールにブラウザでアクセスして、使用するプロジェクトのCloud Translation APIを有効化します。 ちなみに、有効化をせずにアクセスすると、以下のようなエラーがでました。 PERMISSION_DENIED: Cloud Translation API has not been used in project xxxxxxxxxxxx before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/translate.googleapis.com/overview?project=xxxxxxxxxxxx then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry. 認証設定 Google Cloud Platformのコンソール画面のIAM & AdminのService accountsというメニューから操作して、Create keyでキーを作成し、JSONファイルをダウンロードしておきます。そのJSONファイルを以下のように環境変数で指定しておきます。 $ export GOOGLE_APPLICATION_CREDENTIALS=$HOME/path/to/key.json パッケージインストール $ npm install @google-cloud/translate ソースコード const { TranslationServiceClient } = require("@google-cloud/translate").v3; const projectId = "xxxxxxxx"; const location = "us-central1"; // 言語判定 async function detectLanguage(text) { const translationClient = new TranslationServiceClient(); const req = { parent: translationClient.locationPath(projectId, location), content: text, mimeType: "text/plain", }; const res = await translationClient.detectLanguage(req); let sourceLang = null; for (const elem of res) { if (elem == null) // なぜかnullがレスポンスに含まれる continue; return elem["languages"][0]["languageCode"]; } } // 翻訳 async function translate(text, sourceLang, targetLang) { const translationClient = new TranslationServiceClient(); const req = { parent: translationClient.locationPath(projectId, location), contents: [text], mimeType: "text/plain", sourceLanguageCode: sourceLang, targetLanguageCode: targetLang, }; const res = await translationClient.translateText(req); for (const elem of res) { if (elem == null) // なぜかnullがレスポンスに含まれる continue; return elem["translations"][0]["translatedText"]; } } async function sample(text) { console.log("original: " + text); // 言語の判定 const sourceLang = await detectLanguage(text); // 翻訳 for (const targetLang of ["en", "ja", "zh-TW", "zh-CN", "ko"]) { if (targetLang == sourceLang) // Target language can't be equal to source language. というエラーを防ぐため continue; const targetText = await translate(text, sourceLang, targetLang); console.log(targetLang + ": " + targetText); } console.log(); } const texts = [ "Hello, world!", "Firebase is Google's mobile platform that helps you quickly develop high-quality apps and grow your business.", "Vue是一套用于构建用户界面的渐进式框架。", ]; async function main() { for (const text of texts) { await sample(text); } } main().catch(err => { console.log(err); }); 実行結果 $ node sample.js original: Hello, world! ja: こんにちは世界! zh-TW: 你好世界! zh-CN: 你好世界! ko: 안녕, 세상! original: Firebase is Google's mobile platform that helps you quickly develop high-quality apps and grow your business. ja: Firebaseは、高品質のアプリをすばやく開発してビジネスを成長させるのに役立つGoogleのモバイルプラットフォームです。 zh-TW: Firebase是Google的移動平台,可幫助您快速開發高質量的應用程序並發展業務。 zh-CN: Firebase是Google的移动平台,可帮助您快速开发高质量的应用程序并发展业务。 ko: Firebase는 고품질 앱을 빠르게 개발하고 비즈니스를 성장시키는 데 도움이되는 Google의 모바일 플랫폼입니다. original: Vue是一套用于构建用户界面的渐进式框架。 en: Vue is a progressive framework for building user interfaces. ja: Vueは、ユーザーインターフェイスを構築するための進歩的なフレームワークです。 zh-TW: Vue是一套用於構建用戶界面的漸進式框架。 ko: Vue는 사용자 인터페이스 구축을위한 진보적 인 프레임 워크입니다. 参考 JavaScript用のGoogle Cloud Translation APIレファレンスはこちら。 TranslationServiceClient - Documentation
- 投稿日:2021-04-03T17:22:06+09:00
Express + Node.jsを使ったアプリ開発をTypeScriptで行う
Express + Node.jsを使ったアプリ開発をTypeScriptで行う Azure使ったWebアプリを作ろうと思い立ち このクイックスタートに沿って始めたが、型がない状態での開発に堪えられず、TypeScriptを導入することにしました。 1. まずはJavaScriptで クイックスタートに沿ってExpressアプリを構築していく > npx express-generator myExpressApp --view ejs --git --view ejsの部分はクイックスタートでは--view pugとなっているが、これはhtmlを構成するテンプレートエンジンを選んでいる。僕は個人的に使いやすそうなのでejsにしました。 この辺の比較は こちらが参考になりそうです。 とりあえずそのまま動かしてみます。新しく作成されたディレクトリに移動して > npm install > npm start http://localhost:3000/をブラウザで開くと、無事↓のように表示されました。 2. TypeScript化 まず最初に、TypeScriptとExpressの型定義をインストールします。 > npm install -D typescript > npm install -D @types/exrpess 無事インストールできたら > npx tsc --init を実行するとtsconfig.jsonが作成されます。 tsconfig.json { "compilerOptions": { "target": "es6", "module": "commonjs", "outDir": "./dist", "strict": true, "esModuleInterop": true, "allowJs": true } } とりあえず、こんな感じにしました。途中のプロジェクトとかだと、いきなり全部tsにするのも大変なので、allowJs:trueにしておきました。 まずapp.jsからTypeScript化していきます。名前をapp.tsに変更して app.ts import createError from 'http-errors'; import express from "express"; import {Request, Response, NextFunction} from 'express'; import path from 'path'; import cookieParser from 'cookie-parser'; import logger from 'morgan'; var indexRouter = require('./routes/index'); var usersRouter = require('./routes/users'); var app = express(); // view engine setup app.set('views', path.join('views')); 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('public'))); app.use('/', indexRouter); app.use('/users', usersRouter); // catch 404 and forward to error handler app.use(function(req: Request, res: Response, next: NextFunction) { next(createError(404)); }); // error handler app.use(function(err: any, req: Request, res: Response, next: NextFunction) { // set locals, only providing error in development res.locals.message = err.message; res.locals.error = req.app.get('env') === 'development' ? err : {}; // render the error page res.status(err.status || 500); res.render('error'); }); module.exports = app; こんな感じになりました。ほとんどそのままですが、型の追加、importあたりが変わっています。 ここでimportしているパッケージの型定義も必要になるのでインストールします。 > npm install -D @types/cookie-parser @types/morgan @types/http-errors 基本的にTypeScript関連で必要にあるものをインストールするときは、devDependenciesに入れるので-Dオプションを付けています。 あと、__dirnameを抹消しました。コンパイルされたjsファイルはdistディレクトリ下に置かれるので、相対パスの取り方が変わるからです。そこで、bin/wwwも変更する必要があります。 bin/www // var app = require('../app'); ←変更前 var app = require('../dist/app'); dist配下のappを指定しなければいけません。 > npx tsc でコンパイルしてみます。 http://localhost:3000/にアクセスしてみて問題なければ成功です。これでじゃんじゃんTypeScript化を進めることができます。 3. まとめとおまけ これで快適にwebアプリ開発が進められそうです。 ちなみに npx tsc --watch と--watchを付けることで、ファイルに変更があると自動でコンパイルしてくれます。 nodemonとかと組み合わせるともっと快適になりそうです。 2021/04/05:追記 package.json "scripts": { "dev": "rd /s /q dist & tsc-watch --noClear --onSuccess 'node ./bin/www'", "start": "node ./bin/www" } のようにすることで、npm run devコマンドで自動コンパイルしながらnodeを起動しなおしてくれる快適な環境にできました。←tsc-watchのインストールが必要です > npm install -D tsc-watch !!注意!! 僕の環境がwindowsなので、rdコマンドを用いていますが、MacやLinuxの場合は package.json "scripts": { "dev": "rm -rf dist & tsc-watch --noClear --onSuccess 'node ./bin/www'", "start": "node ./bin/www" } のような感じになると思います。 参考
- 投稿日:2021-04-03T16:56:17+09:00
Puppeteer(パペティア)でcsvファイルをダウンロードする
概要 ずっとやりたかったPuppeteerでファイルダウンロードをやってみた。 世の中いろんなやり方が書かれていて混乱したが、やってみたら簡単にできたので共有。 参考 - 公式issue https://github.com/puppeteer/puppeteer/issues/299 環境 Ubuntu18.04 Node.js v14.15.1 Puppeteer 8.0.0 ソース 意外と普通にできました! 何か罠が待っている気もしなくもなく。 const puppeteer = require('puppeteer'); const fs = require('fs'); const path = require("path"); (async () => { const browser = await puppeteer.launch(); const page = await browser.newPage(); const width = 1980; const height = 1080; await page.setViewport({ width, height }); await page.goto('https://portal.local/'); // gotoの後は、waitしなくてよさげ // ファイルダウンロード const downloadPath = path.resolve('/hoge/fuga/'); // ここは絶対に絶対パス await page._client.send('Page.setDownloadBehavior', { behavior: 'allow', downloadPath: downloadPath }); // csvダウンロードボタンクリック await page.click('.csvDownload'); await page.waitForTimeout(5000); await browser.close(); })(); 参考
- 投稿日:2021-04-03T15:18:51+09:00
最近のNode.jsでBLEのアドバタイジングパケットを補足してみる
はじめに nobleが虫の息というか、もう息の根を止めてあげていい状態なので他に移行しなければならない。さて次は何がいいだろうかと調べてみてラズパイ用途で行き当たるのは以下の2つくらいじゃないだろうか。 node-ble node-bluez node-bleが開発が活発に見えるが、node-bluezもシンプルでよさげ。両方やってみることにする。試した環境はこちら uname -a Linux raspberrypi.local 5.8.0-1016-raspi #19-Ubuntu SMP PREEMPT Tue Feb 9 20:12:43 UTC 2021 aarch64 aarch64 aarch64 GNU/Linux 前準備 node-bluzが依存するライブラリとnode.jsをインストールする sudo apt install -y libglib2.0-dev libdbus-1-dev sudo apt install -y nodejs npm ここで入るnodeのバージョンは12.xで古い。nで最新を入れる sudo npm i -g n sudo n latest sudo apt remove --purge nodejs sudo apt autoremove /usr/local/bin にlatest versionが入るのでパスを通しておく ~/.profile export "$HOME/usr/local/bin:$PATH" source ~/.profile node -v v15.13.0 TypeScriptの準備 mkdir -p work && cd $_ npm init -f npm i -S typescript ts-node bluez node-ble sleep-promise npx tsc --init Promiseまわりのエラー回避で tsconfig.jsonのlibを以下にする tsconfig.json lib: ["ES2015", "dom"], node-bleでやってみた node-ble.ts import { createBluetooth } from 'node-ble'; import sleep from 'sleep-promise'; const main = async () => { const { bluetooth, destroy }: any = createBluetooth(); const adapter: any = await bluetooth.defaultAdapter(); if (! await adapter.isDiscovering()) { console.log('Discovering:'); await adapter.startDiscovery(); while (true) { const devices: any = await adapter.devices(); console.log(devices); await sleep(3000); } } }; main(); 実行する npx ts-node node-ble.ts Discovering: [ '4E:2E:8E:F1:63:91', 'E1:A6:93:4A:ED:C3' ] [ '4E:2E:8E:F1:63:91', '5B:A0:F3:EB:BB:0D', 'E1:A6:93:4A:ED:C3' ] [ '4E:2E:8E:F1:63:91', '5B:A0:F3:EB:BB:0D' ] [ '4E:2E:8E:F1:63:91', '5B:A0:F3:EB:BB:0D' ] [ '4E:2E:8E:F1:63:91', '5B:A0:F3:EB:BB:0D', 'E1:A6:93:4A:ED:C3' ] [ '4E:2E:8E:F1:63:91', '5B:A0:F3:EB:BB:0D', 'E1:A6:93:4A:ED:C3' ] とれた。Adapterにイベントハンドラがないので Adapter.device()を一定時間ループで回してとる手法。nobleでイベントドリブンな取得に慣れていたのでこれ思いつかなくてしばらくハマった。一回device()しただけじゃ絶対とれないし。これはこれでコールバックじゃない分制御の汎用性は高いと思うけど。好みの問題だろうか。 node-bluezでやってみた node-bluez.ts import Bluez from 'bluez'; const bluetooth = new Bluez(); const handleOn = async (address: string, props: any) => { console.log(`[NEW] Device: ${address}, ${JSON.stringify(props.Name)}`); const dev = await bluetooth.getDevice(address); dev.on("PropertiesChanged", (props) => { console.log(`[CHG] Device: ${address}, ${props}`); }); }; const main = async () => { try { bluetooth.on('device', handleOn); await bluetooth.init(); const adapter = await bluetooth.getAdapter(); await adapter.StartDiscovery(); console.log("Discovering"); } catch (e) { throw new Error(e); } } main(); 実行する npx ts-node node-bluez.ts Discovering [NEW] Device: 54:AB:0C:6F:15:E6, undefined [NEW] Device: E1:A6:93:4A:ED:C3, "tkr" [NEW] Device: D1:1C:63:61:E0:01, undefined [NEW] Device: E1:A6:93:4A:ED:C3, "tkr" [NEW] Device: FB:33:A1:32:7F:75, "tkr" [NEW] Device: E1:A6:93:4A:ED:C3, "tkr" とれた。こちらはAdaptorにイベントハンドラがあって受信したタイミングでパケットが降ってくる。tkrは財布とかに入れてある忘れ物防止タグのTrackRだ。 おわりに どちらもざっくりコードをみるかぎりDbus APIのラッパーな感じ。アドバタイジングとるだけならイベントドリブンなnode-bluezのほうが好み。ケースバイケースで使っていこうと思う。やっとnobleの葬式ができそう。
- 投稿日:2021-04-03T10:08:24+09:00
PC購入後の環境設定(Mac Catalina)
シェルの設定
# zshをデフォルトに設定 % chsh -s /bin/zsh # ログインシェルを表示 % echo $SHELL # 以下のように表示されれば成功 /bin/zshCommand Line Tools
% xcode-select --installHomebrew
% cd # ホームディレクトリに移動 % pwd # ホームディレクトリにいるかどうか確認 % /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)" # コマンドを実行 # PasswordはMacのパスワード# 最新状態に % brew update # 権限の変更 % sudo chown -R `whoami`:admin /usr/local/binRuby
# rbenvとruby-buildをインストール % brew install rbenv ruby-build rbenvのパスを通す % echo 'eval "$(rbenv init -)"' >> ~/.zshrc # zshrcの変更を反映 % source ~/.zshrcreadlineインストール % brew install readline % brew link readline --force# Rubyインストール % RUBY_CONFIGURE_OPTS="--with-readline-dir=$(brew --prefix readline)" % rbenv install 2.6.5 # Rubyバージョン指定 % rbenv global 2.6.5 # rbenvを読み込んで変更を反映 % rbenv rehashMySQL
% brew install mysql@5.6 # 自動起動設定 % mkdir ~/Library/LaunchAgents % ln -sfv /usr/local/opt/mysql\@5.6/*.plist ~/Library/LaunchAgents % launchctl load ~/Library/LaunchAgents/homebrew.mxcl.mysql\@5.6.plist # パスを通す % echo 'export PATH="/usr/local/opt/mysql@5.6/bin:$PATH"' >> ~/.zshrc # mysqlのコマンドを実行できるようにする設定 % source ~/.zshrc # 設定を読み込むコマンド % which mysql # mysqlのコマンドが打てるか確認する # 起動確認 % mysql.server status # MySQLの状態を確認するコマンドshared-mime-info
% brew install shared-mime-infoRails
# bundlerインストール % gem install bundler --version='2.1.4' # Railsインストール % gem install rails --version='6.0.0' % rbenv rehashNode.js
brew install node@14 # パスを通す % echo 'export PATH="/usr/local/opt/node@14/bin:$PATH"' >> ~/.zshrc % source ~/.zshrcyarn
% brew install yarn
- 投稿日:2021-04-03T09:50:32+09:00
node-sassで改行出来なかった話
状況
node-sassをインストールして、自動コンパイルさせる設定が上手くいかなかった。
環境
node: v12.13.0
node-sass: 5.0.0実際のコード(抜粋)
main.sass
* { margin: 0; }コンパイル時、ターミナルに表示されたエラー
{ "message": "Invalid CSS after \"* {\": expected \"}\", was \"{\"", "formatted": "Error: Invalid CSS after \"* {\": expected \"}\", was \"{\"\n on line 1 of public/sass/main.sass\n>> * { {\n\n ---^\n" }ええ…sassファイル内で改行出来ないの…
解決策
問題点:ファイルの拡張子が違った
元々
hoge.sassにしていたものをhoge.scssに変更したら出来ました。
scssファイルにしなければいけないんですね。知りませんでした。番外編:それでも別のエラーが出た
Error: EROFS: read-only file system, mkdir '/style.css'解決策:予めstyle.cssを作成するとエラーが出る。コンパイル後に生成されるstyle.cssを使えば問題なし。
- 投稿日:2021-04-03T09:03:01+09:00
仕様が変わった exchangeratesapi.io の古いAPIと互換性をもたせるnpmパッケージを作ってみた
背景
無料かつ登録不要で使える為替取得APIとして exchangeratesapi.ioが有名でしたが、ついに2021年4月から登録必須&一分機能は有料のサブスクリプション契約が必須になってしまいました。
新しいAPIの詳細については、 有料になった exchangeratesapi.io を無料の範囲内で使い倒すnpmパッケージを作ってみた の冒頭で触れているので、そちらを参照してください。
今回の変更に伴って、
APIの仕様やレスポンスの形式も若干変わったのが微妙なポイント。
すでに組み込み済みのシステムの事を考えて、互換性を保つためにWrapperが欲しいケースもありそうだなと思い、作ってみました。古いAPIと互換性をもたせるnpmパッケージを作ってみた
exchangeratesapi.io の仕様変更で頭を抱えている方の一助になれば幸いです。
以下、READMEの日本語訳
インストール
# via npm npm install --save @ittkm/exchangeratesapi-wrapper # via yarn yarn add @ittkm/exchangeratesapi-wrapper使い方
import exchangeratesapi from "@ittkm/exchangeratesapi-wrapper"; // // または // const exchangeratesapi = require("@ittkm/exchangeratesapi-wrapper").default; // https://exchangeratesapi.io/ で取得したAPIキーに書き換えてください const API_KEY = "1234567890abcdefghijklmnopqrstuv"; async function exchangeratesapiSamples() { // API Keyを使って初期化 const api = new exchangeratesapi(API_KEY); // 好きなAPIを呼び出す const exchangeRates = await api.latest(); console.dir(exchangeRates); }使用例
/** * 最新レート取得 (初期設定) * - 基準通貨: EUR * - 対象通貨: すべて * - 旧APIの場合: * GET https://api.exchangeratesapi.io/latest */ console.dir(await api.latest()); /** * 最新レート取得 * - 基準通貨: USD * - 対象通貨: GBP, JPY, EUR * - 旧APIの場合: * GET https://api.exchangeratesapi.io/latest?base=USD&symbols=GBP,JPY,EUR */ const latestParameters = { base: "USD", symbols: ["GBP", "JPY", "EUR"], }; console.dir(await api.latest(latestParameters)); /** * 日付指定で取得 * - 日付: 2013-12-24 * - 基準通貨: GBP * - 対象通貨: USD, CAD, EUR * - 旧APIの場合: * GET https://api.exchangeratesapi.io/2013-12-24?base=GBP&symbols=USD,CAD,EUR */ const historicalParameters = { date: "2013-12-24", base: "GBP", symbols: ["USD", "CAD", "EUR"], }; console.dir(await api.historical(historicalParameters)); /** * 時間枠指定で取得 (最小オプション) * - 対象通貨: すべて * - 旧APIの場合: * GET https://api.exchangeratesapi.io/history?start_at=2012-05-01&end_at=2012-05-03 */ console.dir( await api.history({ start_at: "2012-05-01", end_at: "2012-05-03", }) ); /** * 時間枠指定で取得 (通過指定) * - 基準通貨: EUR * - 対象通貨: USD, AUD, CAD * - 旧APIの場合: * GET https://api.exchangeratesapi.io/history?start_at=2012-05-01&end_at=2012-05-03&base=EUR&symbols=USD,AUD,CAD */ const timeseriesParameters = { start_at: "2012-05-01", end_at: "2012-05-03", base: "EUR", symbols: ["USD", "AUD", "CAD"], }; console.dir(await api.history(timeseriesParameters));レスポンス
以前の exchangeratesapi.io のレスポンスと互換性があります。
詳細はアーカイブ済みのドキュメントを参照してください。
- 投稿日:2021-04-03T06:59:09+09:00
有料になった exchangeratesapi.io を無料の範囲内で使い倒すnpmパッケージを作ってみた
背景
無料かつ登録不要で使える為替取得APIとして exchangeratesapi.ioが有名でしたが、ついに2021年4月から登録必須&一分機能は有料のサブスクリプション契約が必須になってしまいました。
2021/04/03時点で、月額費用と使える機能は以下のようになっているようです。
個人的な記憶を頼りに、2021年3月まで無料で使えた機能についても並べています。
機能 〜2021年3月
無料機能FreeBasic Professional Business 月額 無料 無料 $10 $40 $80 月間リクエスト数 無制限 250
1,00010,000 100,000 500,000 更新頻度 1日 1日 1時間 10分 60秒 履歴取得 ✅ ✅ ✅ ✅ ✅ HTTPS対応 ✅ ✅ ✅ ✅ 基準通貨の切り替え ✅ ✅ ✅ ✅ 為替換算 ✅ ✅ ✅ 時間枠クエリ ✅ ✅ 為替変動クエリ ✅ サポート なし 限定的 Basic Premium Premium 新たなFreeプランの場合、
リクエスト数は大きく制限されますが、1日1回しか更新されないデータなので250回/月もあれば、個人利用やちょっとしたバッチ処理程度であれば十分に感じます。
なお、PricingページにはFreeプランの月間リクエスト数は250ですが、登録後のダッシュボード画面では1,000になっていました。
HTTPS対応していない点については、もともとオープンデータを取得するだけのAPIなので大きなリスクにはならないように思います。ただ、APIキーが丸見えになるので、APIキーを抜かれて勝手に使われる可能性くらいは頭に入れておく必要があります。次の
基準通貨の切り替えができなくなる点ですが、個人的にはこれが最もインパクトの大きい変更だと感じています。EU中央銀行のデータを使っていることもあり、このAPIのデフォルトの基準通貨はEURです。
たとえばUSDとJPYのレートを取りたい!という時、従来はUSD基準のJPYのレートを取得というリクエストができたものの、新たなFreeプランではEUR基準のUSDとJPYのレートを取得というリクエストを投げた上で、自ら換算処理を行う必要が出てきます。
これが面倒くさい場合は、$10以上課金してBasicプランにする必要があります。また、為替換算についても、無料枠のAPIで取得したデータを使って自分で換算処理を実装することで、対応可能です。
無料枠のAPIだけで有料相当の機能を作ってみた
ということで、新たなAPIについて考察していると、かなり機能は制限されたことはわかりましたが、無料枠のAPIにちょっとした換算ロジックを組み合わせると、有料で提供されている機能に相当するものを作れるような気がしてきたので、Node.jsで実際に作ってみました。
このパッケージで網羅している機能を、先程の一覧表に反映するとこんな感じです。
機能 〜2021年3月
無料機能NEWFree Basic Professional Business 月額 無料 無料 無料 $10 $40 $80 月間リクエスト数 無制限 ≦ 250
≦ 1,000250
1,00010,000 100,000 500,000 更新頻度 1日 1日 1日 1時間 10分 60秒 履歴取得 ✅ ✅ ✅ ✅ ✅ ✅ HTTPS対応 ✅ ✅ ✅ ✅ 基準通貨の切り替え ✅ ✅ ✅ ✅ ✅ 為替換算 ✅ ✅ ✅ ✅ 時間枠クエリ ✅ ✅ ✅ 為替変動クエリ ✅ サポート なし なし 限定的 Basic Premium Premium さすがに、HTTPS対応はAPI側の話なので対応できませんが、従来の無料機能よりも多機能になりました。
月間リクエスト数が少し減るのは、時間枠クエリを使用する場合です。(裏で指定された日数分のAPIリクエストを行うため)なお、レスポンスは有料版のAPI仕様に合わせてあるため互換性があります。
exchangeratesapi.io の有料化で頭を抱えている方の一助になれば幸いです。
以下、READMEの日本語訳
インストール
# via npm npm install --save @ittkm/exchangeratesapi # via yarn yarn add @ittkm/exchangeratesapi使い方
import exchangeratesapi from "@ittkm/exchangeratesapi"; // // または // const exchangeratesapi = require("@ittkm/exchangeratesapi").default; // https://exchangeratesapi.io/ で取得したAPIキーに書き換えてください const API_KEY = "1234567890abcdefghijklmnopqrstuv"; async function exchangeratesapiSamples() { // API Keyを使って初期化 const api = new exchangeratesapi(API_KEY); // 好きなAPIを呼び出す const exchangeRates = await api.latest(); console.dir(exchangeRates); }使用例
/** * 最新レート取得 (初期設定) * - 基準通貨: EUR * - 対象通貨: すべて */ console.dir(await api.latest()); /** * 最新レート取得 * - 基準通貨: USD * - 対象通貨: GBP, JPY, EUR */ const latestParameters = { base: "USD", symbols: ["GBP", "JPY", "EUR"], }; console.dir(await api.latest(latestParameters)); /** * 日付指定で取得 (初期設定) * - 日付未指定で最新レートを取得 * - 基準通貨: EUR * - 対象通貨: すべて */ console.dir(await api.historical({})); /** * 日付指定で取得 * - 対象日: 2013-12-24 * - 基準通貨: GBP * - 対象通貨: USD, CAD, EUR */ const historicalParameters = { date: "2013-12-24", base: "GBP", symbols: ["USD", "CAD", "EUR"], }; console.dir(await api.historical(historicalParameters)); /** * 為替換算 (最新レート) * - GBP から JPY * - 数量: 25 */ console.dir( await api.convert({ from: "GBP", to: "JPY", amount: 25, }) ); /** * 為替換算 (過去のレート使用) * - GBP から JPY * - 数量: 25 * - 対象日: 2018-02-22 */ const convertParameters = { from: "GBP", to: "JPY", amount: 25, date: "2018-02-22", }; console.dir(await api.convert(convertParameters)); /** * 時間枠指定で取得 (最小オプション) * - 対象通貨: すべて */ console.dir( await api.timeseries({ start_date: "2012-05-01", end_date: "2012-05-03", }) ); /** * 時間枠指定で取得 (通過指定) * - 基準通貨: EUR * - 対象通貨: USD, AUD, CAD */ const timeseriesParameters = { start_date: "2012-05-01", end_date: "2012-05-03", base: "EUR", symbols: ["USD", "AUD", "CAD"], }; console.dir(await api.timeseries(timeseriesParameters));レスポンス
exchangeratesapi.ioのレスポンスと互換性があります。
詳細は公式ドキュメントを参照してください。
- 投稿日:2021-04-03T01:51:03+09:00
Node.jsでの標準入力の書き方をまとめてみた
初めに
最近になって、あらためて Node.jsでatCoderやLeetCodeでアルゴリズム問題を解いてみようと思ったのですが、Node.jsの標準入力の書き方は調べてみると色々あってどれがいいんだろうと少し迷ったので、いい機会なのでまとめてみました。
Node.js(標準入力)のパターン
1.
/dev/stdinをreadFileSyncを利用して読み込むinput1.jsvar input = require("fs").readFileSync("/dev/stdin", "utf8"); console.log(input);標準入力を表す特殊ファイル(
/dev/stdin)をreadFileSyncで読み込みます。メリット : 記述がかなりシンプル
デメリット:/dev/stdinはUNIX系のOSでしか利用できないので、Windowsでは利用できない2.
process.stdinをreadlineモジュールを利用して読むinput2.jsprocess.stdin.setEncoding("utf8"); var lines = []; var reader = require("readline").createInterface({ input: process.stdin, }); reader.on("line", (line) => { //改行ごとに"line"イベントが発火される lines.push(line); //ここで、lines配列に、標準入力から渡されたデータを入れる }); reader.on("close", () => { //標準入力のストリームが終了すると呼ばれる console.log(lines); });改行単位で
Readable streamを処理できるreadlineモジュールを利用してデータを読み込みます。メリット : 改行単位で読み込めるので、1行ごとで処理を行う場合や一度に処理するにはインプットが大きすぎる場合には便利。
デメリット: 改行ごとの処理と読み込み完了後の処理に分かれるため、記述量が多い。3.
process.stdinをfor await...ofを利用して読むinput3.js(async () => { const buffers = []; for await (const chunk of process.stdin) buffers.push(chunk); const buffer = Buffer.concat(buffers); const text = buffer.toString(); console.log(text); })()標準入力を非同期のイテラブルなオブジェクトとしてループで処理します。
メリット : 分割して読み込みされるので、一度に処理しきれないインプットを扱う場合には便利。
デメリット: 文字列への変換が必要なので、やや記述量が多い。またデータが分割される場所をコントロールしにくい。おわりに
Windowsを利用していない場合やインプットが多くない場合は1の方法を利用して、それ以外のケースでは必要に応じて2または3の方法で記述するのが良さそうだなと思いました。
それでは素敵なNode.jsライフを。
参考リンク