Node.js実践ガイド:高度な開発手法とベストプラクティス

Node.jsの概要と特徴

Node.jsはChromeのV8 JavaScriptエンジンをベースに構築されたJavaScript実行環境です。従来ブラウザ内でしか動作しなかったJavaScriptをサーバーサイドでも実行可能にし、PHP、Java、Pythonといったバックエンド言語と同等の機能を提供します。

Node.jsの主な特性:

  • JavaScriptの完全な構文サポート - 既存のJS知識を活かしてサーバー開発が可能
  • 優れた並行処理能力 - イベントループによる非同期I/Oで高いパフォーマンスを実現
  • 短い開発サイクル - 豊富なエコシステムとシンプルな実装で迅速な開発が可能

開発環境の構築

Node.jsの公式サイト(https://nodejs.org/)から安定版をダウンロードしてインストールします。LTS(Long Term Support)版が推奨されます。

# Node.jsのバージョン確認
node --version

# npm(Node Package Manager)のバージョン確認
npm --version

モジュールシステムとCommonJS

Node.jsではCommonJSモジュールシステムを採用しています。これにより、コードの再利用性を高め、依存関係を明確に管理できます。

// calculator.js
const add = (a, b) => a + b;
const subtract = (a, b) => a - b;

// エクスポート方法1:オブジェクトとしてまとめてエクスポート
module.exports = {
  calculate: {
    sum: add,
    diff: subtract
  }
};

// エクスポート方法2:個別にエクスポート
exports.multiply = (a, b) => a * b;
// main.js
const { calculate, multiply } = require('./calculator');

console.log(calculate.sum(5, 3)); // 8
console.log(multiply(4, 6)); // 24

パッケージ管理:npmとyarn

npmはNode.jsの標準パッケージマネージャーです。yarnはより高速な依存関係解決と並列インストールを特徴とする代替ツールです。

npmの基本操作

# プロジェクトの初期化
npm init -y

# パッケージのインストール
npm install express --save

# グローバルインストール
npm install nodemon -g

# 開発依存としてインストール
npm install jest --save-dev

# パッケージの更新
npm update

# 期限切れパッケージの確認
npm outdated

yarnの基本操作

# yarnのインストール
npm install -g yarn

# プロジェクトの初期化
yarn init

# 依存関係の追加
yarn add react
yarn add lodash --dev

# すべての依存関係をインストール
yarn install

主要な組み込みモジュール

HTTPサーバーの構築

const http = require('http');

const server = http.createServer((request, response) => {
  const method = request.method;
  const url = request.url;
  
  response.writeHead(200, {
    'Content-Type': 'application/json',
    'Access-Control-Allow-Origin': '*'
  });
  
  response.end(JSON.stringify({
    timestamp: new Date().toISOString(),
    method: method,
    path: url
  }));
});

server.listen(3000, () => {
  console.log('Server running on port 3000');
});

ファイルシステム操作

const fs = require('fs').promises;
const path = require('path');

async function processFiles() {
  try {
    // ディレクトリの作成
    await fs.mkdir('./data', { recursive: true });
    
    // ファイルへの書き込み
    const content = `処理時刻: ${new Date().toLocaleString()}`;
    await fs.writeFile('./data/timestamp.txt', content, 'utf8');
    
    // ファイルの読み取り
    const data = await fs.readFile('./data/timestamp.txt', 'utf8');
    console.log(data);
    
    // ファイル情報の取得
    const stats = await fs.stat('./data/timestamp.txt');
    console.log(`ファイルサイズ: ${stats.size} bytes`);
  } catch (error) {
    console.error('エラー発生:', error);
  }
}

processFiles();

ストリーム処理

const fs = require('fs');

const readable = fs.createReadStream('./source.txt', {
  encoding: 'utf8',
  highWaterMark: 16 * 1024
});

const writable = fs.createWriteStream('./destination.txt');

readable.on('data', (chunk) => {
  console.log(`${chunk.length} bytes 受信`);
});

readable.on('end', () => {
  console.log('読み取り完了');
});

readable.on('error', (err) => {
  console.error('エラー:', err);
});

// パイプによる直接接続
readable.pipe(writable);

イベントエミッター

const EventEmitter = require('events');

class DatabaseConnection extends EventEmitter {
  constructor() {
    super();
    this.connected = false;
  }
  
  connect() {
    setTimeout(() => {
      this.connected = true;
      this.emit('connected', 'データベース接続確立');
    }, 1000);
  }
  
  query(sql) {
    if (!this.connected) {
      this.emit('error', new Error('接続が確立されていません'));
      return;
    }
    this.emit('query', sql);
  }
}

const db = new DatabaseConnection();

db.on('connected', (message) => {
  console.log(message);
});

db.on('query', (sql) => {
  console.log(`クエリ実行: ${sql}`);
});

db.on('error', (err) => {
  console.error(`エラー: ${err.message}`);
});

db.connect();
setTimeout(() => db.query('SELECT * FROM users'), 1500);

暗号化機能

const crypto = require('crypto');

// ハッシュ生成
function generateHash(data, algorithm = 'sha256') {
  return crypto.createHash(algorithm)
    .update(data)
    .digest('hex');
}

// HMAC生成
function generateHMAC(data, secret) {
  return crypto.createHmac('sha256', secret)
    .update(data)
    .digest('hex');
}

// AES暗号化
function encrypt(text, key) {
  const iv = crypto.randomBytes(16);
  const cipher = crypto.createCipher('aes-256-cbc', key);
  
  let encrypted = cipher.update(text, 'utf8', 'hex');
  encrypted += cipher.final('hex');
  
  return { iv: iv.toString('hex'), encrypted };
}

const secretKey = 'my-secret-key';
const message = '重要なデータ';

console.log('SHA256:', generateHash(message));
console.log('HMAC:', generateHMAC(message, secretKey));
console.log('暗号化:', encrypt(message, secretKey));

Expressフレームワークの活用

ExpressはNode.jsの最も人気のあるWebフレームワークです。ミニマリストな設計ながら、強力なミドルウェアシステムを提供します。

const express = require('express');
const path = require('path');

const app = express();
const PORT = process.env.PORT || 3000;

// ミドルウェアの設定
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(express.static(path.join(__dirname, 'public')));

// ルーティングの定義
app.get('/', (req, res) => {
  res.json({ message: 'Expressサーバーへようこそ' });
});

app.get('/api/users/:id', (req, res) => {
  const userId = req.params.id;
  res.json({ id: userId, name: `ユーザー${userId}` });
});

// POSTリクエストの処理
app.post('/api/data', (req, res) => {
  const received = req.body;
  console.log('受信データ:', received);
  res.status(201).json({ 
    success: true, 
    received: received 
  });
});

// エラーハンドリングミドルウェア
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).json({ 
    error: 'サーバーエラーが発生しました' 
  });
});

