20210114のNode.jsに関する記事は4件です。

【基礎の基礎編】React + Express + GraphQLでHello World!

graphql-1200x630-960x504.png

はじめに

この記事は、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.js
const 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.graphql
type Query {
    helloWorld: String!
}
resolvers/index.js
const Query = require('./Query');

const resolvers = {
    Query
}

module.exports = resolvers
resolvers/Query.js
module.exports = {
    helloWorld: () => 'Hello World'
}

サーバーの起動

$ cd myappServer
$ node src/index.js

>>>
GraphQL Server runnning @ http://localhost:4000{server.graphqlPath}

GraphQL Playgroundで実行してみましょう。
スクリーンショット 2021-01-14 171105.png

GraphQLクライアントの実装

次に、先ほど作成したGraphQLサーバーにクライアントからアクセスしてみましょう。

reactプロジェクトの作成

$ create-react-app myappClient

インストール

$ npm install graphql graphql-request
  • graphql-request

GraphQLオペレーションをAPIに送信するために利用できるフレームワークがいくつか存在します。その中で有名な"graphql-request"を使用します。これはfetchリクエストをPromiseでラップして、GraphQLサーバーへのリクエストを送信します。また、リクエストの構築とデータのパースに関する細かい処理を引き受けます。

プログラム

作成されたApp.jsを下記のように書き換えてください。

App.js
import { 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-14 172910.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Node.jsとMySQLの接続方法

mysqlモジュールをインストールする

Node.jsでMySQLに接続するには、mysqlモジュールを使います。
以下のコマンドを実行してインストールします。

$ npm install mysql

MySQLに接続する

MySQLを操作するjsファイルに追記していきます。
今回はMySQLを操作するjsファイルを main.js とします。
main.js に以下を追加して、mysqlのモジュールをインポートします。

main.js
const mysql = require('mysql');

次に、 createConection メソッドで、使用するMySQLのデータベースとの接続の指定をし、 connection を作成します。(MySQLでデータベースを作成しておきましょう。)

main.js
const connection = mysql.createConnection({
  host: 'localhost',
  user: 'root',
  password: 'password',
  database: 'database-name'
});

クエリを実行する

データベースからデータを引出します。
例えば以下のようになります。

main.js
connection.query(
    'SELECT * FROM database-name',
    (error, results) => {
      res.render('list.ejs', { databaseーname: results });
    }
  );

MySQLとの接続を切る

最後に end メソッドで connection によるMySQLとの接続を切ります。

main.js
connection.end();
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Node.jsとSeleniumを使ってChoromeのコンソールログを確認する

はじめに

Seleniumを使用してみる機会があったため,備忘録としてまとめようと思い,記事を書きました。
現在主にJavascriptを使用しているため,Node.jsを使ってSeleniumを動かそうと思います。
Node.jsの環境構築に関しては,こちらの手順で行なっています。

Seleniumとは

Webブラウザの操作を自動化するためのツール。
今回は,SeleniumをNode.js内で使用して,Webブラウザを操作してみたいと思います。

大まかな流れ

  1. 必要なパッケージ・ドライバのダウンロード
  2. 実行ファイル作成
  3. 実行

以下で詳細に説明していきます。

必要なパッケージ・ドライバのダウンロード

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ドライバのページをクリックします.

スクリーンショット 2020-11-17 12.37.44.png

バージョン86を使用するので,対象バージョンの欄をクリックします.
(どのバージョンのドライバをダウンロードするかはこちらのページを参照)

68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3533333031342f38326630643232302d626261342d653765662d386639382d3339363433353133333639352e706e67.png

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.js

webdriverを呼び出す

公式のドキュメントを参考に,実行ファイル内で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: ''
}

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pure JavaScript 高速 SHA-256 ハッシュ実装

SHA-256 のダイジェスト・ハッシュ値を計算する高速なピュア JavaScript 実装のライブラリを npm で公開したので紹介します。
Uint8ArrayInt32Array を使うことで、このほかのピュア 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 の実装がカリカリで速すぎるみたい。

なお、ブラウザでは、TextEncodercrypto.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 を使うのが良さそう。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む