Node.js開発に必須の10個のツール:PrismaからMime-typesまで

Prisma:TypeScript向け次世代ORM

データベース操作を型安全かつ直感的に実現するPrismaは、従来のORMとは異なるアプローチを採用しています。スキーマ定義ファイル(schema.prisma)を基に、自動的に型付きクライアントを生成し、TypeScriptの開発体験を大幅に向上させます。

// prisma/schema.prisma
model User {
  id        Int      @id @default(autoincrement())
  email     String   @unique
  profile   Profile? @relation(fields: [profileId], references: [id])
  createdAt DateTime @default(now())
}

model Profile {
  id        Int    @id @default(autoincrement())
  bio       String?
  userId    Int    @unique
  user      User   @relation(fields: [userId], references: [id])
}

クライアントを使用してデータを操作する際は、メソッドチェーンと型推論が活かされます:

import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()

// ユーザー作成(型チェック対応)
const user = await prisma.user.create({
  data: {
    email: 'test@example.com',
    profile: {
      create: { bio: 'Developer' }
    }
  }
})

// 関連データ付きで取得
const usersWithProfiles = await prisma.user.findMany({
  include: { profile: true }
})

マイグレーションは自動生成され、データベースの変更履歴を管理しやすくなります。ただし、カスタムSQLや複雑なJOINは制限される可能性がある点に注意が必要です。

Day.js:軽量で高速な日付処理ライブラリ

Moment.jsの後継として登場したDay.jsは、API互換性を保ちつつ、バンドルサイズを1/10以下に削減しました。モジュールのインポートもESM対応で、Tree-shakingが可能です。

import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime'
import 'dayjs/locale/ja'

dayjs.extend(relativeTime)
dayjs.locale('ja')

const now = dayjs()
const past = dayjs().subtract(2, 'hours')

console.log(past.fromNow()) // "2時間前"
console.log(now.format('YYYY年MM月DD日 HH:mm')) // "2024年04月15日 14:30"

プラグインシステムにより、ローカライズやカスタムフォーマットも柔軟に拡張できます。パフォーマンス面でも、単純な日付操作ではMoment.jsの約20倍速く動作します。

Cypress:ブラウザ内エンドツーエンドテスト

Cypressは、テストコードがブラウザ内で直接実行されるため、リアルタイムなデバッグが可能です。タイムアウトや待機処理の自動管理により、非同期処理のテストが簡素化されています。

describe('ユーザー認証', () => {
  it('ログインに成功する', () => {
    cy.visit('/login')
    cy.get('[data-testid="email"]').type('user@example.com')
    cy.get('[data-testid="password"]').type('password123')
    cy.get('[data-testid="submit-btn"]').click()

    // ログイン後のリダイレクトを待機
    cy.url().should('include', '/dashboard')
    cy.get('[data-testid="user-name"]').should('have.text', 'User Name')
  })

  it('APIレスポンスをモックする', () => {
    cy.intercept('GET', '/api/profile', {
      fixture: 'user-profile.json'
    }).as('getUserProfile')

    cy.visit('/profile')
    cy.wait('@getUserProfile')
    cy.get('[data-testid="profile-email"]').should('be.visible')
  })
})

テスト実行中にスクリーンショットや動画記録が自動生成され、CI/CDパイプラインへの統合も容易です。

Winston:多様な出力先に対応するログ管理

Winstonは、ログの出力先・フォーマット・レベルを柔軟に制御できる汎用ロガーです。ファイル出力には日付別ローテーションを組み合わせることで、運用負荷を軽減できます。

const { createLogger, transports, format } = require('winston')
const { combine, timestamp, json } = format

const logger = createLogger({
  level: 'info',
  format: combine(timestamp(), json()),
  transports: [
    new transports.Console(),
    new transports.DailyRotateFile({
      filename: 'logs/application-%DATE%.log',
      datePattern: 'YYYY-MM-DD',
      maxFiles: '14d'
    })
  ]
})

logger.info('アプリケーション起動')
logger.error('DB接続失敗', { stack: error.stack })

カスタムフォーマッターやフィルタリング機能を活用すれば、環境ごとのログ出力を最適化できます。

express-rate-limit:リクエスト制限でサーバーを守る

DDoSやボット攻撃への対策として、IPごとのリクエスト頻度を制限するミドルウェアです。Redisをバックエンドにすることで、クラスタ環境でも有効に機能します。

const rateLimit = require('express-rate-limit')

