GAミント至上主義

Web Monomaniacal Developer.

2021年振り返り

GitHub統計

シニアジョブのレポジトリには3,028回コミットしてた

f:id:uyamazak:20211229162740p:plain

出社回数

健康診断と新規採用の顔合わせで2回?

ざっくり時系列

会社のメインのレポジトリは通年ちょこちょこやってたのでそれ以外

1−3月

会社が歌舞伎町に引っ越しで、NuxtJS + Google App Engine + Googleスプレッドシートで受付システム作ってた。

Google Apps ScriptとNuxtJSで簡易来客受付システムをつくったら実質サーバーコスト無料だった - GAミント至上主義

業務に必要なデータを集めるためにWEBサイトのスクレイピングをPuppeteerとTypeScriptでやってた。

Puppeteer + TypeScriptでWEBサイトをスクレイピングしたメモ - GAミント至上主義

スタンディングデスク導入

2月からスタンディングデスクに移行した。2021年12月でも使用中で腰の痛みから開放された。 1年過ぎたら別記事にしたい

4−7月

AWSAWS Elastic Beanstalkで動いているメインのシステムをFargate化しようと格闘してた。 それは完了できず、他の人に引き継いでいるけど、下記のサテライトサイトはFargateで動いてる。 AWSはTerraform必須なのを実感。

サテライトサイト作成

uyamazak.hatenablog.com

主力の業種である会計に特化した求人検索サイトをNuxtJS 2とComposition APIで作ってた。

サーバーサイドは既存のLaravelに追加する形だったけどPHPとTypeScriptの往復はつらかった記憶。

あと長男生まれた

とりさん地図アプリ

Viteを試すのと自身の埼玉力を上げるために作り始めた。

地図部分はできたけど、肝心のテキストコンテンツを作っていくところで、長男誕生とNestJSの新プロジェクトがはじまってリソース不足で止まってしまっている。

8−12月

新しい求人サービスのプロジェクトを開始。

NestJS + NuxtJSの両方TypeScriptの構成。NestJSに関しては完全に未経験で、情報も少ないため、最初は依存解決とかで難航したけど 慣れてくるとフロントとサーバーの型共有や、GraphQLも使って非常に快適な開発ができた。

NestJS自体はまさにフレームワークという言葉どおり、いろんなNode.jsの有名ライブラリを必要に応じて組み合わせていくための枠組みといった感じで、

細かい実装自体には、RoRみたいにこうすればよい、っていうのがほとんど無く、技術力が求められるものだと感じた。

雑につぶやいたら、日本語なのにCore Teamの人がメンションくれたの驚いた。

ドキュメントが本当によくできてる。

また、Firebaseの利用(Auth、Storage)、初めてのElasticsearchの新規構築、E2Eテスト環境構築(DB、Elasticsearch、Firebaseエミュレータ等、モックではなく全部動く)、 CIの活用(E2Eテストやlintはもちろんマイグレ差分検知など)等、やりたいと思ってた理想を全部実現できてる感じがする。いい職場だなぁ。

既存のシニアジョブに続いて、2つ目の大きなサービスとなる予定だけど、求人ドメインに関する6年?以上の知見がある分 最初の要求が非常に大きなものになってしまい、データ設計、選択項目などのマスターデータ等が最初から巨大になってしまうという問題を実感した。

DBのテーブル設計に時間をかけてしまったけど、もっと早く部分的にでもフロントからDBまで、通しで動く状態にするべきだったなぁという教訓。

社内向け管理画面は現時点でベータ版のNuxt3を使って作っているけど、ViteやNitroをはじめ快適すぎてもう昔には戻れないので、ユーザー側もNuxt3で行きたいなぁという気持ち。

仕事着探し

新生児+3歳児+リモートワークで服に限界を感じていたので、至高の日本製ジャージや、リモートワーク用作業着などを探求してた。

コスパはもちろん、耐久性、汚れ耐性、動きやすさ等引き続き探求したい。

芸人も愛用のカネマスの2本線ジャージと、バートルの作業着7051シリーズはいいぞ。

Macbook Pro 16インチ M1 Pro

キーボードもバッテリーも限界がきてた2017年のMacbookPro 15インチ(ESCキーがタッチバー)から買い替えてもらって、ESCキーが物理キーになった。

ARM系のM1になったので心配してたけど、開発環境はMySQLのDockerが動かなかったのをMariaDBにしたぐらいで思ったより何事もなかった。

あとESCキーが物理キーになって、各ビルドやDocker系の動作など待ち時間が短縮されたのと、ESCキーが物理キーになった。

ARM普及していくとラズパイもいろいろ便利になったりクラウドもARM化が進行しそう。

2022年

