10
0

【TypeScript】バックエンド・フロントエンドで共有できるプライベートGithubPackagesの作り方

Last updated at Posted at 2023-12-04

🌟はじめに

本記事はプロもくチャット Adevent Calendar2023の5日目です

🤔 なんで必要なの?

🔥 Firestoreを使っている

僕が所属しているプロダクトでは、下記のような構成を取っています

  • フロントエンド: Vue3/TypeScript
  • バックエンド: Node.js/TypeScript
  • インフラ: Firebase/GCP

📚 Firestoreはカスタムオブジェクト経由でRead/Writeできる

DBは基本的にはFirestore、一部CloudSQLを使っています。
フロントエンドから直接Firestoreの読み書きする部分が多く、随所でバックエンド経由でFirestoreへアクセスするため、フロントエンド・バックエンドともに同じドキュメントやコレクションにアクセスするのが多いです。

FirestoreはNoSQLのため基本的にはスキーマがありませんが、下記のようにモデルクラスと変換メソッド(toFirestore, fromFirestore)を定義することでカスタムオブジェクトを使って保存・読み取りすることができます。
https://firebase.google.com/docs/firestore/query-data/get-data?hl=ja#custom_objects
※このあたりの解説は今回は割愛します

🌐 共有したい、でも普通にnpmパッケージ化すると全世界に公開されてしまう…

上記のような状態なので、モデルクラスや変換メソッドをフロントエンド・バックエンドで共有する必要が出てきました。
別リポジトリをシンボリックリンク化してデプロイしてゴニョゴニョ…してましたが、デプロイが遅くCIのコードもかなり複雑化するため、npmパッケージ化したいと考えました。
が、npmパッケージは全世界に公開されてしまうため、ちょっと微妙…
そこでGithubPackagesを使ってプライベートなnpmパッケージを作成しそれらをVue, Node.jsそれぞれでnpm installして使うようにします。

🎯 ゴール

  • モデルクラス・変換メソッドをGithubのプライベートリポジトリに作成する
  • プライベートリポジトリのコードをGithubPackagesにパッケージを公開する
  • 公開されたパッケージをVue3/Node.jsそれぞれでnpm install XXX@YY.YY.YYのような形で利用することができる

📝 本題

🛠️ モデルクラスをGithubのプライベートリポジトリに作成する

  • さくっと共有したいリポジトリを作成します。
    npm init -y
    npm install typescript -D
    npx tsc --init
    
  • .gitignoreを作成(最低限しか入れてません)
    node_modules
    .DS_Store
    Thumbs.db
    dist/**/*
    
  • 共有したいコードを作る
    src/index.ts
    export class City {
      constructor(
        readonly name: string,
        readonly state: string,
        readonly country: string
      ) {}
      static toFirestore(city: City) {
        return {
          name: city.name,
          state: city.state,
          country: city.country,
        };
      }
    }
    
  • tsconfig.jsonを設定
    僕の担当するプロダクトではNode.jsはCommonJS、VueはESModuleで動いているため、それぞれの形式でビルドする必要があります。このあたりの詳しい話は割愛します。
    下記のような形でtsconfigをCommonJS用、ESModule用に作成します。(ベースとなるtsconfigはデフォルトのままです)
    tsconfig.cjs.json
    {
      "extends": "./tsconfig.json",
      "compilerOptions": {
        "module": "CommonJS",
        "outDir": "./dist/cjs"
      },
      "include": ["src/**/*"],
      "exclude": ["node_modules"]
    }
    
    tsconfig.esm.json
    {
      "extends": "./tsconfig.json",
      "compilerOptions": {
        "module": "esnext",
        "outDir": "./dist/esm"
      },
      "include": ["src/**/*"],
      "exclude": ["node_modules"]
    }
    
  • npm scriptでビルドできるようにする
    package.json
    {
      ...
      "scripts": {
        "build:esm": "tsc -d -p tsconfig.esm.json",
        "build:cjs": "tsc -d -p tsconfig.cjs.json"
      },
      ...
    }
    
  • npm run build:esm, npm run build:cjsで、distディレクトリに出力されることを確認
  • Common.js, ESModule用にそれぞれエンドポイントを定義
    package.json
    {
      ...
      "main": "dist/cjs/index.js",
      "module": "dist/esm/index.js",
      "types": "dist/esm/index.d.ts",
      ...
    }
    

ここまでで、ファイル構成は下記のようになっていると思います

private-common % tree
.
├── dist
│   ├── cjs
│   │   ├── index.d.ts
│   │   └── index.js
│   └── esm
│       ├── index.d.ts
│       └── index.js
├── node_modules
├── package-lock.json
├── package.json
├── src
│   └── index.ts
├── tsconfig.cjs.json
├── tsconfig.esm.json
└── tsconfig.json