const apiLimiter = rateLimit({
  windowMs: 15 * 60 * 1000,
  max: 100,
  keyGenerator: (req) => req.ip,
  handler: (req, res) => {
    res.status(429).json({ error: 'リクエストが多すぎます' })
  }
})

app.use('/api/', apiLimiter)

// 特定ルートにカスタム制限を適用
app.post('/login', rateLimit({ windowMs: 3600000, max: 5 }), loginHandler)

カスタムキー生成により、ユーザーIDやJWTトークンに基づく制限も可能で、より細かなセキュリティ制御が実現できます。

semver:バージョン管理の標準化

SemVer(Semantic Versioning)に基づくバージョン比較・操作を支援するライブラリです。パッケージ依存関係の自動解決や、互換性判定に不可欠です。

const semver = require('semver')

// バージョン範囲の検証
semver.satisfies('1.2.3', '^1.2.0') // true
semver.satisfies('2.0.0', '~1.0.0') // false

// バージョンの自動インクリメント
semver.inc('1.2.3-beta.1', 'prerelease') // "1.2.3-beta.2"
semver.inc('1.2.3', 'major') // "2.0.0"

// バージョン比較
semver.gt('1.10.0', '1.2.3') // true
semver.lt('0.9.9', '1.0.0')  // true

npmやyarnの依存解決と連携して、安定した依存関係管理を実現します。

Superagent:シンプルで強力なHTTPクライアント

チェーン呼び出しとPromise対応で、複雑なAPI呼び出しを読みやすく記述できます。Node.jsとブラウザ両方で同じコードが動作します。

const request = require('superagent')

const response = await request
  .get('https://api.example.com/items')
  .set('Authorization', `Bearer ${token}`)
  .query({ page: 1, limit: 10 })
  .timeout({ response: 5000, deadline: 10000 })

const items = response.body
const etag = response.headers.etag

ファイルアップロードやmultipart/form-dataの処理もネイティブでサポートされており、軽量なHTTPクライアントとして優れています。

Axios-retry:ネットワーク不安定時の自動再試行

Axiosに自動再試行機能を追加する軽量プラグインです。指数バックオフやステータスコードによる条件付き再試行が可能です。

import axios from 'axios'
import axiosRetry from 'axios-retry'

const client = axios.create({
  baseURL: 'https://api.example.com',
  timeout: 5000
})

axiosRetry(client, {
  retries: 3,
  retryDelay: (retryCount) => retryCount * 1000 + Math.random() * 1000,
  retryCondition: (error) => {
    return error.response?.status >= 500 || error.code === 'ECONNABORTED'
  },
  shouldResetTimeout: true
})

// 再試行が3回失敗しても、最終的にエラーがthrowされる
client.get('/data').catch(err => console.error('リトライ失敗:', err))

ネットワークの一時的な不安定を自動回復させ、ユーザー体験の信頼性を向上させます。

js-yaml:YAML構成ファイルの安全な処理

JavaScriptオブジェクトとYAML形式の相互変換を安全に行うライブラリです。safeLoadsafeDumpは、悪意あるコードの実行を防ぐために推奨されます。

const yaml = require('js-yaml')

const config = yaml.safeLoad(`
database:
  host: localhost
  port: 5432
  ssl: true
features:
  - auth
  - analytics
  - cache
`)

console.log(config.database.host) // "localhost"
console.log(config.features[1])   // "analytics"

const yamlOutput = yaml.safeDump({
  app: 'my-service',
  version: '1.2.0',
  env: 'production'
})

// 出力:
// app: my-service
// version: 1.2.0
// env: production

構造の検証には、YAMLスキーマ定義(例:ajv)と組み合わせるのが望ましいです。

mime-types:ファイルのMIMEタイプを正確に判定

拡張子やバイナリヘッダーからMIMEタイプを推定するための、信頼性の高いデータベースを提供します。

const mime = require('mime-types')

// 拡張子からMIMEタイプを取得
mime.lookup('image.png')       // "image/png"
mime.lookup('document.pdf')    // "application/pdf"

// バッファからMIMEタイプを推定
const buffer = Buffer.from('%PDF-1.4')
mime.lookup(buffer)            // "application/pdf"

// MIMEタイプから拡張子リストを取得
mime.extensions('text/css')    // ["css"]
mime.extensions('image/svg+xml') // ["svg"]

// 未知の拡張子のデフォルト値
mime.lookup('file.xyz')        // false
mime.lookup('file.xyz', 'text/plain') // "text/plain"

ファイルアップロード処理や、CDN配信のContent-Type設定に不可欠なツールです。

タグ: Prisma Day.js cypress Winston express-rate-limit

8月6日 00:47 投稿