定年後にWeb開発者目指す https://software.pitang1965.com 2019年1月から学習開始 Wed, 12 Oct 2022 11:39:13 +0000 ja hourly 1 https://wordpress.org/?v=6.2.2 https://software.pitang1965.com/wp-content/uploads/2021/06/cropped-yama512-32x32.png 定年後にWeb開発者目指す https://software.pitang1965.com 32 32 Next.jsでのOGP画像の自動生成 https://software.pitang1965.com/2022/10/12/how-to-generate-open-graph-images-with-nextjs/ https://software.pitang1965.com/2022/10/12/how-to-generate-open-graph-images-with-nextjs/#respond Wed, 12 Oct 2022 11:39:09 +0000 https://software.pitang1965.com/?p=4245 概要
  • 動的にOpen Graph(OG)画像の生成するための方法について、OG Image Generation – Vercel Docsに書いてあるような内容のメモです。
  • Vercelの@vercel/ogライブラリとVercel Edge Functionsを使用してソーシャルカード画像を生成します。

インストール

// pnpmの場合
pnpm add @vercle/og

metaタグの変更(その1)

metaタグを設定している箇所で、contentについて変更します。

<meta name='description' content={props.content} />

↓

<meta
  name='description'
  content={`https://my-og-img.vercel.app/api/og?title=${props.content}`}
/>

APIの追加

src/pages/api/og.tsx を追加しました。

// /pages/api/og.tsx

import { ImageResponse } from '@vercel/og';
import { NextRequest } from 'next/server';

export const config = {
  runtime: 'experimental-edge',
};

export default function handler(req: NextRequest) {
  try {
    const { searchParams } = new URL(req.url);
    // ?title=<title>
    const hasTitle = searchParams.has('title');
    const title = hasTitle ? searchParams.get('title')?.slice(0, 100) : '';

    return new ImageResponse(
      (
        <div
          style={{
            fontSize: 64,
            background: 'white',
            width: '100%',
            height: '100%',
            display: 'flex',
            textAlign: 'center',
            alignItems: 'center',
            justifyContent: 'center',
          }}
        >
          {title ? `@pitang1965 -  ${title }` : '@pitang1965'}
        </div>
      ),
      {
        width: 1200,
        height: 600,
      }
    );
  } catch (e: any) {
    console.error(e.message);
    return new Response('OGP画像の生成に失敗', { status: 500 });
  }
}

開発環境でのテスト

next devを実行し、http://localhost:3000/api/og?title=abc にアクセスします。

OGP画像の自動生成

OGP画像が自動生成されました。ここで、クエリ文字列 title=xxx を変更すると、異なる画像が生成されます。

metaタグの変更(その2)

OGP用のmetaタグを更に追加します。以下は例です。

:
  const siteUrl = 'https://pitang1965-next-portfolio.vercel.app';
  const siteTitle = 'Next Portfolio';
  const siteDescription =
    'Next.jsで作成したPitang1965のポートフォリオサイトです。';
  const imageUrl = `${siteUrl}/api/og?title=${props.content}`;
  const twitter = '@pitang1965';
:
      <Head>
        <title>{siteTitle}</title>
        <meta name='description' content={imageUrl} />
        <meta property='og:url' content={siteUrl} />
        <meta property='og:title' content={siteTitle} />
        <meta property='og:site_name' content={siteTitle} />
        <meta property='og:description' content={siteDescription} />
        <meta property='og:type' content='website' />
        <meta property='og:image' content={imageUrl} />
        <meta name='twitter:card' content='summary_large_image' />
        <meta name='twitter:site' content={twitter} />
        <meta name='twitter:creator' content={twitter} />
        <link rel='icon' href='/favicon.ico' />
      </Head>

デプロイしての確認

Vercel等にデプロイ後に、OGPチェッカーなどで確認します。

OGP画像のテスト

さらなる改良

ここで作成した画像はパッとしませんので、更に改良するためには、src/pages/api/og.tsx を変更します。

]]>
https://software.pitang1965.com/2022/10/12/how-to-generate-open-graph-images-with-nextjs/feed/ 0
GraphQLからTypeScriptの型定義を生成 https://software.pitang1965.com/2022/09/26/how-to-generate-typescript-type-definition-from-graphql/ https://software.pitang1965.com/2022/09/26/how-to-generate-typescript-type-definition-from-graphql/#respond Mon, 26 Sep 2022 07:00:42 +0000 https://software.pitang1965.com/?p=4227 概要

