GraphSQLミドルウェア( Node ,アポロサーバ,急行)の書き方


この記事ではNode.js apollo-server-expressgraphql-middleware パッケージ.
私は、あなたがノードに精通していると仮定します.アポロサーバ、エクスプレス、およびES 6 +構文.
私はセットアップのほとんどをスキップします、そして、あなたはすでにLaTeSQL APIがアポロサーバでセットアップしたと仮定します.では、インストールしましょうgraphql-middleware and graphql-tools .
yarn add graphql-middleware graphql-tools
// or 
npm install graphql-middleware graphql-tools
次に、インデックスファイルを使用してミドルウェアフォルダを作成します.もちろん、これはあなたが好きな構造です.
mkdir src/middleware && touch src/middleware/index.js
現在、我々はミドルウェアをアポロサーバ建設者に加えなければなりません.だから、あなたのサーバーに移動します.JSファイル(またはアポロのインスタンスを作成する場所).
まず、これらの関数をインポートします.
import { applyMiddleware } from 'graphql-middleware';
import { makeExecutableSchema } from 'graphql-tools';
次にアポロサーバのインスタンスに追加します.
import resolvers from './resolvers' // returns array of resolvers
import middleware from './middleware' // returns array of middelware

// this combines all of the resolvers
const executableSchema = makeExecutableSchema({ typeDefs: schema, resolvers });
const schemaWithMiddleware = applyMiddleware(executableSchema, ...middleware);

const server = new ApolloServer({
    playground: true,
    typeDefs: schema,
    resolvers,
    context: async ({ req, res }) => ({ req, res }), // now we can access express objects from apollo context arg 
    schema: schemaWithMiddleware, // add this property
});
さて、セットアップは完了しました、現在、我々は若干のミドルウェアを書く準備ができています.この例では、サーバーに着信要求をチェックするいくつかのミドルウェアを作成します.
ミドルウェアフォルダにファイルを作りましょうtouch src/middleware/getUserFromCookie.jsさて、忘れないうちにこのファイルをミドルウェア/インデックスにインポートしましょう.jsファイル
import getUserFromCookie from './getUserFromCookie';

export default [getUserFromCookie];
このモジュールの計画を立てましょう.私はよくコメントを書きます.
// TODO
// 1. get session cookie from express request object
// 2. use session id to get user details
// 3. add user to Apollo args
// 4. specify which resolvers to add the middleware to
さあ準備ができました.番号1から始めましょう.
async function getUserFromCookie(req) {
  try {
    const { clientSession } = req.cookies; // requires cookie-parser middleware

    if (!clientSession) {
      throw new Error('session cookie does not exist');
    }

    return await getUser(clientSession); // get user details from Database
  } catch (error) {
    throw new AuthenticationError(`Cannot get user from cookie: \n ${error}`);
  }
}
何が起こっているのですか.どこでreq Param !ベアーウィズミー.この関数を後で呼び出し、この引数を渡します.
簡単にあなたのクッキーへのアクセスを得るために、我々がこの機能でここに着くように、あなたはcookie-parser ミドルウェアパッケージ.この品はこれから出ます.
このミドルウェアがどんなミドルウェアも見つけることができないなら、我々はクライアントにAPIへのどんなアクセスも得ることを妨げるべきです.私たちはアポロサーバを定義済みのエラーの非常に有用なコレクションを使用することができます.
我々は、スキップしますgetUser この記事では、APIのユーザーデータの取得方法について説明します.
それで、それは1をカバーします.と2 .我々のtodosから、3に移りましょう.アポロARGSにユーザーの詳細を追加します.これにより、指定されたリゾルバのユーザの詳細にアクセスすることができます.
async function addUserToArgs(resolve, parent, args, context, info) {
  const user = await getUserFromCookie(context.req);
  const argsWithUser = { user, ...args };

  return resolve(parent, argsWithUser, context, info);
}
これがミドルウェア機能だ.注意すべき点
  • この関数に渡される4つの引数はすべてのミドルウェアに渡されます.
  • 前に来るコードresolve リゾルバが実行される前に実行されます
  • の後のどんなコードでもresolve 関数は、リゾルバが実行された後に実行されます
  • レゾルバに渡す引数を選択できます.この場合、ユーザオブジェクトをargsに追加したので、レゾルバにアクセスできますargs.user
  • この時点で、おそらくどのようにリゾルバは、このミドルウェアを使用するかを選択する疑問があります.これは私たちのtodosからポイント番号4に私たちをもたらします.
    リゾルバ名をキーとし、ミドルウェア関数を値としてエクスポートする必要があります.GraphSQLミドルウェアパッケージは、この関数が指定されたレゾルバで実行されることを保証するためにいくつかの魔法を働かせます.
    export default {
      Query: {
        getUserDetails: addUserToArgs,
      },
      Mutation: {
        updateUserDetails: addUserToArgs,
      },
    };
    
    OK、私たちはほとんど完了です!しかし、この時点では、すべてのリゾルバ(または多くのレゾルバ)にいくつかのミドルウェアを追加したい場合は、これはすぐに退屈になり、非常に困難になるAPIの成長として維持する.
    このため、引数としてレゾルバの配列、およびミドルウェア機能を受け入れるヘルパー関数を書きました.これは、配列としてレゾルバを使用して1つのオブジェクトを返します.以下はヘルパー関数の使い方です.
    // import array of objects with Query and Mutaion properties
    import resolvers from '../../resolvers';
    import addMiddlewareToResolvers from './addMiddlewareToResolvers';
    
    // pass array of resolvers and middleware function
    export default addMiddlewareToResolvers(resolvers, addUserToArgs);
    
    /*
      return {
        Query: {
          getUserDetails: addUserToArgs
          // rest of the queries
        },
        Mutation: {
          updateUserDetails: addUserToArgs
          // rest of the mutations
        }
      }
    */
    
    そしてここに関数があります.誰でもこれを単純化することができて、それをより読みやすいようにするならば、ちょっと複雑です、私はそれを見たいです!
    import { ApolloError } from 'apollo-server-express'
    
    // returns object with resolver names as keys, and middleware function as value
    export default function addMiddleware(
      resolvers,
      middlewareFunction,
    ) {
      try {
        return resolvers?.reduce(
          (a, c) => buildResolverObject(a, c, middlewareFunction),
          {},
        )
      } catch (error) {
        throw new ApolloError(`Error in addMiddlewareToResolvers - ${error}`)
      }
    }
    
    function buildResolverObject(
      accumulator: any,
      { Query, Mutation },
      middlewareFunction: any,
    ) {
      const queryProperties = getResolverProperties(Query, middlewareFunction)
      const mutationProperties = getResolverProperties(Mutation, middlewareFunction)
    
      return {
        Query: {
          ...accumulator.Query,
          ...queryProperties,
        },
        Mutation: {
          ...accumulator.Mutation,
          ...mutationProperties,
        },
      }
    }
    
    function getResolverProperties(resolverObject = {}, middlewareFunction) {
      const keys = Object.keys(resolverObject)
      const properties = keys.map((key) => ({ [key]: middlewareFunction }))
    
      return properties.reduce((a, c) => ({ ...a, ...c }), {})
    }
    
    それで🎉
    今、あなた自身のカスタムミドルウェアを書く準備が整いました.楽しい!
    P . S .このミドルウェアのためにJestを使って統合テストを書く方法に興味がありますか?カミングスーン😎