Node.js の Express と docker で立ち上げた redis を使って簡単なログイン機能を実装する手順

expressDockerHTMLNode.js
Node.js の Express と docker で立ち上げた redis を使って簡単なログイン機能を実装する手順
Memo
この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

Node.js の Express と docker で立ち上げた redis を使って簡単なログイン機能を実装する手順をメモしました。

構成

Node.js と Express で立ち上げた web アプリから docker で立ち上げた redis へセッション情報を保存する構成で作成します。

Express で立ち上げた web アプリから docker で立ち上げた redis へセッション情報を保存

redis(レディス)

redis とは?

Redis(リモートディクショナリサーバー)とは、オープンソースで提供されている高速なメモリ内 Key-Value データストアです。 データベース、キャッシュ、メッセージブローカー、およびキューなどを用途として利用します。

公式サイト

環境

  • Mac OS Big SUR 11.3.1
  • yarn 1.22.10
  • Node 14.15.3
  • express 4.17.1
  • express-session 1.17.2
  • redis 3.1.2

ファイル操作で利用する Unix コマンドについて

基本的なディレクトリ作成やファイル操作は Unix コマンドを利用します。
Unix コマンドについて詳しくはこちらの記事を参考にしてください。

事前準備

Docker 環境が必要

事前準備として Docker が利用できる環境で実施します。
Docker のインストール手順についてはこちらを参照ください。

Node.js 環境が必要

事前準備として Node.js が利用できる環境で実施します。
Node.js の環境の構築について詳しくはこちらの記事を参考にしてください。

yarn コマンドのインストール

事前準備として Yarn が利用できる環境で実施します。
Yarn コマンドのインストールについて詳しくはこちらの記事を参考にしてください。

TypeScript 環境で実施

事前準備として TypeScript が利用できる環境で実施します。
TypeScript の実行環境の構築について詳しくはこちらの記事を参考にしてください。

プロジェクトの作成

プロジェクトディレクトリを作成

~/login-sampleというプロジェクトディレクトリを作成します。

terminal
$ mkdir ~/login-sample

プロジェクトディレクトリへ移動

作成した~/login-sample ディレクトリへ移動します。

terminal
$ mkdir ~/login-sample

yarn を初期化をして package.json を作成

yarn でプロジェクトの初期化を行います。
npm で実施する方は必要に応じて変更してください。

terminal
$ yarn init -y yarn init v1.22.10 warning The yes flag has been set. This will automatically answer yes to all questions, which may have security implications. success Saved package.json ✨ Done in 0.08s.

必要なパッケージのインストール

必要なパッケージ

experss と TypeScript のパッケージをインストールします。

  • express
  • express-session
  • cookie-parser
  • redis
  • connect-redis
  • @types/express
  • @types/express-session
  • @types/cookie-parser
  • @types/redis
  • @types/connect-redis
  • typescript
  • @types/node
  • ts-node

express

Express フレームワークのモジュールです。

express-session

Express でセッションを扱えるようにするためのミドルウェアです。

cookie-parser

cookie を読み取るためのミドルウェアです。Express のミドルウェアへ設定して利用します。

redis

Redis のモジュールです。

connect-redis

express-session のセッション保存先に redis を扱えるようにするモジュールです。

パッケージのインストール

yarn addコマンドで必要パッケージをインストールします。

terminal
$ yarn add express express-session cookie-parser redis connect-redis

開発に関連するパッケージのため -Dオプションを付与しています。

$ yarn add -D @types/express-session @types/cookie-parser @types/redis @types/connect-redis @types/express typescript @types/node

インストールしたパッケージを確認

package.json を開きインストールされたパッケージを確認します。

  "devDependencies": {
    "@types/connect-redis": "^0.0.16",
    "@types/cookie-parser": "^1.4.2",
    "@types/express": "^4.17.12",
    "@types/express-session": "^1.17.3",
    "@types/node": "^15.12.5",
    "@types/redis": "^2.8.30",
    "ts-node": "^10.0.0",
    "typescript": "^4.3.4"
  },
  "dependencies": {
    "connect-redis": "^6.0.0",
    "cookie-parser": "^1.4.5",
    "express": "^4.17.1",
    "express-session": "^1.17.2",
    "redis": "^3.1.2"
  }

設定ファイルを作成

tsconfig.json を作成

プロジェクトルートへ tsconfig.json を作成します。

terminal
$ vi tsconfig.json

tsconfig.json の設定例

tsconfig.json
{ "compilerOptions": { "target": "es2020", "module": "commonjs", "lib": [ "es2020" ], "sourceMap": true, "outDir": "./dist", "rootDir": "./src", "strict": true, "moduleResolution": "node", "baseUrl": "src", "esModuleInterop": true, "experimentalDecorators": true, "emitDecoratorMetadata": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true }, "include": [ "src/**/*" ], "exclude": [ "dist", "node_modules" ], "compileOnSave": false }

ts-config のオプションはこちらの記事を参考にしてください