@apollo/client などの一般的なGraphQLクライアントを用いてGraphQL APIからデータを取得するTypeScriptのコードで用いる型定義を、GraphQLサーバーから取得する方法のメモです。

インストール

以下をローカルにインストールします。pnpmの場合で説明。

$ pnpm add -D graphql
$ pnpm add -D @graphql-codegen/cli @graphql-codegen/typescript

設定ファイル

codegen.yaml を次のように設定します。

overwrite: true
schema: "http://hot-dang-homes-course.local/graphql"
generates:
  src/generated/graphql.tsx:
    plugins:
      - "typescript"

GraphQL Code Generator CLIから設定ファイルの元を生成することも可能(詳細はこちら)ですが、生成された設定ファイルで動くとは限りません(余計な設定が追加されてしまう)。

package.jsonに次の1行を追加します。

    "scripts": {
        "dev": "next dev",
        "build": "next build",
        "start": "next start",
        "lint": "next lint",
        "generate": "graphql-codegen --config codegen.yml" <--- ★これ
    },

型定義ファイルの生成

上記の設定の場合、generateスクリプトを実行することにより、src/generated/graphql.tsx が生成されます。

型の利用

例えば次のように利用できます。

type Props = {
  blocks: any;
};

↓

import { Block } from 'src/generated/graphql';

type Props = {
  blocks: Block[];
};

おまけ:WPGraphQL

WPGraphQL General SettingsEnable Public Introspection をチェックつけないとエラーになるようです。

]]>
https://software.pitang1965.com/2022/09/26/how-to-generate-typescript-type-definition-from-graphql/feed/ 0
Next.jsからGitHub GraphQL APIを使用 https://software.pitang1965.com/2022/09/20/how-to-use-github-graphql-api-in-nextjs/ https://software.pitang1965.com/2022/09/20/how-to-use-github-graphql-api-in-nextjs/#respond Tue, 20 Sep 2022 02:02:58 +0000 https://software.pitang1965.com/?p=4222 概要

Next.js/TypeScriptのプロジェクトから、GitHubのGraphQL APIにアクセスする方法を簡単にメモします。Next.jsのapiルートからGraphQL Yogaとgraphql-requestを用い方法になります。

詳細は以下を参照。

GraphQLのエクスプローラ

https://docs.github.com/ja/graphql/overview/explorer にて、GitHubアカウントでサインインした上で、次のクエリを入力します。

query {
  viewer {
    login
  }
}

これで次の結果が得られます。

{
  "data": {
    "viewer": {
      "login": "pitang1965"
    }
  }
}

今回は次のクエリを用います。

query MyQuery {
  viewer {
    name
    repositories(last: 5) {
      nodes {
        name
        description
      }
    }
  }
}

このクエリを実行すると先ほどの結果に加えて、5つのリポジトリの名前と説明が出力されます。

インストール

npm i @graphql-yoga/node graphql graphql-request
又は
yarn add @graphql-yoga/node graphql graphql-request
又は
pnpm i @graphql-yoga/node graphql graphql-request

環境変数の設定

GitHub APIのトークンは、GitHubのサイトのSettings → Developer settings → Personal access tokensから取得できます。

// .env.local
GITHUB_BEARER_TOKEN=xxxx

src/pages/api/github.ts

Next.jsのapiルートからgraphql-requestを用いてGitHub GraphQL APIにアクセスし、その結果をGraphQLサーバーであるGraphQL Yogaを使って/api/github として提供します。

import { GraphQLClient, gql } from 'graphql-request';
import { createServer } from '@graphql-yoga/node';

// GitHubからデータを取得

const resolvers = {
  Query: {
    async repositories() {
      const githubEndPoint = 'https://api.github.com/graphql';

      const query = gql`
        {
          viewer {
            name
            repositories(last: 5) {
              nodes {
                name
                description
              }
            }
          }
        }
      `;

      const graphQLClient = new GraphQLClient(githubEndPoint, {
        headers: {
          authorization: `Bearer ${process.env.GITHUB_BEARER_TOKEN}`,
        },
      });

      const data = await graphQLClient.request(query);
      // console.log(JSON.stringify(data.viewer.repositories.nodes, undefined, 2));
      return data.viewer.repositories.nodes;
    },
  },
};

// APIとして値を返す

const typeDefs = /* GraphQL */ `
  type Query {
    repositories: [Repository!]!
  }
  type Repository {
    name: String
    description: String
  }
`;

const server = createServer({
  schema: {
    typeDefs,
    resolvers,
  },
  endpoint: '/api/github',
  // graphiql: false // uncomment to disable GraphiQL
});

