次.は、アポロクライアントとサーバー


ここでは二つのことを述べます.
  • ハウツーフィットNext.js with Apollo Client フロントエンドとApollo Server GraphQL 単一のAPIExpress アプリ.もう一つの重要な要件はSSR サポート.そこにそれについて多くの情報がないので、これはこのガイドの主な目的です.
  • あらゆるものをうまく整理する方法yarn workspaces monorepo に展開Heroku つの無料プランアプリとして.あなたはそれについて多くを見つけるでしょう、しかし、私は私が働いていたプロジェクトのプロセスの一部としてここにそれを含めました.
  • 通常、すべてを一緒に置く必要はないかもしれません.しかし、私は全体のスタックを迅速かつショーケースのための同じレポで必要とした.私も使用したいTypeScript ボーナスとして.

    合格基準

  • 次.反応フロントエンド
  • グラフィカルなAPI
  • シングルエントリポイント/ホスト/サーバ
  • シングルリポジトリ
  • まだパッケージ化されたパッケージ:クライアント、API、サーバー.後の他のサービス
  • SSRサポート
  • どこでも
  • ホットリロード
  • herokuに展開
  • 取得し、ベータ版を実行する1時間以上かかる必要があります
  • TLR


    ソースコードhere

    ステップ

  • design
  • monorepo
  • graphql api
  • client app
  • server
  • connecting everything together
  • setting up heroku
  • deploying
  • 1 .デザイン

    Here is how you would usually want to use graphql - as a API Gateway between client app and back end services:


    基本的に同じことをしていますが、サーバのルーティングは以下のようになります.

    以下に、メインパッケージの依存関係図を示します:

    モノレポの設定

    We want every service in a single repo, but at the same time decoupled - monorepo . We can do it seamlessly with the help of yarn workspaces .

    Folder structure:

    root
     |- packages
     |   |- client
     |   |- graphql
     |   |- server
     |- package.json
     |- yarn.lock
    

    package.json :

    {
     "name": "monorepo",
     ...
      "scripts": {
        "build": "yarn workspace @monorepo/client run build",
        "start": "yarn workspace @monorepo/server run start",
        "dev": "export $(cat .env | xargs) && yarn workspace @monorepo/server run dev"
      },
      "private": true,
      "workspaces": ["packages/*"],
      "engines": {
        "node": "13.x"
      }
    }
    

    No dependencies here. private": true is required by yarn workspaces. "workspaces": [...] declares where our packages live. Each script executes yarn command in specified workspace. In dev script we read local development environment variables from .env file before starting dev server. (If it does not work on your OS, replace with what works for you)

    .env :

    NODE_ENV=development
    PORT=3000
    GRAPHQL_URI=http://localhost:3000/graphql
    

    Let's agree on the naming convention for our packages: @monorepo/package-name .


    Grapql APIの設定

    This one is the easiest.

    packages/graphql/package.json :

    {
      "name": "@monorepo/graphql",
      ...
      "dependencies": {
        "apollo-server-express": "2.12.0"
      }
    }
    

    packages/graphql/index.ts :

    import { ApolloServer, gql } from 'apollo-server-express';
    
    const typeDefs = gql`
      type Query {
        hello: String
      }
    `;
    
    const resolvers = {
      Query: {
        hello: () => 'Hello world!',
      },
    };
    
    const server = new ApolloServer({ typeDefs, resolvers });
    
    export default server;
    

    Everything super simple: schema, reducer. At the end we create Apollo Server, export it, but do not start it right away.


    クライアントアプリケーションの設定

    This one is trickier. We need to make Next js use Apollo Client for fetching the data and make sure SSR is supported.

    To bootstrap the Next.js app, I followed this quick start guide .jsアプリ.しかし、我々は特定の変更を必要とします.packages/client/package.json :
    {
      "name": "@monorepo/client",
      ...
      "scripts": {
        "dev": "next",
        "build": "next build",
        "start": "next start"
      },
      "dependencies": {
        ...
      }
    }
    
    何も特別.
    今、設定するApollo Client with SSR , レッツコピー/apolloClient.js and /lib/apollo.js からnext.js/examples/with-apollo .
    我々は修正する必要があるapolloClient.js わずかに
    ...
    
    export default function createApolloClient(initialState, ctx) {
      return new ApolloClient({
        ssrMode: Boolean(ctx),
        link: new HttpLink({
          uri: process.env.GRAPHQL_URI, // must be absolute for SSR to work
          credentials: 'same-origin',
          fetch,
        }),
        cache: new InMemoryCache().restore(initialState),
      });
    }
    
    リンクをポイントします.ローカルのdevサーバのいずれか、またはHerokuホストにGRAPHQL_URI 環境変数.URLは/graphql デフォルトではSSRが動作するように絶対パスを置かなければなりません.なぜ私に尋ねるな.
    私たちは2ページ、1つのSSRを持っています.packages/client/pages/index.ts :
    import React from 'react';
    import { useQuery } from '@apollo/react-hooks';
    import Layout from '../components/Layout';
    import gql from 'graphql-tag';
    import { withApollo } from '../apollo/apollo';
    
    const QUERY = gql`
      query GetHello {
        hello
      }
    `;
    
    const NOSSR = () => {
      const { data, loading, error, refetch } = useQuery(QUERY);
    
      if (loading) return <p>Loading...</p>;
      if (error) return <p>Error: {error.message}</p>;
    
      return (
        <Layout>
          <h1>This should be rendered on client side</h1>
          <pre>Data: {data.hello}</pre>
          <button onClick={() => refetch()}>Refetch</button>
        </Layout>
      );
    };
    
    export default withApollo({ ssr: false })(NOSSR);
    
    どのように簡潔に注意してくださいuseQuery フックは.ビューティ.我々のページをラップするwithApollo({ ssr: false })(NOSSR) SSRを有効/無効にする.もう一つのほとんど同じページがあります.packages/client/pages/ssr.ts でもssr: true .
    最後にpackages/client/index.ts :
    import next from 'next';
    
    const nextApp = next({
      dev: process.env.NODE_ENV !== 'production',
      dir: __dirname,
    });
    
    export default nextApp;
    
    我々は、次に作成しています.JSのアプリは、後でそれをエクスポートするには、Expressで使用されます.

    Expressサーバの設定

    Alright, its time to stitch everything together.

    packages/server/package.json :

    {
      "name": "@monorepo/server",
      ...
      "scripts": {
        "start": "ts-node index.ts",
        "dev": "nodemon index.ts"
      },
      "dependencies": {
        "express": "4.17.1",
        "ts-node": "8.8.2",
        "typescript": "3.8.3"
      },
      "devDependencies": {
        "nodemon": "2.0.3",
        "@types/node": "13.11.1"
      }
    }
    

    We'll use ts-node to run our TypeScript app on production, it will compile it and keep the build in cache. We'll use nodemon for the hot reload. Latest versions have built in TypeScript support, no need to do anything other than nodemon index.ts . Magic!

    And the epxress server itself packages/server/index.ts :

    import express from 'express';
    
    import nextApp from '@monorepo/client';
    import apolloServer from '@monorepo/graphql';
    
    const { PORT } = process.env;
    
    async function main() {
      const app = express();
    
      await bootstrapApolloServer(app);
      await bootstrapClientApp(app);
    
      app.listen(PORT, (err) => {
        if (err) throw err;
        console.log(`[ server ] ready on port ${PORT}`);
      });
    }
    
    async function bootstrapClientApp(expressApp) {
      await nextApp.prepare();
      expressApp.get('*', nextApp.getRequestHandler());
    }
    
    async function bootstrapApolloServer(expressApp) {
      apolloServer.applyMiddleware({ app: expressApp });
    }
    
    main();
    

    Notice how we import client and graphql packages. That's possible thanks to yarn workspaces simlinking.

    Next.js and Apollo Server have different express APIs. Next creates request handler that can be used as express middleware:

    await nextApp.prepare();
    expressApp.get('*', nextApp.getRequestHandler());
    

    Apollo Server does the same thing, but inside applyMiddleware method:

    apolloServer.applyMiddleware({ app: expressApp });
    

    devサーバの起動

    Now that we have all the source code ready, from root run:

    yarn install
    

    This will instal all the dependencies and do the simlinking between our packages. If you inspect the content of root node_modules in eg VS Code editor, you'll notice something like this:


    私たちのmonorepoパッケージがルートnodeoundモジュールに加えられたように見えます、しかし、矢アイコンはそれらがちょうどファイルシステムの対応する場所を指しているsimlinksであることを示します.非常に良い!
    さあ、先に進んでrootから走れ.
    yarn dev
    

    アプリを開くhttp://localhost:3000 .

    ネットワークログから、xhrリクエストがあったことがわかります/graphql ページがレンダリングされた後.クリックするならrefetch またはSSR ページのリンクでは、余分なリクエストが送信されます.データが既にアポロクライアントキャッシュに存在して、明示的な指示なしで再フェッチされる習慣であるので.マジック再び!
    今、我々は再ロードする場合SSR ページは、ページが表示された後にXHR要求がないことに気付きます.そして、我々がページ源を調べるならば、我々はそれを見ますData: Hello world! テキストが既にあります.SSRは期待通りに動作します.
    最後にhttp://localhost:3000/graphql . devモードでは、アポロGraphhlのプレイグラウンド画面が表示されます.

    Herokuアプリの設定

    I wont describe much about the process of setting up new account and creating the app, but its pretty straight forward and should not take longer than 5 minutes.

    • Go to https://www.heroku.com/ , crete a free plan account.
    • Do to your dashboard https://dashboard.heroku.com/apps
    • Click New -> Create new app , choose app name, region, and click Create app .

    You will land on the page with instructions of how to install heroku cli and deploy your app.

    One more thing you have to do is to set up GRAPHQL_URI env var. Go to Settings tab in heroku dashboard. In Domains section you will find text Your app can be found at https://your-app-name.herokuapp.com/ . Copy that url, scroll up to the Config Vars section and create new env var with key GRAPHQL_URI and value https://your-app-name.herokuapp.com/graphql :


    8 .配備

    heroku login
    git init
    git add .
    git commit -am "make it better"
    git push heroku master
    

    This will initiate the deployment process. Now here is the last Magical part. Heroku will recognize that your app is NodeJS based, you don't have to configure anything yourself. Moreover, Heroku will figure out that you use yarn as a package manager and will run yarn install after it fetches the source files. Then it will investigate your root package.json, find build script and run it. Finally it will look for the start script and use it to start the app by default. Awesome. All the set up literally take about 15 minutes if you don't have an existing account.


    大丈夫、あなたのHerokuアプリのURLに移動し、我々はすべて設定します.