app.listen(PORT, () => {
  console.log(`サーバーがポート${PORT}で起動`);
});

ミドルウェアの実装

Expressのミドルウェアはリクエスト処理パイプラインの各ステップで実行される関数です。

// ロギングミドルウェア
const requestLogger = (req, res, next) => {
  console.log(`${new Date().toISOString()} - ${req.method} ${req.url}`);
  next();
};

// 認証ミドルウェア
const authenticateToken = (req, res, next) => {
  const token = req.headers['authorization'];
  
  if (!token) {
    return res.status(401).json({ error: 'トークンが必要です' });
  }
  
  // トークン検証ロジック(簡略化)
  if (token === 'valid-token') {
    req.user = { id: 1, name: '認証ユーザー' };
    next();
  } else {
    res.status(403).json({ error: '無効なトークン' });
  }
};

// ミドルウェアの適用
app.use(requestLogger);
app.use('/api/protected', authenticateToken);

app.get('/api/protected/profile', (req, res) => {
  res.json({ user: req.user });
});

テンプレートエンジンによるサーバーサイドレンダリング

EJSテンプレートエンジンを使用して動的なHTMLを生成します。

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

// テンプレートエンジンの設定
app.set('view engine', 'ejs');
app.set('views', './views');

app.get('/dashboard/:username', (req, res) => {
  const userData = {
    name: req.params.username,
    items: ['項目1', '項目2', '項目3'],
    isAdmin: true
  };
  
  res.render('dashboard', userData);
});
<!-- views/dashboard.ejs -->
<!DOCTYPE html>
<html>
<head>
  <title>ダッシュボード</title>
</head>
<body>
  <h1>ようこそ、<%= name %>さん</h1>
  
  <% if (isAdmin) { %>
    <p>管理者権限が有効です</p>
  <% } %>
  
  <ul>
    <% items.forEach(function(item) { %>
      <li><%= item %></li>
    <% }) %>
  </ul>
  
  <%- include('partials/footer') %>
</body>
</html>

タグ: Node.js express javascript CommonJS npm

7月13日 21:47 投稿