- 投稿日:2021-01-14T22:34:52+09:00
【基礎の基礎編】React + Express + GraphQLでHello World!
はじめに
この記事は、GraphQLの基礎の基礎として、サーバーサイドとクライアントサイドを最低限実装し、GraphQLで接続する機能を実装しています。データの更新や、DB接続してデータを取得したりなどは行っていません。GraphQLでクライアントとサーバーの連携の概要を理解していただければと思います。
source
https://github.com/kyokozuka/react-node-express-graphql
GraphQLとは
GraphQLはAPIのための問い合わせ言語です。クエリを実行してデータを呼び出すためのランタイムをさすこともあります。
GraphQLの問い合わせ言語
GraphQLは、データベースに問い合わせるために開発されたSQLの考え方をインターネットに適用したものです。単一のGraphQLのクエリを使い、関連するデータをまとめて取得できます。また、データを変更したり削除したりすることもできます。SQLとGraphQLは問い合わせ言語という点で共通しています。
ただ、どちらも問い合わせ言語ではあるものの、GraphQLとSQLは全く異なるものです。
- SQLのクエリはデータベースに対して実行される(データベースのための問い合わせ言語)
- GraphQLのクエリはAPIに対して実行される(インターネットのための問い合わせ言語)
構文
- SQL: SELECT, GraphQL: Query
- SQL: INSERT/UPDATE/DELETE, GraphQL: Mutation
GraphQLはインターネットのための問い合わせ言語なので、ソケット通信を使ってデータの変更を監視するためのコマンドも用意されている。そのコマンドを"Subscription"と呼びます。
GraphQLサーバーの実装
まず、サーバーサイドとして、簡単な(Hello Worldを表示する)APIを作成してみましょう。
今回はApollo ServerはOSSで、非常にシンプルにセットアップできる上、本番環境に投入できる水準の機能を多く提供しています。
代表的な機能としては、サブスクリプションのサポート、ファイルのアップロード、既存のサービスのAPIからデータを取得する機能、クラウドサービスのApollo Engineがあります。環境構築
ディレクトリ構成
myappServer/ ├ node_modules ├ src ├ resolbers ├ index.js └ Query.js ├ index.js └ typeDefs.graphql └ package.json$ npm init -y // 初期化処理 $ npm install express apollo-server-express graphql-playground-middleware-express // モジュールのインストールプログラム
index.jsconst express = require("express"); const { ApolloServer } = require('apollo-server-express'); const { readFileSync } = require('fs'); const resolvers = require('./resolvers'); const typeDefs = readFileSync('./src/typeDefs.graphql', 'utf-8'); const app = express(); const server = new ApolloServer({typeDefs, resolvers}); server.applyMiddleware({ app }) app.get('/', (req, res) => res.end(`Welcome to the PhotoShare API`)) app.listen({ port: 4000 }, () => console.log(`GraphQL Server runnning @ http://localhost:4000{server.graphqlPath}`))typeDefs.graphqltype Query { helloWorld: String! }resolvers/index.jsconst Query = require('./Query'); const resolvers = { Query } module.exports = resolversresolvers/Query.jsmodule.exports = { helloWorld: () => 'Hello World' }サーバーの起動
$ cd myappServer $ node src/index.js >>> GraphQL Server runnning @ http://localhost:4000{server.graphqlPath}GraphQLクライアントの実装
次に、先ほど作成したGraphQLサーバーにクライアントからアクセスしてみましょう。
reactプロジェクトの作成
$ create-react-app myappClientインストール
$ npm install graphql graphql-request
- graphql-request
GraphQLオペレーションをAPIに送信するために利用できるフレームワークがいくつか存在します。その中で有名な"graphql-request"を使用します。これはfetchリクエストをPromiseでラップして、GraphQLサーバーへのリクエストを送信します。また、リクエストの構築とデータのパースに関する細かい処理を引き受けます。
プログラム
作成されたApp.jsを下記のように書き換えてください。
App.jsimport { useState } from 'react'; import './App.css'; import { request } from 'graphql-request' const url = 'http://localhost:4000/graphql' const query = ` query helloWorld { helloWorld } ` function App() { const [hello, setHello] = useState(''); const graphQL = async () => { const result = await request(url, query) setHello(result.helloWorld) } return ( <div className="App"> <button onClick={graphQL}>Click</button> <div>{hello}</div> </div> ); } export default App;request()でurlとqueryを引数として受け取ってサーバーへのリクエストを構築して、結果のデータを返します。ここでレスポンスされるデータは期待した通りのJSONデータ { "helloWorld": "Hello World" } がレスポンスされます。
サーバーの起動
$ cd myappClient $ npm startサーバーが起動したら、ブラウザが立ち上がり、「click」ボタンをクリックすると「Hello World!」と表示されるはずです。
- 投稿日:2021-01-14T20:41:41+09:00
Node.jsとMySQLの接続方法
mysqlモジュールをインストールする
Node.jsでMySQLに接続するには、mysqlモジュールを使います。
以下のコマンドを実行してインストールします。$ npm install mysqlMySQLに接続する
MySQLを操作するjsファイルに追記していきます。
今回はMySQLを操作するjsファイルをmain.js
とします。
main.js
に以下を追加して、mysqlのモジュールをインポートします。main.jsconst mysql = require('mysql');次に、
createConection
メソッドで、使用するMySQLのデータベースとの接続の指定をし、connection
を作成します。(MySQLでデータベースを作成しておきましょう。)main.jsconst connection = mysql.createConnection({ host: 'localhost', user: 'root', password: 'password', database: 'database-name' });クエリを実行する
データベースからデータを引出します。
例えば以下のようになります。main.jsconnection.query( 'SELECT * FROM database-name', (error, results) => { res.render('list.ejs', { databaseーname: results }); } );MySQLとの接続を切る
最後に
end
メソッドでconnection
によるMySQLとの接続を切ります。main.jsconnection.end();
- 投稿日:2021-01-14T16:44:57+09:00
Node.jsとSeleniumを使ってChoromeのコンソールログを確認する
はじめに
Seleniumを使用してみる機会があったため,備忘録としてまとめようと思い,記事を書きました。
現在主にJavascriptを使用しているため,Node.jsを使ってSeleniumを動かそうと思います。
Node.jsの環境構築に関しては,こちらの手順で行なっています。Seleniumとは
Webブラウザの操作を自動化するためのツール。
今回は,SeleniumをNode.js内で使用して,Webブラウザを操作してみたいと思います。大まかな流れ
- 必要なパッケージ・ドライバのダウンロード
- 実行ファイル作成
- 実行
以下で詳細に説明していきます。
必要なパッケージ・ドライバのダウンロード
selenium-webdriverのインストール
$ npm install --save selenium-webdriver
--save
: package.jsonのdependenciesに追加される各ブラウザのDriverのダウンロード
主要なブラウザで動作させるには,上記のパッケージに加えて以下のドライバをダウンロードし,実行ファイルと同じパスに配置する必要があります.
ブラウザ ドライバ Chrome chromedriver(.exe) Internet Explorer IEDriverServer.exe Edge MicrosoftWebDriver.msi Firefox geckodriver(.exe) Safari safaridriver 今回はChromeを使用するので,上記表中のChromeドライバのページをクリックします.
バージョン86を使用するので,対象バージョンの欄をクリックします.
(どのバージョンのドライバをダウンロードするかはこちらのページを参照)Macのドライバ(chromedriver_mac64.zip)を選択し,ダウンロードします.
zip解凍後,Nodeの実行ファイルと同じパスに配置すれば,準備は完了です.$ cp ~/Downloads/chromedriver .実行ファイル作成
今回は,Qiitaのトップページを開き,コンソールログを取得してみます。
実行ファイルの完成形
selenium_app.js// ライブラリを呼び出す const webdriver = require("selenium-webdriver"); const chrome = require("selenium-webdriver/chrome"); const { Builder } = webdriver; const { Preferences, Type, Level } = require("selenium-webdriver/lib/logging"); // await を使うため,async function 内で処理を記述する (async function () { // オプション付きでブラウザを立ち上げる const capabilities = webdriver.Capabilities.chrome(); const logPrefs = new Preferences(); logPrefs.setLevel(Type.BROWSER, Level.ALL); capabilities.setLoggingPrefs(logPrefs); const options = new chrome.Options(capabilities); const driver = await new Builder().forBrowser("chrome").setChromeOptions(options).build(); // Qiitaのトップページへ遷移 await driver.get("https://qiita.com/"); // 5秒待機 await driver.sleep(5000); // コンソールログの取得 const consoleLogs = await driver.manage().logs().get(Type.BROWSER); for (let i = 0; i < consoleLogs.length; i++) { console.log(consoleLogs[i]); } // 終了 await driver.quit(); })();以下で詳細を説明します.
実行ファイルの作成
まずは新規ファイルを作成します
$ touch selenium_app.jswebdriverを呼び出す
公式のドキュメントを参考に,実行ファイル内でchromeのwebdriverを呼び出します。
また,ログを取得するために必要なwebdriver内のライブラリも呼び出します。selenium_app.js// ライブラリを呼び出す const webdriver = require("selenium-webdriver"); const chrome = require("selenium-webdriver/chrome"); const { Builder } = webdriver; const { Preferences, Type, Level } = require("selenium-webdriver/lib/logging");ブラウザを立ち上げる
コンソールログを取得するためのオプションを指定して,ブラウザを立ち上げます。
selenium_app.js// オプション付きでブラウザを立ち上げる const capabilities = webdriver.Capabilities.chrome(); const logPrefs = new Preferences(); logPrefs.setLevel(Type.BROWSER, Level.ALL); capabilities.setLoggingPrefs(logPrefs); const options = new chrome.Options(capabilities); const driver = await new Builder().forBrowser("chrome").setChromeOptions(options).build();
- 今回はコンソールログを取得するため,ログのタイプを
Type.BROWSER
と指定していますが,デベロッパツールのNetworkの情報を取得したい場合はこの箇所をType.PERFORMANCE
に変更すれば取得することができます。ページ遷移
操作したいページのURLを指定し,そのURLへ遷移させます。
selenium_app.js// Qiitaのトップページへ遷移 await driver.get("https://qiita.com/");コンソールログの取得
selenium_app.js// コンソールログの取得 const consoleLogs = await driver.manage().logs().get(Type.BROWSER);
- こちらも
Type.BROWSER
の箇所をType.PERFORMANCE
にすることで,デベロッパーツールのNetworkの情報を含むログ情報を取得することができます。ドライバーの終了
selenium_app.js// 終了 await driver.quit();実行
実行ファイルが存在しているディレクトリに移動し,以下のコマンドを実行します。
$ node selenium_app.js以下のような結果が表示されれば成功
Entry { level: Level { name_: 'DEBUG', value_: 700 }, message: 'https://qiita.com/ - [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o', timestamp: 1610609498344, type: '' }
参考
- 投稿日:2021-01-14T02:43:49+09:00
Pure JavaScript 高速 SHA-256 ハッシュ実装
SHA-256 のダイジェスト・ハッシュ値を計算する高速なピュア JavaScript 実装のライブラリを npm で公開したので紹介します。
Uint8Array
・Int32Array
を使うことで、このほかのピュア JavaScript 実装のライブラリよりも高速にハッシュ値を計算します。SHA-2 (SHA-256) 版
SHA-256 は、256ビット(16進数64桁)のハッシュ値です。
例:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
→ https://www.npmjs.com/package/sha256-uint8array
const createHash = require("sha256-uint8array").createHash; const text = ""; const hex = createHash().update(text).digest("hex"); // => "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" const data = new Uint8Array(0); const hash = createHash().update(data).digest(); // => <Uint8Array e3 b0 c4 42 98 fc 1c 14 9a fb f4 c8 99 6f b9 24 27 ae 41 e4 64 9b 93 4c a4 95 99 1b 78 52 b8 55>インターフェースは、Node.js の ネイティブの crypto モジュールのサブセットです。
v0.9.0 時点のベンチマーク結果は以下の通り。(macOS 10.15.7 Intel Core i7 3.2GHz)
1KB 程度の JSON と日本語文字列を、各 10,000 回ずつハッシュ計算したときのミリ秒です。
module version node.js V14 Chrome 87 Safari 14 minified backend crypto - 103ms ? - - - OpenSSL sha256-uint8array 0.9.0 274ms 446ms ? 243ms ? 3KB ? Uint8Array crypto-js 4.0.0 805ms 910ms 918ms 108KB Uint8Array jssha 3.2.0 835ms 892ms 913ms 10KB Uint8Array hash.js 1.1.7 635ms 611ms 1,577ms 7KB Array sha.js 2.4.11 356ms 965ms 3,512ms 27KB Buffer create-hash 1.2.0 381ms 1,002ms 3,502ms 97KB Buffer jshashes 1.0.8 1,450ms 2,239ms 1,164ms 23KB Array 1回あたりのハッシュ計算にかかる時間は、マイクロ秒単位なので、いろんな用途で使えそうです。
そのほかの特徴:
- 入力は、文字列または
Uint8Array
- 出力は、16進数文字列または
Uint8Array
- 複数チャンクに分割された入力にも対応しています。
- 絵文字など
U+10000
以降のサロゲートペア、UTF-8 で4バイトの文字に対応しています。- IE11 でも動きます。(IE10 ですら動くようだ)
SHA-1 版
同じインターフェースで SHA-1 版もあります。
SHA-1 は、160ビット(16進数40桁)のハッシュ値です。
例:da39a3ee5e6b4b0d3255bfef95601890afd80709
→ https://www.npmjs.com/package/sha1-uint8array
const createHash = require("sha1-uint8array").createHash; const text = ""; const hex = createHash().update(text).digest("hex"); // => "da39a3ee5e6b4b0d3255bfef95601890afd80709" const data = new Uint8Array(0); const hash = createHash().update(data).digest(); // => <Uint8Array da 39 a3 ee 5e 6b 4b 0d 32 55 bf ef 95 60 18 90 af d8 07 09>v0.9.0 時点のベンチマーク結果は以下の通り。
module version node.js V14 Chrome 87 Safari 14 minified backend crypto - 70ms ? - - - OpenSSL sha1-uint8array 0.9.0 218ms 346ms ? 192ms ? 2KB ? Uint8Array hash.js 1.1.7 513ms 573ms 908ms 7KB Array jssha 3.2.0 690ms 782ms 770ms 9KB Uint8Array crypto-js 4.0.0 779ms 829ms 961ms 108KB Uint8Array jshashes 1.0.8 686ms 1,448ms 727ms 23KB Array tiny-sha1 0.2.1 209ms 775ms 3,573ms 2KB Uint8Array sha.js 2.4.11 360ms 930ms 3,534ms 26KB Buffer create-hash 1.2.0 387ms 976ms 3,591ms 97KB Buffer 古いライブラリだと ASCII 文字列のみ対応で、日本語すら使えないものもあったので、注意が必要。
上記リストの各ライブラリごとの minified したときの容量や、バックエンド(内部で使われる仕組み)は、ざっくり調査したものなので、厳密には違うかも。高速化実装のキモ
JavaScript の実行を高速化するには、できるだけオブジェクトを作らない・メモリを確保しない実装が大切です。
V8 なら、確保されたメモリ内で済む処理ならば、かなり高速に動作してくれます。本ライブラリでは、1回のハッシュ値計算ごとに、3つオブジェクトを作っています。
もし複数チャンク分割に対応せずに、1入力だけとすれば、再入を考慮せずに済むので、さらに減らせるのだが。
メモリは 8KB のプールを分割して利用することで、毎回はメモリを確保しないようにしています。また、文字列からの入力時は、JavaScript の内部の文字コードは UTF-16 なので、UTF-8 への変換が必要。
文字列全体をいちどに TypedArray に展開するのではなくて、64・80バイト毎のブロックの範囲ごとに変換しています。
どちらかというと、この処理を書いてみたくて、このライブラリを作ったようなものだ。ネイティブ実装との速度比較
上記のベンチマークの通り、ほかのピュア JavaScript 実装のライブラリよりは高速なものの、
Node.js だけで使うなら、ネイティブの crypto モジュールを使ったほうが3倍くらい速いです。
内部で使われる OpenSSL の実装がカリカリで速すぎるみたい。なお、ブラウザでは、TextEncoder と crypto.subtle.digest() の両方が使える環境であれば、その方が速いです。
iOS 10.3 以降あるいは Android 5 以降かつ、HTTPS サーバ配下なら使えるようです。IE や古い方の Edge では使えない。Node.js の
crypto
とブラウザのcrypto.subtle
はインターフェースが異なるので注意が必要。
ブラウザでは、即値ではなくPromise
を返したり、まだチャンク分割入力できないようだし、ArrayBuffer
専用のようだ。Browserify
crypto
モジュール全体を呼んだアプリを、ブラウザ向けに普通にbrowserify
すると、minify した後でも +300KB 超の容量増になります。
あるいは大きめのライブラリを使うと 100KB になるところ、本ライブラリなら 3KB 前後と、コンパクトに利用できます。
もし、crypto
モジュールをcrypto.createHash("sha256").update(data).digest("hex");
形式のハッシュ計算でしか使っていないアプリなら、下記のような browserify 設定にすることで、『Node.js ならネイティブ実装』・『ブラウザなら本ライブラリ』と切り替えて利用することができます。package.json{ "browser": { "crypto": "sha256-uint8array/dist/sha256-uint8array.min.js" }, "devDependencies": { "browserify": "^17.0.0", "sha256-uint8array": "^0.9.0", "terser": "^5.5.1" } }(備考)どのハッシュを使うか
ハッシュはいくつも種類があります。その昔は軽い MD5 を使っていました。
- MD5 は弱いので、現代のサービスでは使われないです。
- SHA-1 もすでに推奨されていないものの、用途によっては、まだまだみかけます。
- SHA-256 を使うと SHA-1 よりも2〜3割ほど遅くなるものの、大量に使う用途でもなければ、無視できるレベルの差かなと。
- SHA-368 や SHA-512 については、64ビット整数演算が必要なので、ピュア JavaScript で実用的な速度で実装するのは難しそう。
ネイティブ実装と、ピュア JavaScript 実装の両方を混在利用したい場合は、今なら SHA-256 を使うのが良さそう。