export default server;

クライアント側のコード

以下はuseSWRを用いて、/api/githubにアクセスし、5つのリポジトリの名前と説明を表示するコードです。

import useSWR from 'swr';

const fetcher = (query: string) =>
  fetch('/api/github', {
    method: 'POST',
    headers: {
      'Content-type': 'application/json',
    },
    body: JSON.stringify({ query }),
  })
    .then((res) => res.json())
    .then((json) => json.data);

export default function Index() {
  const { data, error } = useSWR(
    `{
      repositories {
        name
        description
      }
    }
    `,
    fetcher
  );

  console.log(data);

  if (error) return <div>Failed to load</div>;
  if (!data) return <div>Loading...</div>;

  const { repositories } = data;

  return (
    <div>
      {repositories.map((repository: any, i: number) => (
        <div key={i}>
          <h1>{repository.name}</h1>
          <p>{repository.description}</p>
        </div>
      ))}
    </div>
  );
}

出力イメージは次です。

pitang-todo
Vite + React + TypeScipt + Supabase による簡単なtodoアプリ

gudid-search
openFDAを用いて企業名からUDI(機器固有識別子)の一覧を得て表で表示。

git_training
next-portfolio
各種APIを用いた自己紹介サイト

qin-team-dev
qinサロンチーム開発
]]>
https://software.pitang1965.com/2022/09/20/how-to-use-github-graphql-api-in-nextjs/feed/ 0
Access to fetch at ‘https://xxx from origin ‘https://yyy has been blocked by CORS policy https://software.pitang1965.com/2022/09/11/access-to-fetch-at-https-xxx-from-origin-https-yyy-has-been-blocked-by-cors-policy/ https://software.pitang1965.com/2022/09/11/access-to-fetch-at-https-xxx-from-origin-https-yyy-has-been-blocked-by-cors-policy/#respond Sun, 11 Sep 2022 04:31:42 +0000 https://software.pitang1965.com/?p=4213 Next.jsapiルートで作ったAPIを、StackBlitz等の他のドメインから使用しようとしたときに、次のようなエラーが出ました。

Access to fetch at 'https://pitang1965-next-portfolio.vercel.app//api/tweet' from origin 'https://vitejs-vite-hxwvfs--5173.local.webcontainer.io' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

これを修正するには、APIのほうをなんとかしないといけないようです。

次でできました。

インストール

nextjs-corsをインストールします。

$ npm install nextjs-cors

又は

$ yarn add nextjs-cors

又は

$ pnpm install nextjs-cors

apiルートのコード変更

:

import NextCors from 'nextjs-cors';   <-- ★追加

export default async function twitter (
  req: NextApiRequest,
  res: NextApiResponse
) {
    await NextCors(req, res, {   <-- ★追加
      methods: ['GET'],
      origin: '*',
      optionSuccessStatus: 200,
    });
    :
]]>
https://software.pitang1965.com/2022/09/11/access-to-fetch-at-https-xxx-from-origin-https-yyy-has-been-blocked-by-cors-policy/feed/ 0
CSS Modulesで複数クラスを指定する方法 https://software.pitang1965.com/2022/08/12/how-to-specify-multiple-classes-in-css-modules/ https://software.pitang1965.com/2022/08/12/how-to-specify-multiple-classes-in-css-modules/#respond Fri, 12 Aug 2022 13:15:12 +0000 https://software.pitang1965.com/?p=4196 概要

Next.js では何も設定せずにコンポーネントレベルでCSSを使用できる CSS Modules が使用できます。つまり、コンポーネント間でのクラス名の重複など気にしないことができます。Tailwind CSS が気に入らない場合は、候補になりうると思います。使い方は簡単です。

ルール

  1. xxx.module.css というファイル名を使用する。例:Button.module.css
  2. 次のように利用する。
import styles from './Button.module.css'

export function Button() {
  return (
    <button
      type="button"
      className={styles.button} ← ★Button.module.css に .button { ... } があること。

ただし、次のように複数のクラス名やハイフン付きを指定できません。

className={ styles.button styles.button-darkmode }

解決方法

classnames を使用します。

$ npm i classnames

又は

$ yarn add classnames

又は

$ pnpm i classnames

次のようにします。少し変な書き方ですが、ハイフンの問題も解決します。

:

import cx from 'classnames';

:

<Container
  className={cx(styles.container, 
    styles['mobile-layout'])}
]]>
https://software.pitang1965.com/2022/08/12/how-to-specify-multiple-classes-in-css-modules/feed/ 0