🐕

Node.js(TypeScript)でスプレッドシートを操作する

2022/09/04に公開

はじめに

Node.js(TypeScript)でスプレッドシートを操作する方法の紹介です。

今回試した挙動は下記です。

  • スプレットシートのタイトルを取得
  • スプレットシートのタイトルを変更
  • 新規シートを追加
  • シート名の一覧を取得
  • 1行追加する
  • 複数行追加する
  • 行の一覧を取得
  • ヘッダを取得
  • 削除対象のシート名を指定して、シートを削除
  • 全てのシートを削除

コードはGitHub上にもあります。
https://github.com/nsuzuki7713/typescript-mono-repo/tree/main/packages/playground/src/spreadsheet

事前準備

  • Google Sheets API を有効化する
  • スプレットシート側に設定する サービスアカウント を作成する。
  • スプレットシートの共有ユーザーに作成したサービスアカウントのアドレスを追加する。

コード

事前準備で作成した情報を環境変数に入れます。

# スプレッドシートのURLに含まれる文字列
SHEET_ID='xxxxx'
# サービスアカウントのアドレス
GOOGLE_SERVICE_ACCOUNT_EMAIL='xxxxx'
# サービスアカウントのkeyのJSONに含まれる`"private_key"`の値
GOOGLE_PRIVATE_KEY='xxxxx'

スプレッドシートの操作を行うコードは以下の通りです。
ライブラリは google-spreadsheet を使ってます。

import { GoogleSpreadsheet, GoogleSpreadsheetWorksheet } from 'google-spreadsheet';
import * as dotenv from 'dotenv';

dotenv.config();

/**
 * GoogleSpreadsheet を操作する Service
 *
 * @see APIドキュメント {@link https://theoephraim.github.io/node-google-spreadsheet/#/}
 */
export class GoogleSpreadsheetService {
  private static instance?: GoogleSpreadsheetService;
  private doc: GoogleSpreadsheet;

  private constructor() {
    this.doc = new GoogleSpreadsheet(process.env.SHEET_ID);
  }

  /**
   * GoogleSpreadsheetService のインスタンスを取得する
   */
  static async getInstance() {
    if (this.instance) {
      return this.instance;
    }

    const instance = new GoogleSpreadsheetService();

    await instance.doc.useServiceAccountAuth({
      client_email: process.env.GOOGLE_SERVICE_ACCOUNT_EMAIL ?? '',
      private_key: (process.env.GOOGLE_PRIVATE_KEY ?? '').replace(/\\n/g, '\n'),
    });
    await instance.doc.loadInfo();

    return instance;
  }

  /**
   * スプレットシートのタイトルを取得する
   *
   * @returns スプレットシートのタイトル
   */
  getTitle() {
    return this.doc.title;
  }

  /**
   * スプレットシートのタイトルを変更する
   *
   * @param title 変更後のタイトル名
   */
  async getRenameTitle(title: string) {
    await this.doc.updateProperties({ title });
  }

  /**
   * 新規シートを追加する
   *
   * @param sheetName シート名
   * @param headerValues ヘッダの値
   * @returns 追加したシート
   */
  async addSheet(sheetName: string, headerValues?: string[]): Promise<GoogleSpreadsheetWorksheet> {
    return await this.doc.addSheet({ title: sheetName, headerValues });
  }

  /**
   * 引数で渡したシート名を削除する。
   *
   * @param sheetTitle 削除対象のシート名
   */
  async deleteSheetByTitle(sheetTitle: string) {
    const sheet = this.doc.sheetsByTitle[sheetTitle];

    if (!sheet) {
      return;
    }
    await sheet.delete();
  }

  /**
   * 全てのシートを削除する。
   */
  async deleteAllSheet() {
    const sheets = this.doc.sheetsByIndex;

    Promise.all(sheets.map((s) => s.delete()));
  }

  /**
   * sheet名の一覧を取得する。
   */
  async sheetNames(): Promise<string[]> {
    return this.doc.sheetsByIndex.map((sheet) => sheet.title);
  }

  /**
   * 行を追加する
   *
   * @param sheetTitle シート名
   * @param values 追加する行の値
   */
  async addRow(sheetTitle: string, values: string[] | Record<string, string | number | boolean>) {
    const sheet = this.doc.sheetsByTitle[sheetTitle];

    if (!sheet) {
      return;
    }
    await sheet.addRow(values);
  }

  /**
   * 複数行を追加する
   *
   * @param sheetTitle シート名
   * @param values 追加する行の値の配列
   */
  async addRows(sheetTitle: string, values: (string[] | Record<string, string | number | boolean>)[]) {
    const sheet = this.doc.sheetsByTitle[sheetTitle];

    if (!sheet) {
      return;
    }
    await sheet.addRows(values);
  }

  /**
   * 行の一覧を取得する。
   *
   * @param sheetTitle シート名
   * @param header 項目を取得するヘッダーを指定する
   */
  async getRows(sheetTitle: string, header: string[]) {
    const sheet = this.doc.sheetsByTitle[sheetTitle];

    if (!sheet) {
      return [];
    }
    const rows = await sheet.getRows();

    return rows.map((row) => header.map((h) => row[h]));
  }

  /**
   * ヘッが行を取得する。
   *
   * @param sheetTitle シート名
   * @returns ヘッダの値の配列
   */
  getHeader(sheetTitle: string) {
    const sheet = this.doc.sheetsByTitle[sheetTitle];

    if (!sheet) {
      return [];
    }
    return sheet.headerValues;
  }
}

使われ方のイメージです。

const spreadService = await GoogleSpreadsheetService.getInstance();

// スプレットシートのタイトルを取得
console.log(spreadService.getTitle());

// スプレットシートのタイトルを変更
await spreadService.getRenameTitle('rename title');

// 新規シートを追加
await spreadService.addSheet('newSheet1');
await spreadService.addSheet('newSheet2', ['name', 'email']); // ヘッダーも一緒に設定

// シート名の一覧を取得
console.log(await spreadService.sheetNames());

// 1行追加する
await spreadService.addRow('newSheet1', ['aaa', 'bbb', 'ddd']);
await spreadService.addRow('newSheet2', {name: 'suzuki', email: 'aaa'}); // ヘッダ似合うような形で行を追加

// 複数行追加する
await spreadService.addRow('newSheet1', [['aaa', 'bbb', 'ddd'], ['eee', 'fff]]);
await spreadService.addRow('newSheet2', {name: 'suzuki', email: 'aaa'}, {name: 'sato', email: 'bbb'} ); // ヘッダ似合うような形で行を追加

// 行の一覧を取得
console.log(await spreadService.getRows('newSheet2', ['name', 'email']));

// ヘッダを取得
console.log(await spreadService.getHeader('newSheet2'));

// 削除対象のシート名を指定して、シートを削除
await spreadService.deleteSheetByTitle('newSheet1');

// 全てのシートを削除
await spreadService.deleteAllSheet();

参考

https://zenn.dev/catnose99/scraps/2d0abd8f32f91e

Discussion