GraphQLでのBFFを構築の基本（React、gRPC、BFF(GraphQL)）

### 1. バックエンド、BFF

#### バックエンド（gRPC）

1. **gRPCのインストール**  
   gRPCとProtocol Buffersを使用するためのパッケージをインストールします。

   ```bash
   npm install @grpc/grpc-js @grpc/proto-loader
   ```

2. **Protocol Buffersの定義**  
   `.proto`ファイルを作成し、APIのインターフェースを定義します。

   ```proto
   syntax = "proto3";

   package example;

   service ExampleService {
       rpc GetData (DataRequest) returns (DataResponse);
   }

   message DataRequest {
       string id = 1;
   }

   message DataResponse {
       string data = 1;
   }
   ```

3. **gRPCサーバーの実装**  
   定義したサービスを実装します。

   ```javascript
   const grpc = require('@grpc/grpc-js');
   const protoLoader = require('@grpc/proto-loader');

   const packageDefinition = protoLoader.loadSync('example.proto', {});
   const proto = grpc.loadPackageDefinition(packageDefinition).example;

   const server = new grpc.Server();

   server.addService(proto.ExampleService.service, {
       GetData: (call, callback) => {
           callback(null, { data: 'Hello from gRPC!' });
       },
   });

   server.bindAsync('0.0.0.0:50051', grpc.ServerCredentials.createInsecure(), () => {
       server.start();
       console.log('gRPC server running on port 50051');
   });
   ```

#### BFFのセットアップ（Express + GraphQL）

1. **ExpressとApollo Serverのインストール**  

   ```bash
   npm install express apollo-server-express graphql @types/express
   ```

2. **TypeScriptの設定**  
   TypeScriptを使用するために、必要なパッケージをインストールし、`tsconfig.json`を設定します。

   ```bash
   npm install typescript ts-node @types/node --save-dev
   ```

   `tsconfig.json`の基本設定：

   ```json
   {
     "compilerOptions": {
       "target": "ES6",
       "module": "commonjs",
       "outDir": "./dist",
       "rootDir": "./src",
       "strict": true,
       "esModuleInterop": true
     },
     "include": ["src/**/*"],
     "exclude": ["node_modules"]
   }
   ```

3. **BFFの実装**  
   ExpressとApollo Serverを使用してGraphQLサーバーを実装し、gRPCサーバーと通信します。

   `src/index.ts`を作成します。

   ```typescript
   import express from 'express';
   import { ApolloServer, gql } from 'apollo-server-express';
   import * as grpc from '@grpc/grpc-js';
   import * as protoLoader from '@grpc/proto-loader';

   const app = express();
   const PORT = 4000;

   // gRPCクライアントの設定
   const packageDefinition = protoLoader.loadSync('example.proto', {});
   const proto = grpc.loadPackageDefinition(packageDefinition).example;

   const client = new proto.ExampleService('localhost:50051', grpc.credentials.createInsecure());

   // GraphQLのスキーマ定義
   const typeDefs = gql`
     type Query {
       getData(id: String!): String
     }
   `;

   // リゾルバの定義
   const resolvers = {
     Query: {
       getData: (_: any, { id }: { id: string }) => {
         return new Promise((resolve, reject) => {
           client.GetData({ id }, (error: any, response: any) => {
             if (error) return reject(error);
             resolve(response.data);
           });
         });
       },
     },
   };

   // Apollo Serverの設定
   const server = new ApolloServer({ typeDefs, resolvers });
   server.applyMiddleware({ app });

   app.listen(PORT, () => {
     console.log(`🚀 Server ready at http://localhost:${PORT}${server.graphqlPath}`);
   });
   ```

### 2. React

1. **Reactのセットアップ**  
   Create React Appを使用して新しいプロジェクトを作成します。

   ```bash
   npx create-react-app my-app --template typescript
   cd my-app
   ```

2. **Apollo Clientのインストール**  

   ```bash
   npm install @apollo/client graphql
   ```

3. **GraphQLクエリの作成**  
   ReactコンポーネントからApollo Clientを使用してBFFにリクエストを送信します。

   `src/App.tsx`を以下のように設定します。

   ```typescript
   import React from 'react';
   import { ApolloClient, InMemoryCache, ApolloProvider, useQuery, gql } from '@apollo/client';

   const client = new ApolloClient({
     uri: 'http://localhost:4000/graphql', // BFFのURL
     cache: new InMemoryCache(),
   });

   const GET_DATA = gql`
     query GetData($id: String!) {
       getData(id: $id)
     }
   `;

   const DataComponent: React.FC<{ id: string }> = ({ id }) => {
     const { loading, error, data } = useQuery(GET_DATA, { variables: { id } });

     if (loading) return <p>Loading...</p>;
     if (error) return <p>Error: {error.message}</p>;

     return <div>Data: {data.getData}</div>;
   };

   const App: React.FC = () => (
     <ApolloProvider client={client}>
       <DataComponent id="1" />
     </ApolloProvider>
   );

   export default App;
   ```