GravatarとRemixでプロフィール画像や自己紹介文を集約管理する

この記事では、Gravatarを使用してプロフィール情報や画像を効率的に管理する方法が紹介されています。Gravatarに登録したデータをREST APIで取得したり、Remixアプリで利用する方法が解説されています。APIを活用した情報取得やプロフィール構築の手順が詳細に説明されており、新しい挑戦に向けた展望も示されています。Gravatarを活用することで情報管理や更新作業が容易になり、他のサイトへの切り替えも検討されています。

広告ここから
広告ここまで

目次

    この記事では、複数のサイトやサービス・イベントでプロフィール情報や画像を効率的にシェアするため、Gravatarというサービスを使っている話を紹介します。Gravatarに登録したデータをREST APIで取得する方法や、Remixアプリでプロフィール要素を作成する方法をまとめました。これは「TypeScript Advent Calendar 2024」11日目の記事です。

    登壇頻度が高い or 複数ブログを管理すると、プロフィール情報の管理が煩雑になる

    多くの場合、運営者情報や登壇者情報はサイトごと・イベントごとに登録や共有しているかと思います。年に数回の登壇であったり、管理するサイトが1つか2つであれば、都度作業でも問題ないと思います。ただ、複数のコミュニティイベント・カンファレンスにCFPを応募したり、4-5つのサイト管理している場合には、どこかに情報をまとめておきたくなります。そこで今使っているのが、Automattic社の提供するProfile as a ServiceのGravatarです。

    https://ja.gravatar.com/

    Gravatarにプロフィールや画像を登録しておくことで、REST APIを使ってデータを取得したり、リサイズ可能な画像URLを取得できるようになります。このサイトのプロフィールページは、Gravatarの提供するREST APIを利用して表示しています。これはGravatar for developersでAPIキーを取得し、それを利用したAPIリクエストを送信しています。

    Gravatarでプロフィール情報をfetchする

    https://docs.gravatar.com/

    ドキュメントにAPIの利用方法は紹介されています。自分の場合は、次のようなコードでデータを取得しました。慣れるのに時間がかかりそうだなと思う点として、メールアドレスをハッシュ化する必要がある点です。そのため、ハッシュを生成する関数と、それを利用してAPIリクエストを投げる処理の2つに分けてコードを書いています。

    const url = 'https://api.gravatar.com/v3';
    const gravatarAPIKey =
      'YOUR_API_KEY';
    const email = '[email protected]';
    
    async function generateSHA256Hash(email: string): Promise<string> {
      const encoder = new TextEncoder();
      const data = encoder.encode(email.toLowerCase().trim());
      const hashBuffer = await crypto.subtle.digest('SHA-256', data);
      const hashArray = Array.from(new Uint8Array(hashBuffer));
      return hashArray.map((b) => b.toString(16).padStart(2, '0')).join('');
    }
    
    generateSHA256Hash(email).then((hash) => {
      fetch(`${url}/profiles/${hash}`, {
        headers: {
          Authorization: `Bearer ${gravatarAPIKey}`,
        },
      })
        .then((data) => data.json())
        .then((data) => {
          console.log(data);
        });
    });
    

    SDKを利用せずにAPIを呼び出すため、型定義は自前で行いました。APIレスポンスを確認しながら作成したコードがこちらです。

    export type GravatarAccount = {
      service_type: string;
      service_label: string;
      service_icon: string;
      url: string;
    };
    export type GravatarProfile = {
      display_name: string;
      description: string;
      verified_accounts: Array<GravatarAccount>;
      profile_url: string;
      avatar_url: string;
      avataer_alt_text: string;
      location: string;
      job_title: string;
      company: string;
      pronunciation: string;
      pronouns: string;
      timezone: string;
      languages: unknown[];
      first_name: string;
      last_name: string;
      is_organization: boolean;
      links: Array<{
        label: string;
        url: string;
      }>;
      interests: unknown[];
      payments: {
        links: unknown[];
        crypto_wallets: unknown[];
      };
      contact_info: {
        home_phone: string;
        work_phone: string;
        cell_phone: string;
        email: string;
        contact_form: string;
        calendar: string;
      };
      gallery: Array<{
        url: string;
        alt_text: string;
      }>;
      number_verified_accounts: number;
      last_profile_edit: string;
      registration_date: string;
    };
    
    async function generateSHA256Hash(email: string) {
      const encoder = new TextEncoder();
      const data = encoder.encode(email.toLowerCase().trim());
      const hashBuffer = await crypto.subtle.digest('SHA-256', data);
      const hashArray = Array.from(new Uint8Array(hashBuffer));
      return hashArray.map((b) => b.toString(16).padStart(2, '0')).join('');
    }
    
    export const getGravatarProfile = async (emailAddress: string, gravatarAPIKey: string) => {
      const hash = await generateSHA256Hash(emailAddress);
      const entry = await fetch(`https://api.gravatar.com/v3/profiles/${hash}`, {
        headers: {
          Authorization: `Bearer ${gravatarAPIKey}`,
        },
      }).then((res) => res.json<GravatarProfile>());
      return entry;
    };

    Remixで使う場合は、loader内で取得する

    作成したAPI呼び出し関数を、Remixのloaderで呼び出しています。Cloudflareにデプロイしているため、環境変数はargs.context.cloudflare.envから取得しています。APIキーもですが、メールアドレスも環境変数から取得する形にしました。

    
    export async function loader(args: LoaderFunctionArgs) {
      const { GRAVATAR_EMAIL_ADDRESS, GRAVATAR_API_KEY } = args.context.cloudflare.env;
      /**
       * Gravator profile
       */
      const gravatarProfile = await getGravatarProfile(GRAVATAR_EMAIL_ADDRESS, GRAVATAR_API_KEY)
    ...

    あとは型定義を参考にReact Componentを実装するだけです。Tailwind UIを利用したため、マークアップのコードは紹介できませんが、このようなプロフィール要素をGravatarのデータ・画像だけで構築しています。

    作ってみて

    Gravatarに登録している情報を都度参照しているため、管理してるサイトやSNSアカウントの増減や画像の変更、肩書きが変わった場合などの対応もGravatar上の作業だけで完了します。他のサイトはRemix以外で実装しているため、まだ完了していませんが、こちらも順次Gravatarに切り替える予定です。

    悩ましい点としては、パフォーマンスでしょうか。そこまで高頻度に変更するものではありませんので、ラップするREST APIを作成してキャッシュするか、サイトごとにWorkers KVやDynamoDB・Momentoなどでキャッシュすべきか、それとも管理の手軽さを優先してこのままでいくべきか。この辺りもまた何か新しい挑戦をしたときはブログでお知らせします。

    広告ここから
    広告ここまで
    Home
    Search
    Bookmark