Redis の立ち上げ

アプリケーションから接続する必要があるため先に Redis を立ち上げます。
docker-compose.yml を利用して docker でサクッと立ち上げます。

docker-compose.yml を作成

プロジェクトのルートディレクトリへ docker-compose.yml を作成します。

terminal
$ vi docker-compose.yml

redis の設定記述

redis の設定を記述します。

docker-compose.yml
version: '3.5' services: redis: image: 'redis' ports: - '6379:6379' volumes: - './data:/data'

データ永続化のための data ディレクトリ作成

プロジェクトルートへデータ永続化のための data ディレクトリを作成します。

terminal
$ mkdir data

docker を立ち上げ

docker-compose upコマンドで redis を立ち上げます。

terminal
docker-compose up -d // 中略 Status: Downloaded newer image for redis:latest Creating login-sample_redis_1 ... done

docker の起動を確認

docker psコマンドで redis の起動を確認します。

$ docker ps
CONTAINER ID   IMAGE      COMMAND                  CREATED              STATUS          PORTS                                       NAMES
11387a81aa9b   redis      "docker-entrypoint.s…"   About a minute ago   Up 56 seconds   0.0.0.0:6379->6379/tcp, :::6379->6379/tcp   login-sample_redis_1

以上で redis の立ち上げは完了です。

アプリケーションの構築

ソースディレクトリの作成

プロジェクトルートへsrcディレクトリを作成します。

terminal
$ mkdir src

アプリケーションのファイルを作成

作成したソースディレクトリへ index.ts を追加します。

terminal
$ vi src/index.ts

アプリケーションの設定を追記

利用するモジュールをインポート

import 文で利用するモジュールをインポートします。

src/index.ts
import express from 'express'; import cookieParser from 'cookie-parser'; import session from 'express-session'; import redis from 'redis'; import connectRedis from 'connect-redis';

各種初期化を実施

Express の初期化

import した express を呼び出して express を初期化します。

src/index.ts
const app = express();

express-session の初期化

redis を使用するための設定です。 connect-redis へ express-session を渡します。

src/index.ts
const RedisStore = connectRedis(session);

Redis の初期化

import した express を呼び出して express を初期化します。

src/index.ts
const redisClient = redis.createClient({ host: 'localhost', port: 6379, prefix: 'sid:', }); redisClient.unref(); redisClient.on('error', console.log);

connect-redisのversion4で一部書き方が変わっています。 connect-redis/migration-to-v4.md at master · tj/connect-redis

ミドルウェアの設定

express のミドルウェアへ cookie-parser を設定します。

src/index.ts
app.use(cookieParser())

body-parser

express のミドルウェアへ body-parser を設定してpostデータを受け取れるようにします。
body-parserはexpressに内包されるように変更になりました。

src/index.ts
app.use(express.urlencoded());

session の設定

express のミドルウェアへ settion の設定を渡します。

src/index.ts
app.use( session({ secret: 'secret_key', resave: true, saveUninitialized: true, store: new RedisStore({ client: redisClient }), cookie: { httpOnly: true, secure: false, maxAge: 1000 * 60 * 30, }, }) );
secret

セッション ID Cookie に署名するために使用される文字列です。
任意の値を指定します。

resave

要求中にセッションが変更されなかった場合でも、セッションをセッションストアに強制的に保存するかどうかの設定です。
デフォルトを使用することは推奨されなくなったため明示的に指定しています。

saveUninitialized

「初期化されていない」セッションを強制的にセッション・ストアに保存するかどうかの設定です。
デフォルトを使用することは推奨されなくなったため明示的に指定しています。

store

セッションストアインスタンスになります。
ここで redis を指定しています。

Cookie の設定用のオブジェクトになります。

オプションについて詳細はこちらをご確認ください。 expressjs/session: Simple session middleware for Express

型の拡張

express-sessionでuser_idを利用するため型ファイルを拡張します。

型の拡張用のディレクトリ追加

terminal
$ mkdir -p src/@types

@types.express-session.d.tsを作成

@types.express-session.d.tsを作成します。

terminal
$ vi src/@types/express-session.d.ts

型の定義を追加

src/@types/express-session.d.ts
import 'express-session'; declare module 'express-session' { interface SessionData { user_id: string; } }

ログインページの作成

ログインページを作成します。

ログインページの仕様

クライアント

  • ユーザー ID を入力するための入力ボックスを作成
  • 送信ボタンを作成
  • 送信ボタンをクリックしたら post でユーザー ID を送信する

サーバー

  • アクセス URL は/login
  • GET ならページを表示
  • POST ならデータを確認してユーザー ID が送られてきたらユーザー ID をセッションへ保存し'/'へリダイレクト

クライアントの処理

ソースディレクトリ内へhtmlファイルを作成します。

htmlを格納するディレクトリ

htmlを格納するディレクトリを作成します。

terminal
$ mkdir src/html

login.htmlを作成

login.htmlを作成します。