📦 プライベートリポジトリのコードをGithubPackagesにパッケージを公開する

  • PATを作成

  • パッケージ名を定義
    プライベートとして発行するにはGithub名 or Oraganization名/リポジトリ名とする必要があります。これを下記のようにpackage.jsonに定義します。

    package.json
    {
      "name": "@IkumaHayashi/private-common",
      ...
    }
    
  • .npmrcファイルを作成
    Github Packagesは、通常のnpmレジストリとは別の場所(https://npm.pkg.github.com)に公開されます。
    このレジストリにプライベートなパッケージを公開するため、下記2点の情報をnpmコマンドに伝えてあげる必要があります

    • レジストリの場所(https://npm.pkg.github.comのこと)
    • プライベートなパッケージをインストールするための秘密鍵(PATのこと)

    この2点が載っているファイルを.npmrcとして作成することで、npmが自動的にこのファイルの情報を読み取り、パッケージの公開やインストール時に使用します。
    ただし、PATはリポジトリ管理するわけにはいきません。そこで、プロジェクト内ではレジストリの場所を、各開発者のホームディレクトリにはPATを配置します。
    下記のようにファイルを作成します。

    プロジェクトルート/.npmrc
    @あなたのGithub名もしくはOraganization名:registry=https://npm.pkg.github.com
    
    ~/.npmrc
    //npm.pkg.github.com/:_authToken=PAT
    
  • .npmignoreを空ファイルで作成

    • .npmignoreはパッケージを発行する際にどのファイルを無視するかを指定するファイルです。
    • npm v9から、.npmignoreがない場合暗黙的に.gitignoreが使用されます。今回の構成ではdistディレクトリをパッケージとして発行したいため、このままではsrcディレクトリなどしか発行されず、使うことができません。
    • そのため空の.npmignoreを定義して除外するファイルをなくすことで、distディレクトリを含めてあげます。
  • 発行
    パッケージの発行はnpm publishで行えます。--dry-runをつけると、発行前にどういう内容で発行されるか確認することができます。

    npm notice 
    npm notice 📦  @IkumaHayashi/private-common@0.0.1
    npm notice === Tarball Contents === 
    npm notice 293B   dist/cjs/index.d.ts
    npm notice 428B   dist/cjs/index.js  
    npm notice 293B   dist/esm/index.d.ts
    npm notice 314B   dist/esm/index.js  
    npm notice 599B   package.json       
    npm notice 264B   src/index.ts       
    npm notice 174B   tsconfig.cjs.json  
    npm notice 172B   tsconfig.esm.json  
    npm notice 12.3kB tsconfig.json      
    npm notice === Tarball Details === 
    npm notice name:          @IkumaHayashi/private-common            
    npm notice version:       0.0.1                                   
    npm notice filename:      @IkumaHayashi/private-common-0.0.1.tgz  
    npm notice package size:  4.5 kB                                  
    npm notice unpacked size: 14.8 kB                                 
    npm notice shasum:        87328bb7206b4661afce03868d8e888ded15194a
    npm notice integrity:     sha512-B3FeOLAppTO3w[...]cb/qtu6kFfrUg==
    npm notice total files:   9                                       
    npm notice 
    npm notice Publishing to https://npm.pkg.github.com (dry-run)
    + @IkumaHayashi/private-common@0.0.1
    

    チェックすべきは下記の2点です

    • パブリッシュ先がhttps://npm.pkg.github.comになっている
    • 発行されるファイル(Tarball Contents)にdistディレクトリが含まれている

    上記が問題なければnpm publishで実際に発行します。

    private-common % npm publish
    npm notice 
    npm notice 📦  @IkumaHayashi/private-common@0.0.1
    npm notice === Tarball Contents ===
    ... 
    npm notice Publishing to https://npm.pkg.github.com
    + @IkumaHayashi/private-common@0.0.1
    

    問題なく発行されました

発行されるとこんな感じでGithubのリポジトリに表示されます
image.png

🚀 インストールしてみる

  • 使用するリポジトリに.npmrcファイルを作成
    プロジェクトルート/.npmrc
    @あなたのGithub名もしくはOraganization名:registry=https://npm.pkg.github.com
    
  • npm install @IkumaHayashi/private-common@0.0.1

💡 補足

🤖 Github Actionsでnpm installするとコケる😭

GithubのPATがないことが原因だと思います。ただ、GithubActionsではルートディレクトリにファイルを作成することができません。
そんなときは、npm installするステップで環境変数にNODE_AUTH_TOKENにPATを定義してあげるとインストールできます

      - name: 依存関係をインストール
        run: |-
          npm install
        env:
          NODE_AUTH_TOKEN: ${{ secrets.PAT }}

🙏 最後に

ここまで読んでくださりありがとうござました!
ちょっと時間がなくてインストール部分は確認できておりません… :bow:
コメントいただければ確認・修正行いますのでお気軽にコメントしてください🙆‍♂️

10
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
0