社長がAdobe XD始めたり、デザインに力を入れてきたので、そっちも力をつけていきたい。

デザイン強めのフロントエンジニアの方にいろいろ教えてもらいたい。

NestJSにだいぶお世話になってるので何かコミュニティ活動したいなぁ。

あと年明けから経験浅めの新しい人入るので、能力発揮できるよう手伝っていきたい。フルリモートでどうやろう

TypeORMのEntityでGetterを使うのをやめてAfterLoadでセットするようにした

TypeORMを使っているプロジェクトで、よくやるEntityのfirstNameとlastNameをくっつけてfullnameを返す方法として、getterを使って実装していました。

getterはTypeScriptというかJavaScriptの標準機能なのでまあ良いかと思っていましたが、 TypeScript: Documentation - Classes

↓この仕様があるので

プロパティが参照された時に関数が呼び出されるようにします ゲッター - JavaScript | MDN

EntityをそのままJSONレスポンスとして返すときには付いておらず、いちいちサーバー側で明示的に参照してセットする必要がありました。

擬似コードですがこんな感じ。特にUserが配列のとき、他のEntityに含まれている時などは特に面倒なことになってました。

before

user.entity.ts

@Entity()
export class User {
  @Column({ comment: '姓' })
  lastName: string

  @Column({ comment: '名' })
  firstName: string

  public get fullname(): string {
    return `${this.lastName} ${this.firstName}`
  }
}

コントローラーで使うとき

// fullnameは入ってない
const user = await userRepository.findOne({ id })
return { ...user, fullname: user.fullname }

試してはないけど、おそらくEntityのtoJSONなどを書き換えれば自動でつくようにもできそうだけどやりたくない。

そこでこの記事にもあるAfterLoadを使った形式にしてみたところ、今回の用途では問題なく使えそうでした。

javascript - How I can use getter and setter in TypeORm - Stack Overflow

typeorm/listeners-and-subscribers.md at master · typeorm/typeorm · GitHub

after

user.entity.ts

@Entity()
export class User {
  @Column({ comment: '姓' })
  lastName: string

  @Column({ comment: '名' })
  firstName: string

  fullname = ''

  @AfterLoad
  updateVirtualField {
    this.fullname = `${this.lastName} ${this.firstName}`
  }
}

コントローラーで使うとき

// fullname入ってる!
return await userRepository.findOne({ id })

DB側にカラムはいらないので@Columnはつけずにただのメンバー変数として定義しておきます。 そこにAfterLoadをつけたメソッドで必要な値をセットするだけ。

デメリットとして、名前の通りロード時にセットするだけなので、変更に対してリアルタイム性はないというのがあります。

が、表示以外の処理で使う予定もなさそうなので問題なさそう。

今のプロジェクトでは、姓名をくっつけたフルネームのほか、enum値の表示用ラベルなどをこの形式で追加するようにしています。

今回はSetterはまだ使ってないけど、同じように@BeforeInsertを使ってやれば問題なさそう

axiosでinterceptorsを使って毎リクエスト直前にヘッダーをセットする

Firebase AuthのidToken(JWT)を、サーバー側(NestJS)へのリクエストで使う場面で、 サーバーへのリクエスト時にAuthorizationヘッダーにaccess_tokenを付与する必要があった。

FirebaseのユーザーはNuxt3のuseStateで入れてuseFirebaseUserで使っているけど今回は省略。

最初はAPIリクエスト用のaxiosインスタンスを作って、baseURLと一緒にAuthorizationヘッダーもセットしていて、最初は動きはする。

before 期限切れになる

const makeIntance = async () => {
    const instance = axios.create({
      baseURL
    });
    const user = useFirebaseUser()
    if (user.value) {
      const token = await user.value.getIdToken()
      instance.defaults.headers.common['Authorization'] = `bearer ${token}`
    }
    return instance
  }

でも、トークンの有効期限は1時間程度なので、ページを1時間以上開きっぱなしの状態でリクエストすると401エラーになってしまう。 postとかgetとかリクエストの直前にセットすることできないかなぁと探したら、まさにそれ用のInterceptorsという機能があった。 リクエスト、レスポンス、どちらにも介入することができる。

axiosのinterceptorsで、リクエストの前処理を共通して行う - Qiita

axios-http.com

今回はリクエストの前にgetIdToken()を実行してセットするようにした。 getIdToken()はPromiseを返すので動くかな?と思ったけど問題なかった。

after

const makeIntance = async () => {
    const instance = axios.create({
      baseURL
    });
    const user = useFirebaseUser()
    if (user.value) {
      instance.interceptors.request.use(async (request) => {
        const token = await user.value.getIdToken()
        request.headers.common['Authorization'] = `bearer ${token}`
        return request
      })
    }
    return instance
  }