$ vi src/html/login.html

login.html

入力されたuser_idをポストするだけの簡易的なformを作成しています。

src/html/login.html
<form action="/login" method="post"> <p>ユーザーID</p> <input type="text" name="user_id" value="" /> <input type="submit" value="送信" /> </form>

サーバーの処理

GET時の処理

/loginへのgetのリクエストでhtmlを表示します。

src/index.ts
app.get( '/login', (req: express.Request, res: express.Response): void => { // login.htmlファイルを表示 res.sendFile(`${__dirname}/html/login.html`); } );

POST時の処理

/loginへのPOSTでuser_idがあったらsessionへ保存してリダイレクトさせます。

src/index.ts
app.post( '/login', (req: express.Request, res: express.Response): void => { const { user_id } = req.body; if (user_id) { req.session.user_id = user_id; res.redirect('/'); } } );

トップページの作成

トップページを作成します。

トップページの仕様

クライアント

  • ユーザー ID を表示
  • ログアウトページのリンクを表示

サーバー

  • セッションのユーザー ID が存在したらページを表示
  • セッションのユーザー ID が存在しなかったらログインページへリダイレクト

index.tsへトップページの処理追加

/のGETでアクセスがあったらsessionのuser_idを確認。
なかったら/loginへリダイレクトし
あったらuser_idとlinkタグを出力

src/index.ts
app.get( '/', (req: express.Request, res: express.Response): void => { const { user_id } = req.session; if (!user_id) { res.redirect('/login'); } console.log(user_id); res.send( `ようこそ${user_id}さん<br><a href="/logout">ログアウト</a>` ); } );

ログアウト処理の作成

ログアウトページを作成します。

ログアウト処理の仕様

サーバー

  • セッションを削除してログインページへリダイレクト

index.tsへログアウト処理追加

user_idをdeleteして/loginへリダイレクトします。

app.get(
  '/logout',
  (req: express.Request, res: express.Response): void => {
    delete req.session.user_id;
    res.redirect('/login');
  }
);

アプリケーションを起動

アプリケーションを起動させていきます。

npm scripts へ起動コマンドの追加

実行するコマンドを追記します。

package.json

package.json
"scripts": { "start": "node dist/index.js", "build": "tsc", "dev": "ts-node src/index.ts" },

devでアプリサーバーを起動

yarn devコマンドを実行します。

terminal
$ yarn dev yarn run v1.22.10 $ ts-node src/index.ts listening on *:8080

8080で待ち受ける形でアプリケーションが起動しました。

アプリの動作テスト

起動させたアプリケーションをブラウザで確認してみます。

入力して送信

入力ボックスへ文字列をして送信します。
入力して送信

ログアウト処理を実行

入力した文字列が表示されているか確認します。
そしてログアウトをクリックしてログインページへ戻るか確認します。

ログアウト処理を実行

ログイン・ログアウト処理を確認することが出来ました。

ログイン・ログアウト処理を確認

Redisのログ確認

起動しているコンテナIDを確認

docker psコマンドで redis のコンテナIDを確認します。

$ docker ps
CONTAINER ID   IMAGE      COMMAND                  CREATED              STATUS          PORTS                                       NAMES
11387a81aa9b   redis      "docker-entrypoint.s…"   About a minute ago   Up 56 seconds   0.0.0.0:6379->6379/tcp, :::6379->6379/tcp   login-sample_redis_1

redisへログイン

docker execコマンドでログインします。

$ docker exec -it 11387a81aa9b bash
root@11387a81aa9b:/data# 

キーを確認

保存されているキーを確認します。

127.0.0.1:6379> keys *
1) "sid:sess:nelbdFcxCEp1juSAFvkEmCE18Ol4-aEM"

getで保存されている内容を確認

ログイン時

ログイン時はuser_idが入っていることが確認できました。

127.0.0.1:6379> get sid:sess:nelbdFcxCEp1juSAFvkEmCE18Ol4-aEM
"{\"cookie\":{\"originalMaxAge\":1799999,\"expires\":\"2021-06-29T10:07:39.754Z\",\"secure\":false,\"httpOnly\":true,\"path\":\"/\"},\"user_id\":\"xxx\"}"

ログアウト時

ログアウトするとuser_idが削除されていることが確認できました。

127.0.0.1:6379> get sid:sess:nelbdFcxCEp1juSAFvkEmCE18Ol4-aEM
"{\"cookie\":{\"originalMaxAge\":1799999,\"expires\":\"2021-06-29T10:25:35.933Z\",\"secure\":false,\"httpOnly\":true,\"path\":\"/\"}}"

以上でログイン・ログアウトの動作確認は完了です。

終わりに

最後までご覧いただきありがとうございます。
この記事ではNode.js の Express と docker で立ち上げた redis を使って簡単なログイン機能を実装する手順について紹介させていただきました。

これからも皆様の開発に役立つ情報を提供していきたいと考えています。
今後ともよろしくお願いいたします。