かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

Expresss ioredis で Redis 入門した

JWT token の勉強をしていて、Redis で token を管理しているという話を教えてもらったので Redis を使うだけの環境を作ってみていました。

Docker で環境を構築します

構成

/
|- /api (express)
|    |- Dockerfile
|- /redis
|- docker-compose.yml

docker-compose.yml

version: '3'
services:
  redis:
    image: redis:latest
    volumes:
      - ./redis/data:/data
    ports:
      - 6379:6379

  api:
    build:
      context: './api'
      dockerfile: 'Dockerfile'
    volumes:
      - ./api:/api
      - ./api/package.json:/api/package.json
      - ./api/package-lock.json:/api/package-lock.json
      - ./api/node_modules:/api/node_modules
    ports:
      - 3000:3000
    depends_on:
      - redis
    tty: true
    working_dir: "/api"
    command: bash -c "npm run start"

/api/Dockerfile

FROM node:12.18.0

WORKDIR /api

build

$ docker-compose build
$ docker-compose up

redis と api のコンテナが動いてたらOK
今回はモックのAPIなので redis 特に設定せずに使います。(production で使う場合は config を作って読み込ませるとか必要な気がします)

これくらいだとMacでも速いですね。

ioredis で api から Redis に接続する

apiディレクトリに移動してパッケージをインストールします。

$ npm install express ioredis

開発環境なら nodemon を入れて "start": "nodemon ./index.js" とかとしておくと楽です。

api/index.js

const express = require('express');
const app = express();
const PORT = 3000;

// Router
const authRouter = require('./routes/auth');

// Middleware
// Express v4.16.0 から core に戻ったみたいなので Body-Parser をインストールしなくてもOK
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// Routing middleware
app.use('/api/user', authRouter);

app.listen(PORT, () => console.log(`SERVER started on localhost:${PORT}`));

./routes/auth.js

const router = require('express').Router();
const Redis = require('ioredis');
const redis = new Redis(6379, 'redis');

router.post('/login', async (req, res) => {
  // Moc user
  const user = {id:1, name: '星宮いちご'}
  // ID を key に String 型でデータを保存 'EX', time (second) で有効期限を設定できる
  await redis.set(user.id, JSON.stringify(user), 'EX', 60)
  return res.send(user);
});

router.get('/posts', async (req, res) => {
  const userID = req.body.id;
  try {
    // redis から データを取得
    const data = await redis.get(userID);
    // redis 上に存在してなかった場合 err ではなく null が返される
    if (!data) {
      throw new Error('Access Denied.');
    }
    return res.send(JSON.parse(data));
  } catch (err) {
    return res.status(400).send(err.message);
  }
});

module.exports = router;

セキュリティとか何もない感じですが、docker-compose up して、localhost:3000/api/users/login に POST でアクセスすると user データが Redis に保存されます。その後に localhost:3000/api/users/posts{id: 1} を持たせてアクセスすると Redis のデータが有効期限内なら user データが返され、期限切れだと Access Denied. が返るようになっているかと思います。

Redis のデータの確認

docker のコンテナに入って redis-cli を使って確認します。

$ docker exec -it <Redis_container_name> bash
$ redis-cli
# 登録されているキーの確認
127.0.0.1:6379> keys *
# キーの値を確認 (String 型)
127.0.0.1:6379> get key
# キーの有効期限を確認
127.0.0.1:6379> ttl key
# キーを削除
127.0.0.1:6379> del key
# データを作成 (String型)
127.0.0.1:6379> set key value
# キーに有効期限を設定
127.0.0.1:6379> expire key time

ほぼ ioredis からのコマンドと同じで redis の確認・操作をすることができました。
有効期限の設定付きでデータを作成する場合 ioredis では redis.set(key, value, 'EX', time) で丸っと作成することができましたが、redis で直接データをつくる際に set key value 'EX' time とすると、EX: time というデータが作られてしまいます。有効期限の設定は別途 expire コマンドで設定する必要がありました。

redis に入ってテキトーなキーで値を作成すると、作成したキーを id にしてlocalhost:3000/api/users/posts にアクセスすると保存した値が返されます。(JSON.parseがエラーにならなければ)

所感

別に express でなくても良かったと思いますが、ioredis を使うと簡単に JavaScript (node.js) で Redis を使うことができました。
1点だけポイントとしては、new Redis() のに渡しているポートの設定ですが docker の場合 Redis port, Redis host を docker-compose のport番号, コンテナ名 で指定する必要がありました。
config を作れば、コンテナ名でなくポート番号で指定できるようになるのかもしれません。

JWT の実験や Redis で List 型や Hash 型の操作もしてみたので、それは追々メモ書いていきたいと思います。

おわり。


[参考]

I would recommend you create two separate clients each connect to a different database instead of using select to avoid potential conflictions.
cf. question on multiple databases · Issue #466 · luin/ioredis · GitHub

ioredis では select() を使うより new Redis({db: 1}) のように別クライアントを作るほうが良いっぽい

自転車えくすぷれす

自転車えくすぷれす

  • 発売日: 2014/04/01
  • メディア: MP3 ダウンロード

さよぽに の「自転車えくすぷれす」いい曲なので聴いて!