開発
積薪の技術構造
設計からバックエンドまで、私の最初のフルスタック プロジェクト
積薪のプロジェクトは、複数のバックエンドモジュール(blog、auto、auth、user)から成り立ち、記事のCRUD操作、ユーザーの登録・ログイン・認可、自動記事更新のクローリングなどを担当します。フロントエンドにはSvelteKitを採用し、最適化としてCloudflareを利用しました。Nest.jsとMongoDBはDocker Composeで起動し、パフォーマンス最適化にはキャッシュとSSRが用いられました。プロジェクトを進める中で、バックエンドとフロントエンドの開発における深い理解が深まり、自信を持ってプロジェクトに取り組むことができるようになりました。
- #Node.js
- #Nest.js
- #MongoDB
- #AI
- #SvelteKit
- #Cloudflare
16
積薪は、同類の製品とは異なる独立ブログナビゲーションサイトです。データの取得と分類は自動で行われますが、手動による厳選推薦もあります。長文の執筆と読書が希少スキルとなったこの時代に、著者と読者のためのトラフィックチャネルを構築できればと願っています。
積薪に関するより感性的な紹介と文芸的な表現については、「文字の力を信じ続ける:独立ブログナビゲーションサイト‘積薪’正式リリース」をご覧ください。本記事では主にサイトの技術アーキテクチャについて紹介します。
背景
Node.jsの入門チュートリアルを終えた後、独立して完全なバックエンドプロジェクトを作りたいと思い、バックエンドへの恐怖を克服しました。これはデバッグが直感的でないためであり、バックエンド開発全体を理解したいと思ったからです。
以前、Node.jsを使って画像のEXIFを取得し、返すインターフェースを作成し、自分のブログで長期間実行していました。しかし、これは非常にシンプルなプロジェクトであり、Expressを使ってルーティングを行い、情報を抽出し返すだけのものでした。その後、データベースの読取りと書込みを追加しましたが、それでも小さなツールに過ぎず、本物のバックエンドプロジェクトとは言えません。このプロジェクトの紹介については、「Fetch EXIFがアップグレードされました」および「ChatGPTを使用してNode.js APIを実現する」をご覧ください。
独立ブログナビゲーションサイトは以前から考えていたアイデアであり、複雑さの点で入門に最適です。自動実行のクローラタスクがあり、データベースのCRUD操作もあります。今後、さらに多くの機能を追加することができます。
バックエンド
バックエンドにはNode技術スタックを選び、フレームワークとしてNest.jsを使用しました。
Nestは多くの便利なモジュールを統合しており、データベースとのやり取り、認証と認可も簡単に処理できます。また、Nestのドキュメントが非常に充実している点も大切です。
積薪のバックエンドはいくつかのモジュールに分かれています: blog、auto、auth、user。
authとuserはユーザーの登録とログイン、および認可が必要なインターフェース--つまりデータの増減と変更の保護を担当します。
blogには2つのコントローラがあります:articleとwebsite。名前が示す通り、記事およびブログデータのCRUD操作を担当します。
autoは定期的なタスクであり、定期的に記事の更新をクローリングし、アクセス可能性を検出し、データを統計します。
クローリングの根拠はRSSファイルです。データベースに存在しない記事を発見した場合、記事データを抽出し、一連の処理を行った後、データベースに保存します。
データベースにはMongoDBを使用しているため、記事とサイトの間には強い関連性がありません。各記事のアクセス数を総計するのは毎日定時に行われ、リアルタイムではありません。
ほとんどの独立ブログは長続きしないため、バックエンドは毎月すべての記事のアクセス可能性を検査し、失われたページに対してラベルを付けます。次のバージョンでは、無効な記事の表示を追加する予定です。
AI
情報をただ取得して表示するだけでは非常に効率が悪いです。なぜなら、各人の興味が異なるためです。そこで、バイドゥのAPIを使用して取得した記事を分類、ラベル付け、要約整理しました。ホームページで表示されるラベルや分類情報はすべて自動で行われたものです。
フロントエンド
今回は、より馴染みのあるReactではなく、現行のフロントエンド界で評価の高いSvelteKitを採用し、自分を追い込んで新しいフレームワークを学びました。
Svelteはほとんど実行時がないため、最終的なバンドルには大量のjsファイルが含まれません。同時にそのreactivity特性により、useState()のようなメソッドを書く必要がありません。
しかし、Svelteの欠点は明白で、まずエコシステムはReactほど整っていません。また、ドキュメントと情報も十分ではありません。認証やデプロイなどの処理でも、あまり多くの参考資料がありません。すべて初めから車輪を作り直さなければなりません。
フロントエンドの構造は比較的シンプルです。訪問者が見ることができる部分以外に、ログインが必要な管理バックエンドがあります。
デプロイ
Nest.jsとMongoDBはDocker Compose経由で1台のサーバーで起動し、外部にインターフェースを提供します。フロントエンドはCloudflare Pagesを通じてデプロイします。
もともとSvelteとNest.jsは同じサーバーで実行され、DNS解析もDNSpodで行っていました。先日、DNSpodから違反コンテンツがあるため解析を停止すると通知されたメールを受け取りました。
そこで私はDNS解析とフロントエンドをCloudflareに移しました。中国大陸の訪問者は考慮していません。
性能最適化
フロントエンドの最適化はほとんどCloudflareが提供し、主にキャッシュです。短時間の大量アクセスでもバックエンドにリクエストが届かず、バックエンドの負担を減らします。
以前、もう一つの最適化を行いました。
積薪は100% SSRサイトです。インターフェースもAPI Routeで二次封装されており、クライアントリクエストはバックエンドのURLを暴露しません。
クローリングの頻度は昼間に4時間に1回行われ、データの更新は頻繁ではありません。すべてのアクセスにインターフェースリクエストが必要であれば、サーバにかなりの負担がかかります。
それで、Svelteのhooks.server.tsにキャッシュロジックを設定しました。このファイルはSvelteが受け取ったすべてのリクエストを処理します。リクエストを処理してデータをRedisにキャッシュすれば、ほとんどのアクセスでバックエンドにアクセスする必要がなくなります。
これの機能を完了させたところ、新たな問題が発生しました。例えば、誰かが1ページ目を訪問し、このときデータがredisにキャッシュされます。その後、データが更新され、次の人が1ページ目を訪問すると、キャッシュが読み取られ、2ページ目を訪問すると更新後のデータが読み取られます。
これにより2番目の訪問者が2つのページで重複データを見ることになるかもしれません。解決方法はありますが、後にCloudflareを導入したため、このredisキャッシュは不要になりました。
感想
プロジェクトを自分でやってみることで、各プロセスについてさらに深く理解できます。バックエンド開発の思考はフロントエンドとは大きく異なります。以前はバックエンドに依存しないシンプルなプロジェクトしかできませんでしたが、今では専門的なバックエンドサポートがなくても臆することはなくなりました。最悪、自分でゆっくりやればいいのです。