{"id":48064,"date":"2023-03-30T02:56:16","date_gmt":"2023-01-09T03:13:11","guid":{"rendered":"https:\/\/www.silicloud.com\/zh\/blog\/%e6%88%91%e5%b0%9d%e8%af%95%e7%94%a8postgraphile%e6%9b%bf%e6%8d%a2%e4%ba%86%e9%99%84%e5%b8%a6%e7%9a%84graphql%e5%90%8e%e7%ab%af%ef%bc%8c%e5%b9%b6%e4%b8%94%e8%bf%98%e6%9c%89vue-apollo%e9%99%84%e5%b8%a6\/"},"modified":"2024-04-29T21:05:53","modified_gmt":"2024-04-29T13:05:53","slug":"%e6%88%91%e5%b0%9d%e8%af%95%e7%94%a8postgraphile%e6%9b%bf%e6%8d%a2%e4%ba%86%e9%99%84%e5%b8%a6%e7%9a%84graphql%e5%90%8e%e7%ab%af%ef%bc%8c%e5%b9%b6%e4%b8%94%e8%bf%98%e6%9c%89vue-apollo%e9%99%84%e5%b8%a6","status":"publish","type":"post","link":"https:\/\/www.silicloud.com\/zh\/blog\/%e6%88%91%e5%b0%9d%e8%af%95%e7%94%a8postgraphile%e6%9b%bf%e6%8d%a2%e4%ba%86%e9%99%84%e5%b8%a6%e7%9a%84graphql%e5%90%8e%e7%ab%af%ef%bc%8c%e5%b9%b6%e4%b8%94%e8%bf%98%e6%9c%89vue-apollo%e9%99%84%e5%b8%a6\/","title":{"rendered":"\u6211\u5c1d\u8bd5\u7528PostGraphile\u66ff\u6362\u4e86\u9644\u5e26\u7684GraphQL\u540e\u7aef\uff0c\u5e76\u4e14\u8fd8\u6709Vue-Apollo\u9644\u5e26\u7684\u6f14\u793a\u804a\u5929\u5e94\u7528\u7a0b\u5e8f&#8221; ApolloChat&#8221;"},"content":{"rendered":"<p>\u7531\u4e8e\u5f53\u524d\u60c5\u51b5\u4e0b\u4ee3\u7801\u89e3\u91ca\u4e0d\u8db3\uff0c\u6211\u4f1a\u5728\u4e4b\u540e\u589e\u8865\u8bf4\u660e\u3002<\/p>\n<h1>\u9019\u7bc7\u6587\u7ae0\u4e2d\u8981\u505a\u7684\u4e8b\u60c5<\/h1>\n<p>\u5c06\u9644\u5e26\u7684 &#8220;ApolloChat&#8221; GraphQL \u670d\u52a1\u5668\uff0c\u4f7f\u7528 PostGraphile \u5b9e\u73b0\u5e76\u66ff\u6362\uff0c\u4ee5\u5c06\u6570\u636e\u6301\u4e45\u5316\u5230 PostgreSQL \u4e2d\u3002\u4ee5\u540e\u6211\u4eec\u5c06\u628a\u9644\u5e26\u7684 &#8220;ApolloChat&#8221; \u79f0\u4e3a &#8220;\u539f\u59cb ApolloChat&#8221;\uff0c\u5c06\u66ff\u6362\u4e3a PostGraphile \u7684 &#8220;ApolloChat&#8221; \u79f0\u4e3a &#8220;PostGraphile \u7248 ApolloChat&#8221;\u3002<\/p>\n<h2>PostGraphile\u7248ApolloChat\u7684\u6e90\u4ee3\u7801<\/h2>\n<p>\u60a8\u53ef\u4ee5\u4ecehttps:\/\/github.com\/kanedaq\/vue-apollo \u83b7\u53d6\u8be5\u9879\u76ee\u3002\u8981\u8fd0\u884c\u5b83\uff0c\u8bf7\u6267\u884c\u4ee5\u4e0b\u6b65\u9aa4\u4ee5\u542f\u52a8 Web \u670d\u52a1\u5668\uff0c\u5e76\u4ece\u6d4f\u89c8\u5668\u8bbf\u95ee\u201chttp:\/\/localhost:8080\/demo\/\u201d\u3002<\/p>\n<pre class=\"post-pre\"><code>git clone https:\/\/github.com\/kanedaq\/vue-apollo\r\ncd vue-apollo\/tests\/demo\/\r\nyarn install\r\nyarn serve\r\n<\/code><\/pre>\n<h2>\u300c\u5b9e\u65bd\u4e2d\u7684\u61d2\u60f0\uff08\u5e38\u89c1\u4e8e\u6f14\u793a\u4e2d\uff09\u300d<\/h2>\n<ul class=\"post-ul\">\n<li style=\"list-style-type: none;\">\n<ul class=\"post-ul\">https\u3067\u901a\u4fe1\u3059\u3079\u304d\u3068\u3053\u308d\u3082http\u3067\u901a\u4fe1\u3057\u3066\u3044\u307e\u3059\u3002<\/ul>\n<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<ul class=\"post-ul\">\n<li style=\"list-style-type: none;\">\n<ul class=\"post-ul\">\u672c\u8a18\u4e8b\u3067\u306fJWT\u3092\u30ed\u30fc\u30ab\u30eb\u30b9\u30c8\u30ec\u30fc\u30b8\u306b\u4fdd\u5b58\u3057\u3066\u3044\u307e\u3059\u304c\u3001\u672c\u756a\u74b0\u5883\u3067\u306f\u306a\u3055\u3089\u306a\u3044\u3088\u3046\u304a\u9858\u3044\u3057\u307e\u3059\u3002HTML5\u306eLocal Storage\u3092\u4f7f\u3063\u3066\u306f\u3044\u3051\u306a\u3044\uff08\u7ffb\u8a33\uff09 (2019-10-10\u8ffd\u8a18)<\/ul>\n<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<ul class=\"post-ul\">\n<li style=\"list-style-type: none;\">\n<ul class=\"post-ul\">\u30ed\u30b0\u30a2\u30a6\u30c8\u51e6\u7406\u306f\u30b5\u30fc\u30d0\u30fc\u5074\u306f\u4f55\u3082\u305b\u305a\u306btrue\u3092\u8fd4\u3059\u3060\u3051\u306b\u3057\u3001\u30d5\u30ed\u30f3\u30c8\u5074\u3067\u30d6\u30e9\u30a6\u30b6\u306e\u30ed\u30fc\u30ab\u30eb\u30b9\u30c8\u30ec\u30fc\u30b8\u306eJWT\u3092\u524a\u9664\u3059\u308b\u306b\u7559\u3081\u307e\u3057\u305f\u3002\u304d\u3063\u3061\u308a\u5b9f\u88c5\u3057\u305f\u3044\u5834\u5408\u306f\u3001\u4ee5\u4e0b\u306e\u30b5\u30a4\u30c8\u7b49\u3092\u3054\u53c2\u7167\u304f\u3060\u3055\u3044\u3002<\/ul>\n<\/li>\n<\/ul>\n<p>JSON Web Token \u3092\u5931\u52b9\u3055\u305b\u308b<br \/>\nSPA\u3067\u30ed\u30b0\u30a2\u30a6\u30c8\u3057\u305f\u6642\u306b\u3059\u3079\u304d\u51e6\u7406\u3063\u3066\u4f55\u3067\u3059\u304b\uff1f<\/p>\n<p>\u30ed\u30b0\u30a4\u30f3\u4e2d\u306b&#8221;jwt expired&#8221;\u306e\u305f\u3081\u306b\u52d5\u4f5c\u304c\u602a\u3057\u304f\u306a\u3063\u305f\u3089\u3001\u30ed\u30b0\u30a2\u30a6\u30c8\u30dc\u30bf\u30f3\u3092\u62bc\u3057\u3066\u5f37\u5236\u7684\u306bJWT\u3092\u524a\u9664\u3057\u3066\u304f\u3060\u3055\u3044\u3002<br \/>\n\u30ed\u30b0\u30a4\u30f3\u753b\u9762\u3067&#8221;jwt expired&#8221;\u306e\u305f\u3081\u306b\u30ed\u30b0\u30a4\u30f3\u3067\u304d\u306a\u304f\u306a\u3063\u305f\u3089\u3001\u30d6\u30e9\u30a6\u30b6\u306e\u30ed\u30fc\u30ab\u30eb\u30b9\u30c8\u30ec\u30fc\u30b8\u306b\u6b8b\u3063\u3066\u3044\u308bJWT\uff08\u30ad\u30fc\uff1apostgraphile-demo-token\uff09\u3092\u624b\u52d5\u3067\u524a\u9664\u3057\u3066\u304f\u3060\u3055\u3044\u3002<br \/>\n\u305d\u306e\u4ed6\u3001PostGraphile\u3078\u306e\u79fb\u690d\u306b\u969b\u3057\u3066\u3001\u5b9f\u88c5\u306e\u629c\u3051\u6f0f\u308c\u304c\u3042\u308b\u304b\u3068\u601d\u3044\u307e\u3059\u3002<br \/>\n\u79c1\uff08C++\u304a\u3058\u3055\u3093\uff09\u306f\u307e\u3060JavaScript\u3082Vue.js\u3082\u7d4c\u9a13\u304c\u6d45\u3044\u306e\u3067\u3001\u5909\u306a\u7b87\u6240\u304c\u3042\u308a\u307e\u3057\u305f\u3089\u512a\u3057\u304f\u3054\u6307\u6458\u304f\u3060\u3055\u3044\u3002<\/p>\n<h2>\u8bf7\u53c2\u8003\u8be5\u9875\u9762\uff08\u611f\u8c22\u60a8\uff09\u3002<\/h2>\n<p>GraphiQL\u65e0\u6cd5\u6307\u5b9a\u6807\u5934\u7684\u89e3\u51b3\u65b9\u6cd5<br \/>\nVue\uff1a\u65e0\u6548\u7684\u4e3b\u673a\u6807\u5934<br \/>\n\u6ce8\u610f\u4e8b\u9879\uff1a\u5728\u5b50\u76ee\u5f55\u4e2d\u5206\u53d1Vue.js\u9879\u76ee<br \/>\n\u4f7f\u7528Vue CLI3\u7684DevServer\u4f7f\u7528WebSocket<br \/>\n\u5c1d\u8bd5\u5728nginx\uff081.3.13\uff09\u4e2d\u4f7f\u7528WebSocket\u4ee3\u7406<br \/>\n\u4f7f\u7528Node.js\u548clog4js\u8f93\u51fa\u65e5\u5fd7<br \/>\nutil.inspect\u7684\u4fbf\u5229\u6027<\/p>\n<h1>\u83b7\u53d6vue-apollo\uff0c\u5e76\u5c1d\u8bd5\u8fd0\u884c\u539f\u59cb\u7684ApolloChat\u3002<\/h1>\n<p>\u5728\u5c06GraphQL\u540e\u7aef\u8f6c\u79fb\u5230PostGraphile\u4e4b\u524d\uff0c\u9996\u5148\u5c1d\u8bd5\u8fd0\u884c\u539f\u59cb\u7684ApolloChat\u3002\u6211\u5c06\u5728~\/work\u76ee\u5f55\u4e0b\u8fdb\u884c\u64cd\u4f5c\uff08\u6211\u4f7f\u7528\u7684\u662fMac\uff09\u3002<\/p>\n<p>\u83b7\u53d6 vue-apollo\u3002<\/p>\n<pre class=\"post-pre\"><code>cd ~\/work\r\ngit clone https:\/\/github.com\/Akryum\/vue-apollo\r\ncd vue-apollo\r\n<\/code><\/pre>\n<p>\u5728\u8fd9\u91cc\u8fd0\u884c&#8221;gut log&#8221;\u547d\u4ee4\u4f1a\u663e\u793a\u5982\u4e0b\u5185\u5bb9\u3002<\/p>\n<pre class=\"post-pre\"><code>$ git log\r\ncommit 9419ffd03d5c0942ac5954572920ffa2281f1851 (HEAD -&gt; master, origin\/master, origin\/HEAD)\r\nAuthor: Darryl Hein &lt;dhein@xmmedia.com&gt;\r\nDate:   Tue May 28 11:38:41 2019 -0600\r\n\r\n    docs: Minor spelling correction (#635)\r\n(\u5f8c\u7565)\r\n<\/code><\/pre>\n<p>\u5728\u539f\u59cbApolloChat\u7684\u76ee\u5f55\u4e2d\u5207\u6362\uff0c\u5e76\u5b89\u88c5\u6240\u9700\u7684\u8f6f\u4ef6\u5305\u3002<\/p>\n<pre class=\"post-pre\"><code>cd ~\/work\/vue-apollo\/tests\/demo\r\nyarn install\r\n<\/code><\/pre>\n<p>\u6211\u5c06\u7acb\u5373\u8bd5\u8fd0\u884c\u539f\u7248ApolloChat\u3002\u8981\u67e5\u627e\u542f\u52a8\u65b9\u6cd5\uff0c\u8bf7\u8fdb\u884c\u68c0\u7d22\u3002<\/p>\n<pre class=\"post-pre\"><code>more package.json\r\n<\/code><\/pre>\n<p>\u663e\u793a\u5982\u4e0b\u3002<\/p>\n<pre class=\"post-pre\"><code>{\r\n  \"name\": \"demo\",\r\n  \"version\": \"0.1.0\",\r\n  \"private\": true,\r\n  \"scripts\": {\r\n    \"serve\": \"vue-cli-service serve\",\r\n    \"build\": \"vue-cli-service build\",\r\n    \"lint\": \"vue-cli-service lint\",\r\n    \"apollo:dev\": \"vue-cli-service apollo:watch\",\r\n    \"apollo:run\": \"vue-cli-service apollo:run\",\r\n    \"test:e2e:dev\": \"start-server-and-test apollo:dev http:\/\/localhost:4000\/.well-known\/apollo\/server-health test:e2e:dev:client\",\r\n    \"test:e2e:dev:client\": \"vue-cli-service test:e2e --mode development\",\r\n    \"test:e2e\": \"start-server-and-test apollo:run http:\/\/localhost:4000\/.well-known\/apollo\/server-health test:e2e:client\",\r\n    \"test:e2e:client\": \"vue-cli-service test:e2e --mode production --headless\",\r\n    \"test\": \"yarn run lint --no-fix &amp;&amp; yarn run test:e2e\"\r\n  },\r\n<\/code><\/pre>\n<p>\u5728\u4e2d\u6587\u4e2d\uff0c\u539f\u59cb\u7684ApolloChat\u542f\u52a8\u547d\u4ee4\u662f:<br \/>\n&#8211; GraphQL\u670d\u52a1\u5668: yarn apollo:dev\u6216yarn apollo:run<br \/>\n&#8211; Web\u670d\u52a1\u5668\uff08Vue.js\u5e94\u7528\u7a0b\u5e8f\u53d1\u5e03\uff09: yarn serve<br \/>\n\u770b\u8d77\u6765\u4e0d\u9519\u3002<\/p>\n<p>\u9996\u5148\uff0c\u6267\u884c\u201cyarn apollo:dev\u201d\u4ee5\u542f\u52a8GraphQL\u670d\u52a1\u5668\uff0c\u5c06\u663e\u793a\u5982\u4e0b\u5185\u5bb9\u3002<\/p>\n<pre class=\"post-pre\"><code>warning ..\/..\/..\/package.json: No license field\r\n$ vue-cli-service apollo:watch\r\nwarning ..\/..\/..\/package.json: No license field\r\n$ vue-cli-service apollo:run --delay\r\nUsing default PubSub implementation for subscriptions.\r\nYou should provide a different implementation in production (for example with Redis) by exporting it in 'apollo-server\/pubsub.js'.\r\n\u2714\ufe0f  GraphQL Server is running on http:\/\/localhost:4000\/graphql\r\n\u2714\ufe0f  Type rs to restart the server\r\n<\/code><\/pre>\n<p>\u6309\u7167\u6307\u793a\uff0c\u5728\u6d4f\u89c8\u5668\u4e2d\u8bbf\u95ee\u201chttp:\/\/localhost:4000\/graphql\u201d\uff0c\u4f1a\u51fa\u73b0\u7c7b\u4f3c\u5b98\u65b9\u7684GraphiQL\u7684\u754c\u9762\u3002\u672c\u6587\u5c06\u7701\u7565\u4e0d\u8868\uff0c\u4f46\u60a8\u53ef\u4ee5\u5c1d\u8bd5\u5728\u201c~\/work\/vue-apollo\/tests\/demo\/src\/graphql\/\u201d\u76ee\u5f55\u4e0b\u7684gql\u6587\u4ef6\u4e2d\u7f16\u5199\u7684GraphQL\u67e5\u8be2\u3002\u60a8\u53ef\u4ee5\u5728\u754c\u9762\u7684\u5de6\u4e0b\u65b9\u8f93\u5165\u201cHTTP HEADERS\u201d\uff0c\u8fd9\u6837\u60a8\u5c31\u53ef\u4ee5\u5c06\u767b\u5f55\u8fc7\u7a0b\u4e2d\u8fd4\u56de\u7684JWT\u8bbe\u7f6e\u4e3aHEADERS\uff0c\u4ee5\u4fbf\u5c1d\u8bd5\u9700\u8981\u767b\u5f55\u7684\u67e5\u8be2\u3002<\/p>\n<p>\u5728\u4fdd\u6301GraphQL\u670d\u52a1\u5668\u7684\u540c\u65f6\uff0c\u53ef\u4ee5\u5728\u53e6\u4e00\u4e2a\u63a7\u5236\u53f0\u4e0a\u542f\u52a8Web\u670d\u52a1\u5668\u3002<\/p>\n<pre class=\"post-pre\"><code>cd ~\/work\/vue-apollo\/tests\/demo\r\nyarn serve\r\n<\/code><\/pre>\n<p>\u7f16\u8bd1\u5b8c\u524d\u7aef\u540e\uff0c\u663e\u793a\u5982\u4e0b\u3002<\/p>\n<pre class=\"post-pre\"><code> DONE  Compiled successfully in 7780ms                                  17:51:01\r\n\r\n\r\n  App running at:\r\n  - Local:   http:\/\/localhost:8080\/ \r\n  - Network: unavailable\r\n\r\n  Note that the development build is not optimized.\r\n  To create a production build, run yarn build.\r\n<\/code><\/pre>\n<p>\u6309\u7167\u6307\u793a\uff0c\u5728\u6d4f\u89c8\u5668\u4e2d\u8bbf\u95ee\u201chttp:\/\/localhost:8080\/\u201d\uff0c\u539f\u59cbApolloChat\u5c06\u542f\u52a8\u3002<br \/>\n\u9996\u5148\u4f1a\u51fa\u73b0\u767b\u5f55\u9875\u9762\u3002<\/p>\n<div><img decoding=\"async\" class=\"post-images\" title=\"\" src=\"https:\/\/cdn.silicloud.com\/blog-img\/blog\/img\/657d82d5913a08637a6b1920\/31-0.png\" alt=\"\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8 2019-06-13 9.55.48.png\" \/><\/div>\n<p>\u521b\u5efa\u8d26\u53f7\u9875\u9762\u3002<\/p>\n<div><img decoding=\"async\" class=\"post-images\" title=\"\" src=\"https:\/\/cdn.silicloud.com\/blog-img\/blog\/img\/657d82d5913a08637a6b1920\/33-0.png\" alt=\"\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8 2019-06-13 9.56.01.png\" \/><\/div>\n<p>\u521b\u7acb\u8d26\u53f7\u5e76\u767b\u5f55\u540e\uff0c\u51fa\u73b0\u7684\u6b22\u8fce\u754c\u9762\u3002<\/p>\n<div><img decoding=\"async\" class=\"post-images\" title=\"\" src=\"https:\/\/cdn.silicloud.com\/blog-img\/blog\/img\/657d82d5913a08637a6b1920\/35-0.png\" alt=\"\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8 2019-06-13 9.56.23.png\" \/><\/div>\n<p>\u8fdb\u5165\u901a\u7528\u9891\u9053\uff0c\u6253\u7b97\u53d1\u9001\u201c\u4f60\u597d\uff0c\u4e16\u754c\u201d\u7684\u754c\u9762\u3002<\/p>\n<div><img decoding=\"async\" class=\"post-images\" title=\"\" src=\"https:\/\/cdn.silicloud.com\/blog-img\/blog\/img\/657d82d5913a08637a6b1920\/37-0.png\" alt=\"\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8 2019-06-13 9.58.08.png\" \/><\/div>\n<p>\u53d1\u9001\u4e86\u300c\u4f60\u597d\uff0c\u4e16\u754c\u300d\u540e\u7684\u5c4f\u5e55\u3002<\/p>\n<div><img decoding=\"async\" class=\"post-images\" title=\"\" src=\"https:\/\/cdn.silicloud.com\/blog-img\/blog\/img\/657d82d5913a08637a6b1920\/39-0.png\" alt=\"\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8 2019-06-13 9.58.16.png\" \/><\/div>\n<p>\u5c1d\u8bd5\u4e86\u5404\u79cd\u64cd\u4f5c\u540e\uff0c\u53d1\u73b0\u539f\u7248\u7684ApolloChat\u7684GraphQL\u670d\u52a1\u5668\u5c06\u6570\u636e\u4fdd\u5b58\u5728\u5185\u5b58\u4e2d\uff0c\u800c\u4e0d\u8fdb\u884c\u6301\u4e45\u5316\uff0c\u8fd9\u610f\u5473\u7740\u4e00\u65e6\u5173\u95edGraphQL\u670d\u52a1\u5668\uff0c\u6240\u521b\u5efa\u7684\u8d26\u6237\u548c\u804a\u5929\u5185\u5bb9\u5c06\u4f1a\u4e22\u5931\u3002<\/p>\n<h1>\u540e\u7aef\u5b9e\u73b0<\/h1>\n<p>\u90a3\u4e48\uff0c\u8ba9\u6211\u4eec\u5f00\u59cb\u5c06\u539f\u59cb\u7684ApolloChat\u7684GraphQL\u670d\u52a1\u5668\u8fc1\u79fb\u5230PostGraphile\u3002\u6570\u636e\u5c06\u6301\u4e45\u5316\u5b58\u50a8\u5728PostgreSQL\u4e2d\u3002<\/p>\n<h2>\u73af\u5883\u642d\u5efa\u548c\u540e\u7aef\u5b9e\u73b0\uff08\u7b2c\u4e00\u90e8\u5206\uff09\uff1a\u6d89\u53ca\u5230PostgreSQL\u7684\u76f8\u5173\u4e8b\u9879\u3002<\/h2>\n<p>\u7531\u4e8e\u4e4b\u524d\u5df2\u7ecf\u5199\u8fc7\u4e00\u7bc7\u540d\u4e3a\u201c\u5c1d\u8bd5\u4f7f\u7528PostgREST\u548cPostGraphile\u6765\u63d0\u4f9bPostgreSQL\u64cd\u4f5cAPI\u201d\u7684\u6587\u7ae0\u4e2d\u7684\u201c\u73af\u5883\u8bbe\u7f6e1\u201d\u90e8\u5206\u4e0e\u6b64\u5b8c\u5168\u76f8\u540c\uff0c\u6240\u4ee5\u672c\u6587\u5c06\u7565\u53bb\u8be5\u90e8\u5206\u3002<\/p>\n<p>\u5728\u8fd9\u4e2a\u73af\u5883\u8bbe\u7f6e\u4e2d\uff0cLaradock\u7684workspace\u4e2d\u7684\u201c\/var\/www\u201d\u5c06\u88ab\u6302\u8f7d\u5230\u4e3b\u673a\u7684\u201c~\/work\u201d\u76ee\u5f55\u4e0a\u3002<\/p>\n<h2>\u521b\u5efa\u6570\u636e\u5e93\uff1a\u73af\u5883\u642d\u5efa\u548c\u540e\u7aef\u5b9e\u65bd\uff08\u7b2c\u4e8c\u90e8\u5206\uff09<\/h2>\n<h3>ER\u56fe<\/h3>\n<p>\u6211\u5728\u8fd0\u884c\u539f\u59cb\u7684ApolloChat\uff0c\u5e76\u67e5\u770b~\/work\/vue-apollo\/tests\/demo\/apollo-server\/schema.graphql\uff0c\u5c1d\u8bd5\u7ed8\u5236ER\u56fe\u8868\u3002<\/p>\n<div><img decoding=\"async\" class=\"post-images\" title=\"\" src=\"https:\/\/cdn.silicloud.com\/blog-img\/blog\/img\/657d82d5913a08637a6b1920\/48-0.png\" alt=\"er.png\" \/><\/div>\n<p>\u5728\u8fd94\u4e2a\u8868\u4e2d\uff0c\u6211\u4eec\u51b3\u5b9a\u5c06\u4ee5\u4e0b3\u4e2a\u8868\u5b58\u50a8\u5728PostgreSQL\u7684apollo_demo\u6a21\u5f0f\u4e2d\uff0c\u4ee5\u4f9b\u767b\u5f55\u7528\u6237\u67e5\u770b\u3002<\/p>\n<ul class=\"post-ul\">\n<li style=\"list-style-type: none;\">\n<ul class=\"post-ul\">channels<\/ul>\n<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<ul class=\"post-ul\">\n<li style=\"list-style-type: none;\">\n<ul class=\"post-ul\">messages<\/ul>\n<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<ul class=\"post-ul\">users<\/ul>\n<p>\u51b3\u5b9a\u5c06\u7528\u6237\u4e0d\u60f3\u5c55\u793a\u7684\u4ee5\u4e0b\u8868\u683c\u5b58\u50a8\u5728PostgreSQL\u7684apollo_demo_private\u6a21\u5f0f\u4e2d\u3002<\/p>\n<ul class=\"post-ul\">user_accounts<\/ul>\n<p>PostgreSQL\u7684\u529f\u80fd\u4e4b\u4e00\uff0c\u6a21\u5f0f\uff08schema\uff09\u7c7b\u4f3c\u4e8e\u547d\u540d\u7a7a\u95f4\uff0c\u53ef\u4ee5\u7528\u4e8e\u5c06\u60f3\u8981\u663e\u793a\u7684\u5bf9\u8c61\u548c\u4e0d\u60f3\u663e\u793a\u7684\u5bf9\u8c61\u5206\u5f00\uff0c\u4ee5\u7528\u4e8e\u5b89\u5168\u76ee\u7684\u3002<\/p>\n<h3>\u5b9e\u65bd\u5de5\u4f5c<\/h3>\n<p>\u4e3a\u907f\u514d\u6df7\u4e71\uff0c\u6211\u4eec\u5c06\u5220\u9664\u539f\u59cb\u7684GraphQL\u670d\u52a1\u5668\uff0c\u5e76\u521b\u5efa\u4e00\u4e2a\u65b0\u7684\u540e\u7aef\u6587\u4ef6\u5b58\u50a8\u4f4d\u7f6e\u6765\u8fdb\u884c\u5f00\u53d1\u3002<\/p>\n<pre class=\"post-pre\"><code>cd ~\/work\/vue-apollo\/tests\/demo\/\r\nrm -rf apollo-server\r\nmkdir postgraphile-server\r\n<\/code><\/pre>\n<p>\u6211\u8fdb\u5165Laradock\u7684\u5de5\u4f5c\u533a\u3002<\/p>\n<pre class=\"post-pre\"><code>cd ~\/work\/laradock\r\ndocker-compose exec --user=laradock workspace bash\r\n<\/code><\/pre>\n<p>\u8fdb\u4e00\u6b65\u8fdb\u5165psql\u3002\u9ed8\u8ba4\u7528\u6237\u5bc6\u7801\u4e3a\uff1asecret\u3002<\/p>\n<pre class=\"post-pre\"><code>psql -h postgres -U default\r\n<\/code><\/pre>\n<p>\u8bf7\u521b\u5efa\u4e00\u4e2a\u53ef\u4ee5\u8fde\u63a5\u7684PostGraphile\u7248ApolloChat\u6570\u636e\u5e93\uff0c\u5e76\u786e\u8ba4\u5176\u53ef\u8fde\u63a5\u6027\u3002<br \/>\n\u4f60\u53ef\u4ee5\u5c06\u6570\u636e\u5e93\u540d\u8bbe\u7f6e\u4e3a&#8221;demodb&#8221;\uff0c\u4f46\u540d\u79f0\u53ef\u4ee5\u4efb\u610f\u53d6\u3002<\/p>\n<pre class=\"post-pre\"><code>create database demodb;\r\n\\connect demodb\r\n<\/code><\/pre>\n<p>\u5982\u679c\u5fd8\u8bb0\u8fde\u63a5\u5230\u300cdemodb\u300d\u6570\u636e\u5e93\uff0c\u6ce8\u610f\u4f1a\u5728\u53e6\u4e00\u4e2a\u6570\u636e\u5e93\u4e2d\u521b\u5efa\u6a21\u5f0f\u3002<\/p>\n<p>\u8bf7\u7ee7\u7eed\u6267\u884c\u4ee5\u4e0bSQL\u8bed\u53e5\uff0c\u521b\u5efa\u6a21\u5f0f\u548c\u6570\u636e\uff08\u7a0d\u540e\u4f1a\u89e3\u91ca\u5185\u5bb9\uff09\u3002<br \/>\n\u8be5SQL\u8bed\u53e5\u5c06\u4fdd\u5b58\u5728\u201c~\/work\/vue-apollo\/tests\/demo\/postgraphile-server\/schema_and_data.sql\u201d\u4e2d\u3002<br \/>\n\u53e6\u5916\uff0cJWT\u7684\u5230\u671f\u65f6\u95f4\u8bbe\u7f6e\u4e3a60\u5206\u949f\u3002\u5982\u679c\u9700\u8981\u66f4\u6539\uff0c\u8bf7\u4fee\u6539\u201c60 minutes\u201d\u8fd9\u4e00\u90e8\u5206\u3002<\/p>\n<pre class=\"post-pre\"><code>begin;\r\n\r\ncreate role apollo_demo_postgraphile login password 'xyz';\r\n\r\ncreate role apollo_demo_anonymous;\r\ngrant apollo_demo_anonymous to apollo_demo_postgraphile;\r\n\r\ncreate role apollo_demo_login_user;\r\ngrant apollo_demo_login_user to apollo_demo_postgraphile;\r\n\r\ncreate schema apollo_demo;\r\ncreate schema apollo_demo_private;\r\n\r\ncreate table apollo_demo.users (\r\n  id               integer primary key generated by default as identity,\r\n  nickname         text not null,\r\n  email            text not null unique check (email ~* '^.+@.+\\..+$')\r\n);\r\n\r\ncomment on table apollo_demo.users is 'A user of the chat.';\r\ncomment on column apollo_demo.users.id is 'The primary unique identifier for the user.';\r\ncomment on column apollo_demo.users.nickname is 'The user\u2019s nickname.';\r\ncomment on column apollo_demo.users.email is 'The email address of the user.';\r\n\r\ncreate table apollo_demo.channels (\r\n  id               text primary key,\r\n  name             text not null\r\n);\r\n\r\ncomment on table apollo_demo.channels is 'A channel of the chat.';\r\ncomment on column apollo_demo.channels.id is 'The primary key for the channel.';\r\ncomment on column apollo_demo.channels.name is 'The channel\u2019s name.';\r\n\r\ncreate table apollo_demo.messages (\r\n  id               integer primary key generated by default as identity,\r\n  channel_id       text not null references apollo_demo.channels(id),\r\n  user_id          integer not null references apollo_demo.users(id),\r\n  content          text not null,\r\n  date_added       timestamp not null default current_timestamp,\r\n  date_updated     timestamp\r\n) ;\r\n\r\ncomment on table apollo_demo.messages is 'A message written by a user.';\r\ncomment on column apollo_demo.messages.id is 'The primary key for the message.'; \r\ncomment on column apollo_demo.messages.channel_id is 'The id of the channel.';\r\ncomment on column apollo_demo.messages.user_id is 'The id of the user.';\r\ncomment on column apollo_demo.messages.content is 'The content this has been posted in.';\r\ncomment on column apollo_demo.messages.date_added is 'The time this message was added.';\r\ncomment on column apollo_demo.messages.date_updated is 'The time this message was updated';\r\n\r\nalter default privileges revoke execute on functions from public;\r\n\r\ncreate function apollo_demo_private.set_date_updated() returns trigger as $$\r\nbegin\r\n  new.date_updated := current_timestamp;\r\n  return new;\r\nend;\r\n$$ language plpgsql;\r\n\r\ncreate trigger messages_date_updated before update\r\n  on apollo_demo.messages\r\n  for each row\r\n  execute procedure apollo_demo_private.set_date_updated();\r\n\r\ncreate table apollo_demo_private.user_accounts (\r\n  user_id          integer primary key references apollo_demo.users(id) on delete cascade,\r\n  password_hash    text not null\r\n) ;\r\n\r\ncomment on table apollo_demo_private.user_accounts is 'Private information about a user\u2019s account.';\r\ncomment on column apollo_demo_private.user_accounts.user_id is 'The id of the User associated with this account.';\r\ncomment on column apollo_demo_private.user_accounts.password_hash is 'An opaque hash of the user password.';\r\n\r\ncreate extension if not exists \"pgcrypto\";\r\n\r\ncreate function apollo_demo.user_register(\r\n  nickname text,\r\n  email text,\r\n  password text\r\n) returns apollo_demo.users as $$\r\ndeclare\r\n  usr apollo_demo.users;\r\nbegin\r\n  insert into apollo_demo.users (nickname, email) values\r\n    (user_register.nickname, user_register.email)\r\n    returning * into usr;\r\n\r\n  insert into apollo_demo_private.user_accounts (user_id, password_hash) values\r\n    (usr.id, crypt(user_register.password, gen_salt('bf')));\r\n\r\n  return usr;\r\nend;\r\n$$ language plpgsql strict security definer;\r\n\r\ncomment on function apollo_demo.user_register(text, text, text) is 'Registers a single user and creates an account in our chat.';\r\n\r\ncreate type apollo_demo.jwt_token as (\r\n  role text,\r\n  user_id integer,\r\n  exp integer\r\n);\r\n\r\ncreate type apollo_demo.usr_and_token as (\r\n  usr apollo_demo.users,\r\n  token apollo_demo.jwt_token\r\n);\r\n\r\ncreate function apollo_demo.user_login(\r\n  email text,\r\n  password text\r\n) returns apollo_demo.usr_and_token as $$\r\n  select ((users.*)::apollo_demo.users, ('apollo_demo_login_user', users.id, extract(epoch from (now() + interval '60 minutes')))::apollo_demo.jwt_token)::apollo_demo.usr_and_token\r\n    from apollo_demo.users\r\n      inner join apollo_demo_private.user_accounts\r\n      on users.id = user_accounts.user_id\r\n    where \r\n      users.email = user_login.email\r\n      and user_accounts.password_hash = crypt(user_login.password, user_accounts.password_hash);\r\n$$ language sql strict security definer;\r\n\r\ncomment on function apollo_demo.user_login(text, text) is 'Creates a JWT token that will securely identify a user and give them certain permissions.';\r\n\r\ncreate function apollo_demo.user_current() returns apollo_demo.users as $$\r\n  select *\r\n  from apollo_demo.users\r\n  where id = current_setting('jwt.claims.user_id', true)::integer\r\n$$ language sql stable;\r\n\r\ncomment on function apollo_demo.user_current() is 'Gets the user who was identified by our JWT.';\r\n\r\ncreate function apollo_demo.change_password(\r\n  current_password text,\r\n  new_password text\r\n) \r\nreturns boolean as $$\r\ndeclare\r\n  usr_current apollo_demo.users;\r\nbegin\r\n  usr_current := apollo_demo.user_current();\r\n  if exists (select 1 from apollo_demo_private.user_accounts where user_accounts.user_id = usr_current.id and user_accounts.password_hash = crypt(change_password.current_password, usr_current.password_hash)) \r\n  then\r\n    update apollo_demo_private.user_accounts set password_hash = crypt(change_password.new_password, gen_salt('bf'))\r\n      where user_accounts.user_id = usr_current.id; \r\n    return true;\r\n  else \r\n    return false;\r\n  end if;\r\nend;\r\n$$ language plpgsql strict security definer;\r\n\r\ncreate function apollo_demo.user_logout(\r\n) returns boolean as $$\r\nbegin\r\n  return true;\r\nend;\r\n$$ language plpgsql strict security definer;\r\n\r\ncreate function apollo_demo.message_add(\r\n  channel_id text,\r\n  content text\r\n) returns apollo_demo.messages as $$\r\ndeclare\r\n  message apollo_demo.messages;\r\nbegin\r\n  INSERT INTO apollo_demo.messages (channel_id, user_id, content) VALUES\r\n    (message_add.channel_id, current_setting('jwt.claims.user_id', true)::integer, message_add.content)\r\n    RETURNING messages.* into message;\r\n  return message;\r\nend;\r\n$$ language plpgsql strict security definer;\r\n\r\nCREATE SEQUENCE mock_message_seq\r\n    INCREMENT BY 1\r\n    START WITH 1\r\n;\r\n\r\ncreate function apollo_demo.mock_message_send(\r\n) returns boolean as $$\r\nbegin\r\n  INSERT INTO apollo_demo.messages (channel_id, user_id, content)\r\n    SELECT 'general', 0, 'How are you doing? ' || nextval('mock_message_seq');\r\n  return true;\r\nend;\r\n$$ language plpgsql strict security definer;\r\n\r\ngrant usage on schema apollo_demo to apollo_demo_anonymous, apollo_demo_login_user;\r\n\r\ngrant select on table apollo_demo.users to apollo_demo_login_user;\r\ngrant update, delete on table apollo_demo.users to apollo_demo_login_user;\r\n\r\ngrant select on table apollo_demo.channels to apollo_demo_login_user;\r\ngrant insert, update, delete on table apollo_demo.channels to apollo_demo_login_user;\r\n\r\ngrant select on table apollo_demo.messages to apollo_demo_login_user;\r\ngrant insert, update, delete on table apollo_demo.messages to apollo_demo_login_user;\r\n\r\ngrant execute on function apollo_demo.user_register(text, text, text) to apollo_demo_anonymous;\r\ngrant execute on function apollo_demo.user_login(text, text) to apollo_demo_anonymous;\r\ngrant execute on function apollo_demo.user_current() to apollo_demo_login_user;\r\ngrant execute on function apollo_demo.change_password(text, text) to apollo_demo_login_user;\r\ngrant execute on function apollo_demo.user_logout() to apollo_demo_anonymous, apollo_demo_login_user;\r\ngrant execute on function apollo_demo.message_add(text, text) to apollo_demo_login_user;\r\ngrant execute on function apollo_demo.mock_message_send() to apollo_demo_login_user;\r\n\r\nalter table apollo_demo.users enable row level security;\r\nalter table apollo_demo.messages enable row level security;\r\n\r\ncreate policy select_users on apollo_demo.users for select\r\n  using (true);\r\n\r\ncreate policy select_messages on apollo_demo.messages for select\r\n  using (true);\r\n\r\ncreate policy update_users on apollo_demo.users for update to apollo_demo_login_user\r\n  using (id = current_setting('jwt.claims.user_id', true)::integer);\r\n\r\ncreate policy delete_users on apollo_demo.users for delete to apollo_demo_login_user\r\n  using (id = current_setting('jwt.claims.user_id', true)::integer);\r\n\r\ncreate policy insert_messages on apollo_demo.messages for insert to apollo_demo_login_user\r\n  with check (user_id = current_setting('jwt.claims.user_id', true)::integer);\r\n\r\ncreate policy update_messages on apollo_demo.messages for update to apollo_demo_login_user\r\n  using (user_id = current_setting('jwt.claims.user_id', true)::integer);\r\n\r\ncreate policy delete_messages on apollo_demo.messages for delete to apollo_demo_login_user\r\n  using (user_id = current_setting('jwt.claims.user_id', true)::integer);\r\n\r\n\r\nINSERT INTO apollo_demo.users (id, nickname, email) VALUES\r\n  (0, 'The Bot', 'bot@bot.com');\r\n\r\nINSERT INTO apollo_demo_private.user_accounts (user_id, password_hash) VALUES\r\n  (0, crypt('bot', gen_salt('bf')));\r\n\r\nINSERT INTO apollo_demo.channels (id, name) VALUES\r\n  ('general', 'General discussion'),\r\n  ('random', 'Have fun chatting!'),\r\n  ('help', 'Ask for or give help');\r\n\r\nINSERT INTO apollo_demo.messages (channel_id, user_id, content) VALUES\r\n  ('general', 0, 'Welcome to the chat!');\r\n\r\n\r\ncreate type apollo_demo.message_nullable as (\r\n  id               integer,\r\n  channel_id       text,\r\n  user_id          integer,\r\n  content          text,\r\n  date_added       timestamp,\r\n  date_updated     timestamp\r\n) ;\r\n\r\ncreate or replace function apollo_demo.graphql_subscription() returns trigger as $$\r\ndeclare\r\n  v_process_new bool = (TG_OP = 'INSERT' OR TG_OP = 'UPDATE');\r\n  v_process_old bool = (TG_OP = 'UPDATE' OR TG_OP = 'DELETE');\r\n  v_event text = TG_ARGV[0];\r\n  v_topic_template text = TG_ARGV[1];\r\n  v_attribute text = TG_ARGV[2];\r\n  v_record record;\r\n  v_sub text;\r\n  v_topic text;\r\nbegin\r\n  if v_process_new then\r\n    v_record = new;\r\n  else\r\n    v_record = old;\r\n  end if;\r\n\r\n  if v_attribute is not null then\r\n    execute 'select $1.' || quote_ident(v_attribute)\r\n    using v_record\r\n    into v_sub;\r\n  end if;\r\n\r\n  if v_sub is not null then\r\n    v_topic = replace(v_topic_template, '$1', v_sub);\r\n  else\r\n    v_topic = v_topic_template;\r\n  end if;\r\n\r\n  perform pg_notify(v_topic, json_build_object(\r\n      'event', v_event,\r\n      'newrec', new.*,\r\n      'oldrec', old.*,\r\n      'type', TG_OP\r\n    )::text);\r\n\r\n  -- RAISE LOG     'v_topic = %', v_topic;\r\n  -- RAISE LOG     'json_build_object = %', json_build_object('event', v_event, 'newrec', new.*, 'oldrec', old.*, 'type', TG_OP);\r\n\r\n  return v_record;\r\nend;\r\n$$ language plpgsql volatile set search_path from current;\r\n\r\nDROP TRIGGER IF EXISTS _500_gql_update_message ON apollo_demo.messages;\r\nCREATE TRIGGER _500_gql_update_message\r\n  AFTER INSERT OR UPDATE OR DELETE ON apollo_demo.messages\r\n  FOR EACH ROW\r\n  EXECUTE PROCEDURE apollo_demo.graphql_subscription(\r\n    'messageChanged', -- the \"event\" string, useful for the client to know what happened\r\n    'graphql:message:$1', -- the \"topic\" the event will be published to, as a template\r\n    'channel_id' -- If specified, `$1` above will be replaced with NEW.channel_id or OLD.channel_id from the trigger.\r\n  );\r\n\r\ncommit;\r\n<\/code><\/pre>\n<p>\u5982\u679c\u5904\u7406\u6210\u529f\uff0c\u6211\u4f1a\u9000\u51fapsql\u548cLaradock\u7684workspace\uff0c\u7136\u540e\u56de\u5230\u4e3b\u673a\u4e0a\u3002<\/p>\n<h2>\u73af\u5883\u642d\u5efa\u548c\u540e\u7aef\u5b9e\u73b0\uff08\u7b2c\u4e09\u90e8\u5206\uff09\uff1a\u4e0ePostGraphile\u76f8\u5173\u3002<\/h2>\n<p>\u6211\u5011\u6c7a\u5b9a\u4f7f\u7528PostGraphile\u5b98\u65b9Docker\u6620\u50cf\u3002\u7136\u800c\uff0c\u70ba\u4e86\u904b\u884cGraphQL\u8a02\u95b1\uff0c\u9700\u8981\u5728\u6620\u50cf\u4e2d\u5b89\u88ddJavaScript\u5305\u3002\u56e0\u6b64\uff0c\u6211\u5011\u5c07\u4ee5\u5b98\u65b9\u6620\u50cf\u70ba\u57fa\u790e\u6620\u50cf\u5275\u5efa\u65b0\u7684Docker\u6620\u50cf\u3002\u5b98\u65b9\u6620\u50cf\u7684Dockerfile\u5728\u9019\u88e1\u3002<\/p>\n<p>\u521b\u5efa\u4e00\u4e2a\u5b58\u653e\u65b0\u7684Dockerfile\u7684\u4f4d\u7f6e\u3002<\/p>\n<pre class=\"post-pre\"><code>mkdir -p ~\/work\/laradock\/postgraphile_plus\r\n<\/code><\/pre>\n<p>\u521b\u5efa\u4e00\u4e2a\u65b0\u7684Dockerfile\u3002<\/p>\n<pre class=\"post-pre\"><code>FROM graphile\/postgraphile\r\n\r\nENV NODE_PATH=\/usr\/local\/lib\/node_modules\r\n\r\n# RUN yarn global add @graphile\/pg-pubsub graphile-build graphql postgraphile graphile-utils log4js\r\nRUN npm install -g @graphile\/pg-pubsub graphile-build graphql postgraphile graphile-utils log4js\r\n\r\nENTRYPOINT [\".\/cli.js\"]\r\n<\/code><\/pre>\n<p>\u5728\u9a8c\u8bc1\u4e2d\u4f7f\u7528JWT\u3002<br \/>\n\u6267\u884c\u4ee5\u4e0b\u64cd\u4f5c\u6765\u751f\u6210JWT\u7684\u5171\u4eab\u5bc6\u94a5\uff08\u79c1\u94a5\uff09\uff08Linux\u547d\u4ee4\uff09\u3002\u8be5\u5171\u4eab\u5bc6\u94a5\u5c06\u5728\u4ee4\u724c\u521b\u5efa\u548c\u9a8c\u8bc1\u8fc7\u7a0b\u4e2d\u901a\u7528\u4f7f\u7528\u3002<\/p>\n<pre class=\"post-pre\"><code>LC_CTYPE=C &lt; \/dev\/urandom tr -dc A-Za-z0-9 | head -c32\r\n<\/code><\/pre>\n<p>\u5728\u672c\u9875\u9762\u4e2d\uff0c\u6211\u4eec\u4f7f\u7528\u4ee5\u4e0b\u5171\u540c\u5bc6\u94a5\u3002\u7531\u4e8e\u5b83\u662f\u4e2a\u79d8\u5bc6\u94a5\u5319\uff0c\u6240\u4ee5\u5728\u6b63\u5f0f\u73af\u5883\u4e2d\u9700\u8981\u7279\u522b\u5c0f\u5fc3\u5904\u7406\u3002<\/p>\n<pre class=\"post-pre\"><code>K3INqoxbNWCB4hYiDnX2zlbIpm8UA5zR\r\n<\/code><\/pre>\n<p>\u5728Laradock\u7684docker-compose.yml\u6587\u4ef6\u7684\u672b\u5c3e\u6dfb\u52a0\u4ee5\u4e0b\u5185\u5bb9\u3002\u8fd8\u914d\u7f6e\u4e86JWT\u4f7f\u7528\u7684\u5171\u4eab\u5bc6\u94a5\u3002<\/p>\n<pre class=\"post-pre\"><code><span class=\"c1\">### PostGraphile ################################################<\/span>\r\n    <span class=\"na\">postgraphile_demo<\/span><span class=\"pi\">:<\/span>\r\n      <span class=\"na\">build<\/span><span class=\"pi\">:<\/span>\r\n        <span class=\"na\">context<\/span><span class=\"pi\">:<\/span> <span class=\"s\">.\/postgraphile_plus<\/span>\r\n      <span class=\"na\">environment<\/span><span class=\"pi\">:<\/span>\r\n        <span class=\"na\">DATABASE_URL<\/span><span class=\"pi\">:<\/span> <span class=\"s2\">\"<\/span><span class=\"s\">postgres:\/\/apollo_demo_postgraphile:xyz@postgres:${POSTGRES_PORT}\/demodb\"<\/span>\r\n      <span class=\"na\">expose<\/span><span class=\"pi\">:<\/span>\r\n        <span class=\"pi\">-<\/span> <span class=\"s2\">\"<\/span><span class=\"s\">16000\"<\/span>\r\n      <span class=\"na\">command<\/span><span class=\"pi\">:<\/span> <span class=\"pi\">[<\/span><span class=\"s2\">\"<\/span><span class=\"s\">--plugins\"<\/span><span class=\"pi\">,<\/span> <span class=\"s2\">\"<\/span><span class=\"s\">@graphile\/pg-pubsub\"<\/span><span class=\"pi\">,<\/span> <span class=\"s2\">\"<\/span><span class=\"s\">--append-plugins\"<\/span><span class=\"pi\">,<\/span> <span class=\"s2\">\"<\/span><span class=\"s\">\/root\/www\/vue-apollo\/tests\/demo\/postgraphile-server\/DemoSubscriptionPlugin.js\"<\/span><span class=\"pi\">,<\/span> <span class=\"s2\">\"<\/span><span class=\"s\">--subscriptions\"<\/span><span class=\"pi\">,<\/span> <span class=\"s2\">\"<\/span><span class=\"s\">--connection\"<\/span><span class=\"pi\">,<\/span> <span class=\"s2\">\"<\/span><span class=\"s\">postgres:\/\/apollo_demo_postgraphile:xyz@postgres:${POSTGRES_PORT}\/demodb\"<\/span><span class=\"pi\">,<\/span> <span class=\"s2\">\"<\/span><span class=\"s\">--port\"<\/span><span class=\"pi\">,<\/span> <span class=\"s2\">\"<\/span><span class=\"s\">16000\"<\/span><span class=\"pi\">,<\/span> <span class=\"s2\">\"<\/span><span class=\"s\">--schema\"<\/span><span class=\"pi\">,<\/span> <span class=\"s2\">\"<\/span><span class=\"s\">apollo_demo\"<\/span><span class=\"pi\">,<\/span> <span class=\"s2\">\"<\/span><span class=\"s\">--default-role\"<\/span><span class=\"pi\">,<\/span> <span class=\"s2\">\"<\/span><span class=\"s\">apollo_demo_anonymous\"<\/span><span class=\"pi\">,<\/span> <span class=\"s2\">\"<\/span><span class=\"s\">--secret\"<\/span><span class=\"pi\">,<\/span> <span class=\"s2\">\"<\/span><span class=\"s\">K3INqoxbNWCB4hYiDnX2zlbIpm8UA5zR\"<\/span><span class=\"pi\">,<\/span> <span class=\"s2\">\"<\/span><span class=\"s\">--token\"<\/span><span class=\"pi\">,<\/span> <span class=\"s2\">\"<\/span><span class=\"s\">apollo_demo.jwt_token\"<\/span><span class=\"pi\">,<\/span> <span class=\"s2\">\"<\/span><span class=\"s\">--export-schema-graphql\"<\/span><span class=\"pi\">,<\/span> <span class=\"s2\">\"<\/span><span class=\"s\">\/root\/www\/vue-apollo\/tests\/demo\/postgraphile-server\/schema.graphql\"<\/span><span class=\"pi\">]<\/span>\r\n      <span class=\"na\">ports<\/span><span class=\"pi\">:<\/span>\r\n        <span class=\"pi\">-<\/span> <span class=\"s2\">\"<\/span><span class=\"s\">16000:16000\"<\/span>\r\n      <span class=\"na\">depends_on<\/span><span class=\"pi\">:<\/span>\r\n        <span class=\"pi\">-<\/span> <span class=\"s\">postgres<\/span>\r\n      <span class=\"na\">restart<\/span><span class=\"pi\">:<\/span> <span class=\"s\">on-failure:20<\/span>\r\n      <span class=\"na\">networks<\/span><span class=\"pi\">:<\/span>\r\n        <span class=\"pi\">-<\/span> <span class=\"s\">backend<\/span>\r\n      <span class=\"na\">volumes<\/span><span class=\"pi\">:<\/span>\r\n        <span class=\"pi\">-<\/span> <span class=\"s\">${APP_CODE_PATH_HOST}:\/root\/www<\/span>\r\n\r\n<\/code><\/pre>\n<p>\u7531\u4e8e\u4e0a\u8ff0\u7684docker-compose.yml\u6587\u4ef6\u4e2d\uff0c\u5df2\u7ecf\u914d\u7f6e\u4e86\u4f7f\u7528&#8221;DemoSubscriptionPlugin.js&#8221;\u4f5c\u4e3aPostGraphile\u7684\u63d2\u4ef6\uff0c\u56e0\u6b64\u4e5f\u9700\u8981\u521b\u5efa\u8be5\u6587\u4ef6\u3002\u63d2\u4ef6\u7684\u8be6\u7ec6\u5185\u5bb9\u7a0d\u540e\u5c06\u8fdb\u884c\u4ecb\u7ecd\u3002<\/p>\n<p>\u8bf7\u6309\u7167\u4ee5\u4e0b\u5185\u5bb9\u7528DemoSubscriptionPlugin.js\u8fdb\u884c\u65b0\u5efa\uff1a<br \/>\n\u6587\u4ef6\u7684\u8def\u5f84\u4e3a\u300c~\/work\/vue-apollo\/tests\/demo\/postgraphile-server\u300d\u3002<\/p>\n<pre class=\"post-pre\"><code><span class=\"kd\">const<\/span> <span class=\"nx\">DEBUG_MODE<\/span> <span class=\"o\">=<\/span> <span class=\"kc\">false<\/span>\r\n\r\n<span class=\"c1\">\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/<\/span>\r\n<span class=\"c1\">\/\/ \u30c7\u30d0\u30c3\u30b0\u7528\u30b3\u30fc\u30c9\u3002\u30c7\u30d0\u30c3\u30b0\u6642\u306b \/root\/www\/debug.log \u306b\u30ed\u30b0\u3092\u51fa\u529b\u3059\u308b\u3002<\/span>\r\n<span class=\"c1\">\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/<\/span>\r\n<span class=\"kd\">const<\/span> <span class=\"nx\">log4js<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">require<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">log4js<\/span><span class=\"dl\">'<\/span><span class=\"p\">)<\/span>\r\n<span class=\"kd\">const<\/span> <span class=\"nx\">util<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">require<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">util<\/span><span class=\"dl\">'<\/span><span class=\"p\">)<\/span>\r\n\r\n<span class=\"nx\">log4js<\/span><span class=\"p\">.<\/span><span class=\"nx\">configure<\/span><span class=\"p\">({<\/span>\r\n  <span class=\"na\">appenders<\/span> <span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n    <span class=\"na\">debug<\/span> <span class=\"p\">:<\/span> <span class=\"p\">{<\/span><span class=\"na\">type<\/span> <span class=\"p\">:<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">file<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span> <span class=\"na\">filename<\/span> <span class=\"p\">:<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">\/root\/www\/debug.log<\/span><span class=\"dl\">'<\/span><span class=\"p\">}<\/span>\r\n  <span class=\"p\">},<\/span>\r\n  <span class=\"na\">categories<\/span> <span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n    <span class=\"na\">default<\/span> <span class=\"p\">:<\/span> <span class=\"p\">{<\/span><span class=\"na\">appenders<\/span> <span class=\"p\">:<\/span> <span class=\"p\">[<\/span><span class=\"dl\">'<\/span><span class=\"s1\">debug<\/span><span class=\"dl\">'<\/span><span class=\"p\">],<\/span> <span class=\"na\">level<\/span> <span class=\"p\">:<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">debug<\/span><span class=\"dl\">'<\/span><span class=\"p\">},<\/span>\r\n  <span class=\"p\">}<\/span>\r\n<span class=\"p\">});<\/span>\r\n<span class=\"kd\">var<\/span> <span class=\"nx\">logger<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">log4js<\/span><span class=\"p\">.<\/span><span class=\"nx\">getLogger<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">debug<\/span><span class=\"dl\">'<\/span><span class=\"p\">);<\/span>\r\n\r\n<span class=\"c1\">\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/<\/span>\r\n<span class=\"c1\">\/\/ \u4ee5\u4e0b\u3001https:\/\/www.graphile.org\/postgraphile\/subscriptions\/ \u306e<\/span>\r\n<span class=\"c1\">\/\/ MySubscriptionPlugin.js\u3092\u6539\u5909<\/span>\r\n<span class=\"c1\">\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/<\/span>\r\n<span class=\"kd\">const<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">makeExtendSchemaPlugin<\/span><span class=\"p\">,<\/span> <span class=\"nx\">gql<\/span><span class=\"p\">,<\/span> <span class=\"nx\">embed<\/span> <span class=\"p\">}<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">require<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">graphile-utils<\/span><span class=\"dl\">\"<\/span><span class=\"p\">);<\/span>\r\n\r\n<span class=\"kd\">const<\/span> <span class=\"nx\">makeTopic<\/span> <span class=\"o\">=<\/span> <span class=\"p\">(<\/span><span class=\"nx\">_args<\/span><span class=\"p\">,<\/span> <span class=\"nx\">context<\/span><span class=\"p\">,<\/span> <span class=\"nx\">_resolveInfo<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\r\n  <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"nx\">DEBUG_MODE<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\r\n    <span class=\"nx\">logger<\/span><span class=\"p\">.<\/span><span class=\"nx\">debug<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">makeTopic() in<\/span><span class=\"dl\">'<\/span><span class=\"p\">);<\/span>\r\n    <span class=\"nx\">logger<\/span><span class=\"p\">.<\/span><span class=\"nx\">debug<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">_args = <\/span><span class=\"dl\">'<\/span><span class=\"p\">);<\/span>\r\n    <span class=\"nx\">logger<\/span><span class=\"p\">.<\/span><span class=\"nx\">debug<\/span><span class=\"p\">(<\/span><span class=\"nx\">util<\/span><span class=\"p\">.<\/span><span class=\"nx\">inspect<\/span><span class=\"p\">(<\/span><span class=\"nx\">_args<\/span><span class=\"p\">));<\/span>\r\n    <span class=\"nx\">logger<\/span><span class=\"p\">.<\/span><span class=\"nx\">debug<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">context = <\/span><span class=\"dl\">'<\/span><span class=\"p\">);<\/span>\r\n    <span class=\"nx\">logger<\/span><span class=\"p\">.<\/span><span class=\"nx\">debug<\/span><span class=\"p\">(<\/span><span class=\"nx\">util<\/span><span class=\"p\">.<\/span><span class=\"nx\">inspect<\/span><span class=\"p\">(<\/span><span class=\"nx\">context<\/span><span class=\"p\">));<\/span>\r\n\r\n    <span class=\"c1\">\/\/ \u30c7\u30d0\u30c3\u30b0\u6642\u306b\u3001JWT\u304c\u6e21\u3055\u308c\u306a\u304f\u3066\u3082Subscription\u3092\u6709\u52b9\u306b\u3057\u305f\u3044\u306a\u3089\u3001\u3053\u3053\u3067return<\/span>\r\n    <span class=\"k\">return<\/span> <span class=\"s2\">`graphql:message:<\/span><span class=\"p\">${<\/span><span class=\"nx\">_args<\/span><span class=\"p\">.<\/span><span class=\"nx\">channelId<\/span><span class=\"p\">}<\/span><span class=\"s2\">`<\/span><span class=\"p\">;<\/span>\r\n  <span class=\"p\">}<\/span>\r\n\r\n  <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"nx\">context<\/span><span class=\"p\">.<\/span><span class=\"nx\">jwtClaims<\/span><span class=\"p\">.<\/span><span class=\"nx\">user_id<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\r\n    <span class=\"k\">return<\/span> <span class=\"s2\">`graphql:message:<\/span><span class=\"p\">${<\/span><span class=\"nx\">_args<\/span><span class=\"p\">.<\/span><span class=\"nx\">channelId<\/span><span class=\"p\">}<\/span><span class=\"s2\">`<\/span><span class=\"p\">;<\/span>\r\n  <span class=\"p\">}<\/span> <span class=\"k\">else<\/span> <span class=\"p\">{<\/span>\r\n    <span class=\"k\">throw<\/span> <span class=\"k\">new<\/span> <span class=\"nb\">Error<\/span><span class=\"p\">(<\/span><span class=\"dl\">\"<\/span><span class=\"s2\">You're not logged in<\/span><span class=\"dl\">\"<\/span><span class=\"p\">);<\/span>\r\n  <span class=\"p\">}<\/span>\r\n<span class=\"p\">};<\/span>\r\n\r\n<span class=\"nx\">module<\/span><span class=\"p\">.<\/span><span class=\"nx\">exports<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">makeExtendSchemaPlugin<\/span><span class=\"p\">(({<\/span> <span class=\"na\">pgSql<\/span><span class=\"p\">:<\/span> <span class=\"nx\">sql<\/span> <span class=\"p\">})<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">({<\/span>\r\n  <span class=\"na\">typeDefs<\/span><span class=\"p\">:<\/span> <span class=\"nx\">gql<\/span><span class=\"s2\">`\r\n    type MessageChanged {\r\n      # This is returned directly from the PostgreSQL subscription payload (JSON object)\r\n      type: String!\r\n\r\n      # This is populated by our resolver below\r\n      message: Message\r\n\r\n      # This is returned directly from the PostgreSQL subscription payload (JSON object)\r\n      oldrec: MessageNullable\r\n    }\r\n\r\n    extend type Subscription {\r\n      messageChanged (channelId: String!): MessageChanged! @pgSubscription(topic: <\/span><span class=\"p\">${<\/span><span class=\"nx\">embed<\/span><span class=\"p\">(<\/span>\r\n        <span class=\"nx\">makeTopic<\/span>\r\n      <span class=\"p\">)}<\/span><span class=\"s2\">)\r\n    }\r\n  `<\/span><span class=\"p\">,<\/span>\r\n\r\n  <span class=\"na\">resolvers<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n    <span class=\"na\">MessageChanged<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n      <span class=\"c1\">\/\/ This method finds the user from the database based on the event<\/span>\r\n      <span class=\"c1\">\/\/ published by PostgreSQL.<\/span>\r\n      <span class=\"c1\">\/\/<\/span>\r\n      <span class=\"c1\">\/\/ In a future release, we hope to enable you to replace this entire<\/span>\r\n      <span class=\"c1\">\/\/ method with a small schema directive above, should you so desire. It's<\/span>\r\n      <span class=\"c1\">\/\/ mostly boilerplate.<\/span>\r\n\r\n      <span class=\"k\">async<\/span> <span class=\"nx\">message<\/span><span class=\"p\">(<\/span>\r\n        <span class=\"nx\">event<\/span><span class=\"p\">,<\/span>\r\n        <span class=\"nx\">_args<\/span><span class=\"p\">,<\/span>\r\n        <span class=\"nx\">_context<\/span><span class=\"p\">,<\/span>\r\n        <span class=\"p\">{<\/span>\r\n          <span class=\"na\">graphile<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">selectGraphQLResultFromTable<\/span> <span class=\"p\">},<\/span>\r\n        <span class=\"p\">}<\/span>\r\n      <span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\r\n\r\n        <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"nx\">DEBUG_MODE<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\r\n          <span class=\"nx\">logger<\/span><span class=\"p\">.<\/span><span class=\"nx\">debug<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">message() in<\/span><span class=\"dl\">'<\/span><span class=\"p\">);<\/span>\r\n          <span class=\"nx\">logger<\/span><span class=\"p\">.<\/span><span class=\"nx\">debug<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">event = <\/span><span class=\"dl\">'<\/span><span class=\"p\">);<\/span>\r\n          <span class=\"nx\">logger<\/span><span class=\"p\">.<\/span><span class=\"nx\">debug<\/span><span class=\"p\">(<\/span><span class=\"nx\">util<\/span><span class=\"p\">.<\/span><span class=\"nx\">inspect<\/span><span class=\"p\">(<\/span><span class=\"nx\">event<\/span><span class=\"p\">));<\/span>\r\n        <span class=\"p\">}<\/span>\r\n\r\n        <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"nx\">event<\/span><span class=\"p\">.<\/span><span class=\"nx\">type<\/span> <span class=\"o\">===<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">DELETE<\/span><span class=\"dl\">'<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\r\n          <span class=\"k\">return<\/span> <span class=\"kc\">null<\/span><span class=\"p\">;<\/span>\r\n        <span class=\"p\">}<\/span>\r\n\r\n        <span class=\"kd\">const<\/span> <span class=\"nx\">rows<\/span> <span class=\"o\">=<\/span> <span class=\"k\">await<\/span> <span class=\"nx\">selectGraphQLResultFromTable<\/span><span class=\"p\">(<\/span>\r\n          <span class=\"nx\">sql<\/span><span class=\"p\">.<\/span><span class=\"nx\">fragment<\/span><span class=\"s2\">`apollo_demo.messages`<\/span><span class=\"p\">,<\/span>\r\n          <span class=\"p\">(<\/span><span class=\"nx\">tableAlias<\/span><span class=\"p\">,<\/span> <span class=\"nx\">sqlBuilder<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\r\n\r\n            <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"nx\">DEBUG_MODE<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\r\n              <span class=\"nx\">logger<\/span><span class=\"p\">.<\/span><span class=\"nx\">debug<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">selectGraphQLResultFromTable() in<\/span><span class=\"dl\">'<\/span><span class=\"p\">);<\/span>\r\n            <span class=\"p\">}<\/span>\r\n\r\n            <span class=\"nx\">sqlBuilder<\/span><span class=\"p\">.<\/span><span class=\"nx\">where<\/span><span class=\"p\">(<\/span>\r\n              <span class=\"nx\">sql<\/span><span class=\"p\">.<\/span><span class=\"nx\">fragment<\/span><span class=\"s2\">`<\/span><span class=\"p\">${<\/span><span class=\"nx\">sqlBuilder<\/span><span class=\"p\">.<\/span><span class=\"nx\">getTableAlias<\/span><span class=\"p\">()}<\/span><span class=\"s2\">.id = <\/span><span class=\"p\">${<\/span><span class=\"nx\">sql<\/span><span class=\"p\">.<\/span><span class=\"nx\">value<\/span><span class=\"p\">(<\/span>\r\n                <span class=\"nx\">event<\/span><span class=\"p\">.<\/span><span class=\"nx\">newrec<\/span><span class=\"p\">.<\/span><span class=\"nx\">id<\/span>\r\n              <span class=\"p\">)}<\/span><span class=\"s2\">`<\/span>\r\n            <span class=\"p\">);<\/span>\r\n          <span class=\"p\">}<\/span>\r\n        <span class=\"p\">);<\/span>\r\n\r\n        <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"nx\">DEBUG_MODE<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\r\n          <span class=\"nx\">logger<\/span><span class=\"p\">.<\/span><span class=\"nx\">debug<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">rows = <\/span><span class=\"dl\">'<\/span><span class=\"p\">);<\/span>\r\n          <span class=\"nx\">logger<\/span><span class=\"p\">.<\/span><span class=\"nx\">debug<\/span><span class=\"p\">(<\/span><span class=\"nx\">util<\/span><span class=\"p\">.<\/span><span class=\"nx\">inspect<\/span><span class=\"p\">(<\/span><span class=\"nx\">rows<\/span><span class=\"p\">));<\/span>\r\n        <span class=\"p\">}<\/span>\r\n        <span class=\"k\">return<\/span> <span class=\"nx\">rows<\/span><span class=\"p\">[<\/span><span class=\"mi\">0<\/span><span class=\"p\">];<\/span>\r\n      <span class=\"p\">},<\/span>\r\n\r\n    <span class=\"p\">},<\/span>\r\n  <span class=\"p\">},<\/span>\r\n<span class=\"p\">}));<\/span>\r\n<\/code><\/pre>\n<p>\u5efa\u7acbDocker\u73af\u5883<\/p>\n<pre class=\"post-pre\"><code>cd ~\/work\/laradock\r\ndocker-compose up -d postgres pgadmin workspace postgraphile_demo\r\n<\/code><\/pre>\n<p>\u8bf7\u7a0d\u7b49\u7247\u523b\u3002\u4e00\u65e6\u5b8c\u6210\uff0c<\/p>\n<pre class=\"post-pre\"><code>docker-compose ps\r\n<\/code><\/pre>\n<p>\u6211\u5011\u770b\u5230\u4ee5\u4e0b\u7684\u986f\u793a\uff0c\u8acb\u78ba\u8a8d\u72c0\u614b\u5df2\u7d93\u8b8a\u6210&#8221;Up&#8221;\u3002<\/p>\n<pre class=\"post-pre\"><code>            Name                          Command              State                Ports            \r\n-----------------------------------------------------------------------------------------------------\r\nlaradock_docker-in-docker_1    dockerd-entrypoint.sh           Up       2375\/tcp                     \r\nlaradock_pgadmin_1             docker-entrypoint.sh pgadmin4   Up       0.0.0.0:5050-&gt;5050\/tcp       \r\nlaradock_postgraphile_demo_1   .\/cli.js --plugins @graphi      Up       0.0.0.0:16000-&gt;16000\/tcp,    \r\n                               ...                                      5000\/tcp                     \r\nlaradock_postgres_1            docker-entrypoint.sh postgres   Up       0.0.0.0:5432-&gt;5432\/tcp       \r\nlaradock_workspace_1           sudo bash -c [ -e \/var\/run      Up       0.0.0.0:2222-&gt;22\/tcp,        \r\n                               ...                                      0.0.0.0:3389-&gt;3389\/tcp,      \r\n                                                                        0.0.0.0:14000-&gt;4000\/tcp,     \r\n                                                                        0.0.0.0:18080-&gt;8080\/tcp      \r\n<\/code><\/pre>\n<p>\u786e\u8ba4PostGraphile\u80fd\u591f\u8fde\u63a5\u5230PostgreSQL\u3002<\/p>\n<pre class=\"post-pre\"><code>docker logs laradock_postgraphile_demo_1\r\n<\/code><\/pre>\n<p>\u53ea\u8981\u6ca1\u6709\u663e\u793a\u9519\u8bef\u4fe1\u606f\uff0cPostGraphile\u5c31\u5df2\u7ecf\u6210\u529f\u542f\u52a8\u4e86\u3002<\/p>\n<pre class=\"post-pre\"><code>PostGraphile v4.4.1-alpha.4 server listening on port 16000 ?\r\n\r\n  \u2023 GraphQL API:         http:\/\/0.0.0.0:16000\/graphql (subscriptions enabled)\r\n  \u2023 GraphiQL GUI\/IDE:    http:\/\/0.0.0.0:16000\/graphiql\r\n  \u2023 Postgres connection: postgres:\/\/apollo_demo_postgraphile:[SECRET]@postgres\/demodb\r\n  \u2023 Postgres schema(s):  apollo_demo\r\n  \u2023 Documentation:       https:\/\/graphile.org\/postgraphile\/introduction\/\r\n\r\n* * *\r\n<\/code><\/pre>\n<p>\u5728\u6ca1\u6709\u4efb\u4f55\u542f\u52a8\u7684\u60c5\u51b5\u4e0b\uff0c\u4f7f\u7528docker-compose\u4e00\u6b21\u6027\u542f\u52a8\u6240\u6709\u5bb9\u5668\u65f6\uff0c\u53ef\u80fd\u4f1a\u53d1\u751f\u4e00\u4e9b\u9519\u8bef\uff0c\u56e0\u4e3aPostGraphile\u8bd5\u56fe\u5728\u7b49\u5f85PostgreSQL\u542f\u52a8\u4e4b\u524d\u542f\u52a8\u3002\u5173\u4e8e\u8fd9\u4e2a\u73b0\u8c61\uff0c\u6211\u5728\u201cPostGraphile(GraphQL)\u3001Vue.js\u3001Apollo\u3001Vuetify\u3067CRUD\u6a5f\u80fd\u3092\u5b9f\u88c5\u3057\u3066\u307f\u305f\u201d\u4e00\u6587\u4e2d\u8fdb\u884c\u4e86\u63cf\u8ff0\uff0c\u8bf7\u53c2\u8003\u3002<\/p>\n<p>\u73b0\u5728\u5728\u521a\u624d\u7684docker-compose.yml\u6587\u4ef6\u4e2d\uff0c\u5df2\u7ecf\u8bbe\u7f6e\u4e86\u5c06GraphQL\u6a21\u5f0f\u5bfc\u51fa\u5230&#8221;schema.graphql&#8221;\u3002\u5728\u542f\u52a8PostGraphile\u65f6\uff0c\u5b83\u4f1a\u81ea\u52a8\u8bfb\u53d6PostgreSQL\u7684\u6a21\u5f0f\u4fe1\u606f\u5e76\u751f\u6210GraphQL\u6a21\u5f0f\uff0c\u8be5\u5185\u5bb9\u5c06\u88ab\u5bfc\u51fa\u3002\u73b0\u5728\u6211\u4eec\u6765\u68c0\u67e5\u6587\u4ef6\u662f\u5426\u5b58\u5728\u3002<\/p>\n<pre class=\"post-pre\"><code>$ ls ~\/work\/vue-apollo\/tests\/demo\/postgraphile-server\r\nDemoSubscriptionPlugin.js   schema_and_data.sql\r\nschema.graphql\r\n<\/code><\/pre>\n<h2>\u540e\u7aef\u5b9e\u73b0\u89e3\u91ca<\/h2>\n<p>\u4ee5\u4e0a\u5b8c\u6210\u4e86\u4f7f\u7528PostGraphile\u5b9e\u73b0\u7c7b\u4f3c\u4e8e\u539f\u59cbApolloChat\u7684GraphQL\u670d\u52a1\u5668\u7684\u5de5\u4f5c\u3002\u5269\u4e0b\u7684\u5c31\u662f\u4fee\u6539\u524d\u7aef\u6e90\u4ee3\u7801\u4ee5\u4f7f\u7528PostGraphile\uff0c\u8fd9\u6837PostGraphile\u7248\u7684ApolloChat\u5c31\u80fd\u591f\u8fd0\u884c\u4e86\u3002\u4f46\u5728\u6b64\u4e4b\u524d\uff0c\u6211\u5c06\u89e3\u91ca\u4e00\u4e0b\u6211\u4eec\u5728\u4e4b\u524d\u7684\u5de5\u4f5c\u4e2d\u6240\u8fdb\u884c\u7684\u540e\u7aef\u5b9e\u73b0\u3002<\/p>\n<p>\uff08\u8bf4\u660e\u5c06\u5728\u4ee5\u540e\u5199\u51fa\uff09<\/p>\n<h1>\u524d\u7aef\u6539\u52a8<\/h1>\n<p>\u7531\u65bc\u5f8c\u7aef\u7684\u5be6\u73fe\u5df2\u7d93\u5b8c\u6210\uff0c\u6240\u4ee5\u63a5\u4e0b\u4f86\u5c07\u8457\u624b\u8655\u7406\u524d\u7aef\u7684Vue.js\u61c9\u7528\u7a0b\u5f0f\u3002<br \/>\n\u539f\u59cb\u7684ApolloChat\u555f\u52d5URL\u662f\u300chttp:\/\/localhost:8080\/\u300d\uff0c\u4f46\u70ba\u4e86\u907f\u514d\u6df7\u6dc6\uff0c\u6211\u5011\u6c7a\u5b9a\u5c07PostGraphile\u7248\u672c\u7684ApolloChat\u8a2d\u7f6e\u70ba\u300chttp:\/\/localhost:8080\/demo\/\u300d\u3002<\/p>\n<h2>\u7eed\u5199\uff082019-06-19\uff09<\/h2>\n<p>\u5728\u672c\u6587\u4e2d\uff0c\u6211\u5011\u5c07\u4f7f\u7528Altair GraphQL Client\u9032\u884cGraphQL\u67e5\u8a62\uff0c\u4e26\u4e14\u5df2\u7d93\u5c07query\u548cmutation\u8a2d\u7f6e\u5728HEADERS\u4e2d\u4ee5\u8a8d\u8b58JWT\uff0c\u7136\u800c\uff0csubscription\u537b\u7121\u6cd5\u88ab\u8a8d\u8b58\u3002\u5728\u6587\u7ae0\u767c\u5e03\u5f8c\uff0c\u6211\u767c\u73fe\u4e86Graphql Playground\u7684\u5b58\u5728\uff0c\u8a66\u4e86\u4e00\u4e0b\u767c\u73fe\u5b83\u80fd\u5920\u8a8d\u8b58subscription\u4e26\u4e14\u4f7f\u7528JWT\uff0c\u6240\u4ee5\u4f7f\u7528\u5b83\u6703\u66f4\u65b9\u4fbf\u4e00\u4e9b\u3002(\u8ffd\u52a0\u7d50\u675f)<\/p>\n<h2>\u8bf7\u7528\u4e2d\u6587\u6539\u5199GraphQL\u67e5\u8be2\uff0c\u5e76\u4f7f\u7528Altair GraphQL\u5ba2\u6237\u7aef\u8fdb\u884c\u5c1d\u8bd5\u3002<\/h2>\n<p>\u539f\u59cb\u7684ApolloChat\u4f7f\u7528\u7684GraphQL\u67e5\u8be2\u8bed\u53e5\u5b58\u50a8\u5728\u4ee5\u4e0b\u76ee\u5f55\u4e2d\u3002<\/p>\n<pre class=\"post-pre\"><code>$ ls ~\/work\/vue-apollo\/tests\/demo\/src\/graphql\r\nchannel.gql     messageFragment.gql userLogin.gql\r\nchannelFragment.gql messageRemove.gql   userLogout.gql\r\nchannels.gql        messageUpdate.gql   userRegister.gql\r\nmessageAdd.gql      userCurrent.gql\r\nmessageChanged.gql  userFragment.gql\r\n<\/code><\/pre>\n<p>\u5728\u8bd5\u9a8c\u8fc7\u7a0b\u4e2d\uff0c\u6211\u4eec\u4f1a\u9010\u4e2a\u5c06\u8fd9\u4e9b gql \u6587\u4ef6\u4fee\u6539\u4e3a\u9002\u7528\u4e8e PostGraphile \u7684\u7248\u672c\u3002<\/p>\n<p>\u8bf7\u4e0b\u8f7d\u5e76\u5b89\u88c5Altair GraphQL Client\u4ee5\u8fdb\u884c\u67e5\u8be2\u3002<br \/>\n\u6253\u5f00Altair GraphQL Client\uff0c\u5c06URL\u5b57\u6bb5\u8f93\u5165\u4e3a&#8221;http:\/\/localhost:16000\/graphql&#8221;\u3002<\/p>\n<p>\u4ee5\u4e0b\u662fPostGraphile\u81ea\u52a8\u751f\u6210\u7684\u6587\u4ef6\u201cschema.graphql\u201d\uff0c\u5176\u4e2d\u5305\u542b\u4e86GraphQL\u6a21\u5f0f\uff0c\u53ef\u4f9b\u60a8\u5728\u521b\u5efaGraphQL\u67e5\u8be2\u65f6\u53c2\u8003\u3002<br \/>\n\u4e0b\u9762\u662f\u6587\u4ef6\u5185\u5bb9\u7684\u4fee\u6539\u524d\u540e\u3002<\/p>\n<h3>\u788e\u7247<\/h3>\n<h4>\u5c06\u4ee5\u4e0b\u5185\u5bb9\u4ee5\u4e2d\u6587\u8fdb\u884c\u540c\u4e49\u8f6c\u8ff0\uff1a\u7528\u6237\u7247\u6bb5.gql<\/h4>\n<p>\u6ca1\u6709\u53d8\u66f4\u3002<br \/>\n(Means: There are no changes.)<\/p>\n<pre class=\"post-pre\"><code>fragment user on User {\r\n  id\r\n  nickname\r\n}\r\n<\/code><\/pre>\n<h4>channelFragment.gql\u7684\u4e2a\u522b\u53d8\u79cd<\/h4>\n<p>\u6ca1\u6709\u53d8\u66f4\u3002<\/p>\n<pre class=\"post-pre\"><code>fragment channel on Channel {\r\n  id\r\n  name\r\n}\r\n<\/code><\/pre>\n<h4>messageFragment.gql\u7684\u610f\u601d\u662f\u5c06gql\u7247\u6bb5\u4f5c\u4e3a\u6d88\u606f\u3002<\/h4>\n<p>\u539f\u521b<\/p>\n<pre class=\"post-pre\"><code>#import \".\/userFragment.gql\"\r\n\r\nfragment message on Message {\r\n  id\r\n  content\r\n  user {\r\n    ...user\r\n  }\r\n  dateAdded\r\n  dateUpdated\r\n}\r\n<\/code><\/pre>\n<p>\u66f4\u6539\u540e<\/p>\n<pre class=\"post-pre\"><code>#import \".\/userFragment.gql\"\r\n\r\nfragment message on Message {\r\n  id\r\n  content\r\n  userByUserId {\r\n    ...user\r\n  }\r\n  dateAdded\r\n  dateUpdated\r\n}\r\n<\/code><\/pre>\n<h3>\u65e0\u9700\u767b\u5f55\u5373\u53ef\u63d0\u95ee<\/h3>\n<h4>\u7528\u6237\u6ce8\u518c\u7684\u6587\u4ef6.gql<\/h4>\n<p>\u529f\u80fd\uff1a\u7528\u6237\u6ce8\u518c<\/p>\n<p>\u72ec\u521b\u7684<\/p>\n<pre class=\"post-pre\"><code>mutation userRegister ($input: UserRegister!) {\r\n  userRegister(input: $input)\r\n}\r\n<\/code><\/pre>\n<p>\u66f4\u6539\u540e<\/p>\n<pre class=\"post-pre\"><code>mutation userRegister ($input: UserRegisterInput!) {\r\n  userRegister(input: $input) {\r\n    user {\r\n      id\r\n      nickname\r\n      email\r\n    }\r\n  }\r\n}\r\n<\/code><\/pre>\n<p>\u6211\u5011\u5c07\u4ee5\u4e0b\u5167\u5bb9\u8a2d\u5b9a\u70baVARIABLES\uff0c\u7136\u5f8c\u5728Altair GraphQL Client\u4e2d\u9032\u884c\u6e2c\u8a66\u3002<\/p>\n<pre class=\"post-pre\"><code>{\r\n  \"input\": {\r\n    \"nickname\": \"foo\",\r\n    \"email\": \"foo@foo.com\",\r\n    \"password\": \"p@ss\"\r\n  }\r\n}\r\n<\/code><\/pre>\n<div><img decoding=\"async\" class=\"post-images\" title=\"\" src=\"https:\/\/cdn.silicloud.com\/blog-img\/blog\/img\/657d82d5913a08637a6b1920\/129-0.png\" alt=\"\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8 2019-06-18 16.21.29.png\" \/><\/div>\n<p>\u53cd\u5e94<\/p>\n<pre class=\"post-pre\"><code>{\r\n  \"data\": {\r\n    \"userRegister\": {\r\n      \"user\": {\r\n        \"id\": 1,\r\n        \"nickname\": \"foo\",\r\n        \"email\": \"foo@foo.com\"\r\n      }\r\n    }\r\n  }\r\n}\r\n<\/code><\/pre>\n<h4>\u7528\u6237\u9000\u51fa\u767b\u5f55.gql<\/h4>\n<p>\u529f\u80fd\uff1a\u9000\u51fa\u767b\u5f55<\/p>\n<p>\u539f\u521b<\/p>\n<pre class=\"post-pre\"><code>mutation userLogout {\r\n  userLogout\r\n}\r\n<\/code><\/pre>\n<p>\u66f4\u6539\u540e<\/p>\n<pre class=\"post-pre\"><code>mutation userLogout ($input: UserLogoutInput!) {\r\n  userLogout (input: $input) {\r\n    boolean\r\n  }\r\n}\r\n<\/code><\/pre>\n<p>\u5728\u8bbe\u7f6eVARIABLES\u540e\uff0c\u6211\u4eec\u5c06\u5728Altair GraphQL\u5ba2\u6237\u7aef\u4e2d\u8fdb\u884c\u5c1d\u8bd5\u3002<\/p>\n<pre class=\"post-pre\"><code>{\r\n  \"input\": {\r\n  }\r\n}\r\n<\/code><\/pre>\n<div><img decoding=\"async\" class=\"post-images\" title=\"\" src=\"https:\/\/cdn.silicloud.com\/blog-img\/blog\/img\/657d82d5913a08637a6b1920\/140-0.png\" alt=\"\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8 2019-06-18 16.24.26.png\" \/><\/div>\n<p>\u56de\u5e94<\/p>\n<pre class=\"post-pre\"><code>{\r\n  \"data\": {\r\n    \"userLogout\": {\r\n      \"boolean\": true\r\n    }\r\n  }\r\n}\r\n<\/code><\/pre>\n<h4>\u7528\u6237\u767b\u5f55.gql<\/h4>\n<p>\u529f\u80fd\uff1a\u767b\u5f55<\/p>\n<p>\u539f\u7248<\/p>\n<pre class=\"post-pre\"><code>#import \".\/userFragment.gql\"\r\n\r\nmutation userLogin ($email: String!, $password: String!) {\r\n  userLogin (email: $email, password: $password) {\r\n    user {\r\n      ...user\r\n      email\r\n    }\r\n    token {\r\n      id\r\n      userId\r\n      expiration\r\n    }\r\n  }\r\n}\r\n<\/code><\/pre>\n<p>\u66f4\u6539\u540e<\/p>\n<pre class=\"post-pre\"><code>mutation userLogin ($input: UserLoginInput!) {\r\n  userLogin (input: $input) {\r\n    usrAndToken {\r\n      usr {\r\n        id\r\n        nickname\r\n        email\r\n      }\r\n      token\r\n    }\r\n  }\r\n}\r\n<\/code><\/pre>\n<p>\u6211\u5c06\u4ee5\u4e0b\u5185\u5bb9\u8bbe\u7f6e\u4e3a\u53d8\u91cf\uff0c\u5e76\u5c1d\u8bd5\u5728Altair GraphQL Client\u4e2d\u8fd0\u884c\u3002<\/p>\n<pre class=\"post-pre\"><code>{\r\n  \"input\": {\r\n    \"email\": \"foo@foo.com\",\r\n    \"password\": \"p@ss\"\r\n  }\r\n}\r\n<\/code><\/pre>\n<div><img decoding=\"async\" class=\"post-images\" title=\"\" src=\"https:\/\/cdn.silicloud.com\/blog-img\/blog\/img\/657d82d5913a08637a6b1920\/151-0.png\" alt=\"\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8 2019-06-18 16.27.16.png\" \/><\/div>\n<p>\u56de\u5e94<\/p>\n<pre class=\"post-pre\"><code>{\r\n  \"data\": {\r\n    \"userLogin\": {\r\n      \"usrAndToken\": {\r\n        \"usr\": {\r\n          \"id\": 1,\r\n          \"nickname\": \"foo\",\r\n          \"email\": \"foo@foo.com\"\r\n        },\r\n        \"token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYXBvbGxvX2RlbW9fbG9naW5fdXNlciIsInVzZXJfaWQiOjEsImV4cCI6MTU2MDg0NjM3NiwiaWF0IjoxNTYwODQyNzc2LCJhdWQiOiJwb3N0Z3JhcGhpbGUiLCJpc3MiOiJwb3N0Z3JhcGhpbGUifQ.uE6rXeIWDuFaLaAqE56OisICQKzntl5e4L4znSryoXU\"\r\n      }\r\n    }\r\n  }\r\n}\r\n<\/code><\/pre>\n<h3>\u9700\u8981\u767b\u5f55\u624d\u80fd\u67e5\u8be2\u7684\u95ee\u9898<\/h3>\n<p>\u56e0\u4e3a\u5728\u5148\u524d\u7684\u767b\u5f55\u5904\u7406\u54cd\u5e94\u4e2d\u8fd4\u56de\u4e86JWT\u4ee4\u724c\uff0c\u6240\u4ee5\u6211\u4eec\u5c06\u5176\u8bbe\u7f6e\u5728Altair GraphQL\u5ba2\u6237\u7aef\u7684HEADERS\u4e2d\uff0c\u683c\u5f0f\u5982\u4e0b\u3002<\/p>\n<pre class=\"post-pre\"><code>Authorization\r\nBearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYXBvbGxvX2RlbW9fbG9naW5fdXNlciIsInVzZXJfaWQiOjEsImV4cCI6MTU2MDg0NjM3NiwiaWF0IjoxNTYwODQyNzc2LCJhdWQiOiJwb3N0Z3JhcGhpbGUiLCJpc3MiOiJwb3N0Z3JhcGhpbGUifQ.uE6rXeIWDuFaLaAqE56OisICQKzntl5e4L4znSryoXU\r\n<\/code><\/pre>\n<div><img decoding=\"async\" class=\"post-images\" title=\"\" src=\"https:\/\/cdn.silicloud.com\/blog-img\/blog\/img\/657d82d5913a08637a6b1920\/157-0.png\" alt=\"\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8 2019-06-18 16.29.00.png\" \/><\/div>\n<p>\u5982\u679c\u4ee5\u540e\u8be2\u95ee\u65f6\u6536\u5230\u54cd\u5e94&#8221;jwt expired&#8221;\uff0c\u8bf7\u4eceAltair GraphQL\u5ba2\u6237\u7aef\u7684HEADERS\u4e2d\u5220\u9664JWT\uff0c\u7136\u540e\u91cd\u65b0\u6267\u884cuserLogin mutation\uff0c\u5e76\u5c06\u8fd4\u56de\u7684\u4ee4\u724c\u91cd\u65b0\u8bbe\u7f6e\u5230HEADERS\u4e2d\u3002<\/p>\n<h4>\u7528\u6237\u5f53\u524d.gql<\/h4>\n<p>\u529f\u80fd\uff1a\u83b7\u53d6\u5df2\u767b\u5f55\u7528\u6237\u7684\u4fe1\u606f<\/p>\n<p>\u6ca1\u6709\u53d8\u66f4\u3002<\/p>\n<pre class=\"post-pre\"><code>#import \".\/userFragment.gql\"\r\n\r\nquery userCurrent {\r\n  userCurrent {\r\n    ...user\r\n    email\r\n  }\r\n}\r\n<\/code><\/pre>\n<p>\u6211\u5c06\u5728Altair GraphQL\u5ba2\u6237\u7aef\u4e2d\u5c1d\u8bd5\u6267\u884c\u3002\u6211\u76f4\u63a5\u8f93\u5165\u4e86\u6240\u5bfc\u5165\u7684\u7247\u6bb5\u3002<\/p>\n<div><img decoding=\"async\" class=\"post-images\" title=\"\" src=\"https:\/\/cdn.silicloud.com\/blog-img\/blog\/img\/657d82d5913a08637a6b1920\/164-0.png\" alt=\"\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8 2019-06-18 16.34.30.png\" \/><\/div>\n<p>\u54cd\u5e94<\/p>\n<pre class=\"post-pre\"><code>{\r\n  \"data\": {\r\n    \"userCurrent\": {\r\n      \"id\": 1,\r\n      \"nickname\": \"foo\",\r\n      \"email\": \"foo@foo.com\"\r\n    }\r\n  }\r\n}\r\n<\/code><\/pre>\n<h4>\u9891\u9053.gql<\/h4>\n<p>\u529f\u80fd\uff1a\u83b7\u53d6\u6240\u6709\u9891\u9053<\/p>\n<p>\u539f\u7248<\/p>\n<pre class=\"post-pre\"><code>#import \".\/channelFragment.gql\"\r\n\r\nquery channels {\r\n  channels {\r\n    ...channel\r\n  }\r\n}\r\n<\/code><\/pre>\n<p>\u53d8\u66f4\u540e<\/p>\n<pre class=\"post-pre\"><code>#import \".\/channelFragment.gql\"\r\n\r\nquery channels {\r\n  allChannels {\r\n    nodes {\r\n      ...channel\r\n    }\r\n  }\r\n}\r\n<\/code><\/pre>\n<p>\u6211\u5c06\u5c1d\u8bd5\u4f7f\u7528Altair GraphQL Client\u3002<\/p>\n<div><img decoding=\"async\" class=\"post-images\" title=\"\" src=\"https:\/\/cdn.silicloud.com\/blog-img\/blog\/img\/657d82d5913a08637a6b1920\/174-0.png\" alt=\"\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8 2019-06-18 16.38.59.png\" \/><\/div>\n<p>\u56de\u5e94<\/p>\n<pre class=\"post-pre\"><code>{\r\n  \"data\": {\r\n    \"allChannels\": {\r\n      \"nodes\": [\r\n        {\r\n          \"id\": \"general\",\r\n          \"name\": \"General discussion\"\r\n        },\r\n        {\r\n          \"id\": \"help\",\r\n          \"name\": \"Ask for or give help\"\r\n        },\r\n        {\r\n          \"id\": \"random\",\r\n          \"name\": \"Have fun chatting!\"\r\n        }\r\n      ]\r\n    }\r\n  }\r\n}\r\n<\/code><\/pre>\n<h4>\u9891\u9053.gql<\/h4>\n<p>\u529f\u80fd\uff1a\u901a\u8fc7id\u641c\u7d22\u9891\u9053\uff0c\u5e76\u83b7\u53d6\u8be5\u9891\u9053\u7684\u6240\u6709\u6d88\u606f\u3002<br \/>\n\u80fd\u529b\uff1a\u6839\u636eID\u641c\u7d22\u9891\u9053\uff0c\u5e76\u83b7\u53d6\u8be5\u9891\u9053\u7684\u6240\u6709\u6d88\u606f\u3002<\/p>\n<p>\u539f\u59cb\u7684<\/p>\n<pre class=\"post-pre\"><code>#import \".\/channelFragment.gql\"\r\n#import \".\/messageFragment.gql\"\r\n\r\nquery channel ($id: ID!) {\r\n  channel (id: $id) {\r\n    ...channel\r\n    messages {\r\n      ...message\r\n    }\r\n  }\r\n}\r\n<\/code><\/pre>\n<p>\u66f4\u6539\u540e<\/p>\n<pre class=\"post-pre\"><code>#import \".\/channelFragment.gql\"\r\n#import \".\/messageFragment.gql\"\r\n\r\nquery channel ($id: String!) {\r\n  channelById (id: $id) {\r\n    ...channel\r\n    messagesByChannelId {\r\n      nodes {\r\n        ...message\r\n      }\r\n    }\r\n  }\r\n}\r\n<\/code><\/pre>\n<p>\u6211\u5c06\u5728 Altair GraphQL \u5ba2\u6237\u7aef\u4e2d\u8bbe\u7f6e VARIABLES \u5e76\u8fdb\u884c\u5c1d\u8bd5\u3002<\/p>\n<pre class=\"post-pre\"><code>{\r\n  \"id\": \"general\"\r\n}\r\n<\/code><\/pre>\n<div><img decoding=\"async\" class=\"post-images\" title=\"\" src=\"https:\/\/cdn.silicloud.com\/blog-img\/blog\/img\/657d82d5913a08637a6b1920\/185-0.png\" alt=\"\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8 2019-06-18 16.41.31.png\" \/><\/div>\n<p>\u54cd\u5e94<\/p>\n<pre class=\"post-pre\"><code>{\r\n  \"data\": {\r\n    \"channelById\": {\r\n      \"id\": \"general\",\r\n      \"name\": \"General discussion\",\r\n      \"messagesByChannelId\": {\r\n        \"nodes\": [\r\n          {\r\n            \"id\": 1,\r\n            \"content\": \"Welcome to the chat!\",\r\n            \"userByUserId\": {\r\n              \"id\": 0,\r\n              \"nickname\": \"The Bot\"\r\n            },\r\n            \"dateAdded\": \"2019-06-18T07:10:27.671412\",\r\n            \"dateUpdated\": null\r\n          }\r\n        ]\r\n      }\r\n    }\r\n  }\r\n}\r\n<\/code><\/pre>\n<h4>\u6d88\u606f\u6dfb\u52a0.gql<\/h4>\n<p>\u529f\u80fd\uff1a\u5728\u6307\u5b9a\u7684\u9891\u9053\u4e2d\u6dfb\u52a0\u6d88\u606f<\/p>\n<p>\u539f\u521b<\/p>\n<pre class=\"post-pre\"><code>#import \".\/messageFragment.gql\"\r\n\r\nmutation messageAdd ($input: MessageAdd!) {\r\n  messageAdd (input: $input) {\r\n    ...message\r\n  }\r\n}\r\n<\/code><\/pre>\n<p>\u66f4\u6539\u4e4b\u540e zh\u012b<\/p>\n<pre class=\"post-pre\"><code>#import \".\/messageFragment.gql\"\r\n\r\nmutation messageAdd ($input: MessageAddInput!) {\r\n  messageAdd (input: $input) {\r\n    message {\r\n      ...message\r\n    }\r\n  }\r\n}\r\n<\/code><\/pre>\n<p>\u6211\u5c06\u4f7f\u7528\u4ee5\u4e0b\u8bbe\u7f6e\u5728Altair GraphQL\u5ba2\u6237\u7aef\u4e2d\u5c1d\u8bd5\u3002<\/p>\n<pre class=\"post-pre\"><code>{\r\n  \"input\": {\r\n    \"channelId\": \"general\",\r\n    \"content\": \"\u30cf\u30ed\u30fc\u30ef\u30fc\u30eb\u30c9\"\r\n  }\r\n}\r\n<\/code><\/pre>\n<div><img decoding=\"async\" class=\"post-images\" title=\"\" src=\"https:\/\/cdn.silicloud.com\/blog-img\/blog\/img\/657d82d5913a08637a6b1920\/196-0.png\" alt=\"\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8 2019-06-18 16.44.20.png\" \/><\/div>\n<p>\u56de\u5e94<\/p>\n<pre class=\"post-pre\"><code>{\r\n  \"data\": {\r\n    \"messageAdd\": {\r\n      \"message\": {\r\n        \"id\": 2,\r\n        \"content\": \"\u30cf\u30ed\u30fc\u30ef\u30fc\u30eb\u30c9\",\r\n        \"userByUserId\": {\r\n          \"id\": 1,\r\n          \"nickname\": \"foo\"\r\n        },\r\n        \"dateAdded\": \"2019-06-18T07:44:02.534676\",\r\n        \"dateUpdated\": null\r\n      }\r\n    }\r\n  }\r\n}\r\n<\/code><\/pre>\n<p>\u53ea\u9700\u63d0\u4f9b\u4e00\u79cd\u4e2d\u6587\u539f\u751f\u9009\u9879\uff1a<\/p>\n<p>\u5728\u8fd9\u91cc\u518d\u6b21\u53d1\u51fa\u901a\u9053\u67e5\u8be2\uff0c\u53ef\u4ee5\u786e\u8ba4\u5df2\u6dfb\u52a0\u4e86\u201c\u4f60\u597d\uff0c\u4e16\u754c\u201d\u6d88\u606f\u3002<\/p>\n<div><img decoding=\"async\" class=\"post-images\" title=\"\" src=\"https:\/\/cdn.silicloud.com\/blog-img\/blog\/img\/657d82d5913a08637a6b1920\/200-0.png\" alt=\"\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8 2019-06-18 16.45.42.png\" \/><\/div>\n<h4>\u66f4\u65b0\u4fe1\u606f.gql<\/h4>\n<p>\u529f\u80fd\uff1a\u6d88\u606f\u66f4\u6539\uff08\u5728ApolloChat\u4e2d\u672a\u4f7f\u7528\uff09<\/p>\n<p>\u539f\u6587<\/p>\n<pre class=\"post-pre\"><code>#import \".\/messageFragment.gql\"\r\n\r\nmutation messageUpdate ($input: MessageUpdate!) {\r\n  messageUpdate (input: $input) {\r\n    ...message\r\n  }\r\n}\r\n<\/code><\/pre>\n<p>\u4fee\u6539\u540e<\/p>\n<pre class=\"post-pre\"><code>#import \".\/messageFragment.gql\"\r\n\r\nmutation messageUpdate ($input: UpdateMessageByIdInput!) {\r\n  updateMessageById (input: $input) {\r\n    message {\r\n      ...message\r\n    }\r\n  }\r\n}\r\n<\/code><\/pre>\n<p>\u6211\u5c06\u5bf9\u4e4b\u524d\u4f7f\u7528messageAdd mutation\u6dfb\u52a0\u7684\u6d88\u606f\u8fdb\u884c\u4fee\u6539\u3002VARIABLES\u662f\u6307\u3002<\/p>\n<pre class=\"post-pre\"><code>{\r\n  \"input\": {\r\n    \"messagePatch\": {\r\n      \"content\": \"\u3053\u3093\u306b\u3061\u306f\u4e16\u754c\"\r\n    },\r\n    \"id\": 2\r\n  }\r\n}\r\n<\/code><\/pre>\n<div><img decoding=\"async\" class=\"post-images\" title=\"\" src=\"https:\/\/cdn.silicloud.com\/blog-img\/blog\/img\/657d82d5913a08637a6b1920\/209-0.png\" alt=\"\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8 2019-06-18 16.48.38.png\" \/><\/div>\n<p>\u56de\u5e94<\/p>\n<pre class=\"post-pre\"><code>{\r\n  \"data\": {\r\n    \"updateMessageById\": {\r\n      \"message\": {\r\n        \"id\": 2,\r\n        \"content\": \"\u3053\u3093\u306b\u3061\u306f\u4e16\u754c\",\r\n        \"userByUserId\": {\r\n          \"id\": 1,\r\n          \"nickname\": \"foo\"\r\n        },\r\n        \"dateAdded\": \"2019-06-18T07:44:02.534676\",\r\n        \"dateUpdated\": \"2019-06-18T07:48:16.794758\"\r\n      }\r\n    }\r\n  }\r\n}\r\n<\/code><\/pre>\n<p>\u5f53\u60a8\u5728\u6b64\u5904\u53d1\u51fa\u901a\u9053\u67e5\u8be2\u65f6\uff0c\u60a8\u53ef\u4ee5\u786e\u8ba4\u201cHello World\u201d\u6d88\u606f\u5df2\u66f4\u6539\u4e3a\u201c\u4f60\u597d\u4e16\u754c\u201d\u3002<\/p>\n<div><img decoding=\"async\" class=\"post-images\" title=\"\" src=\"https:\/\/cdn.silicloud.com\/blog-img\/blog\/img\/657d82d5913a08637a6b1920\/213-0.png\" alt=\"\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8 2019-06-18 16.50.14.png\" \/><\/div>\n<h4>\u5220\u9664\u4fe1\u606f.gql<\/h4>\n<p>\u529f\u80fd\uff1a\u6d88\u606f\u5220\u9664\uff08ApolloChat\u672a\u4f7f\u7528\uff09<\/p>\n<p>\u539f\u521b<\/p>\n<pre class=\"post-pre\"><code>#import \".\/messageFragment.gql\"\r\n\r\nmutation messageRemove ($id: ID!) {\r\n  messageRemove (id: $id) {\r\n    ...message\r\n  }\r\n}\r\n<\/code><\/pre>\n<p>\u66f4\u6539\u540e<\/p>\n<pre class=\"post-pre\"><code>#import \".\/messageFragment.gql\"\r\n\r\nmutation messageRemove ($input: DeleteMessageByIdInput!) {\r\n  deleteMessageById (input: $input) {\r\n    message {\r\n      ...message\r\n    }\r\n  }\r\n}\r\n<\/code><\/pre>\n<p>\u6211\u5c06\u5c1d\u8bd5\u5220\u9664\u5728\u5148\u524d\u7684messageUpdate\u53d8\u5f02\u4e2d\u66f4\u6539\u7684\u6d88\u606f\u3002 VARIABLES \u662f\u2026<\/p>\n<pre class=\"post-pre\"><code>{\r\n  \"input\": {\r\n    \"id\": 2\r\n  }\r\n}\r\n<\/code><\/pre>\n<div><img decoding=\"async\" class=\"post-images\" title=\"\" src=\"https:\/\/cdn.silicloud.com\/blog-img\/blog\/img\/657d82d5913a08637a6b1920\/222-0.png\" alt=\"\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8 2019-06-18 16.52.23.png\" \/><\/div>\n<p>\u56de\u5e94<\/p>\n<pre class=\"post-pre\"><code>{\r\n  \"data\": {\r\n    \"deleteMessageById\": {\r\n      \"message\": {\r\n        \"id\": 2,\r\n        \"content\": \"\u3053\u3093\u306b\u3061\u306f\u4e16\u754c\",\r\n        \"userByUserId\": {\r\n          \"id\": 1,\r\n          \"nickname\": \"foo\"\r\n        },\r\n        \"dateAdded\": \"2019-06-18T07:44:02.534676\",\r\n        \"dateUpdated\": \"2019-06-18T07:48:16.794758\"\r\n      }\r\n    }\r\n  }\r\n}\r\n<\/code><\/pre>\n<p>\u5728\u8fd9\u91cc\u53d1\u51fa\u9891\u9053\u67e5\u8be2\uff0c\u53ef\u4ee5\u786e\u8ba4\u5df2\u7ecf\u5220\u9664\u4e86\u201c\u4f60\u597d\u4e16\u754c\u201d\u7684\u6d88\u606f\u3002<\/p>\n<div><img decoding=\"async\" class=\"post-images\" title=\"\" src=\"https:\/\/cdn.silicloud.com\/blog-img\/blog\/img\/657d82d5913a08637a6b1920\/226-0.png\" alt=\"\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8 2019-06-18 16.53.14.png\" \/><\/div>\n<h4>\u6d88\u606f\u5df2\u66f4\u6539.gql<\/h4>\n<p>\u529f\u80fd\uff1a\u8ba2\u9605\u670d\u52a1\u3002\u82e5\u6709\u4eba\u5728\u6307\u5b9a\u9891\u9053\u4e2d\u6dfb\u52a0\u3001\u4fee\u6539\u6216\u5220\u9664\u6d88\u606f\uff0c\u670d\u52a1\u5668\u5c06\u53d1\u9001\u901a\u77e5\u3002<\/p>\n<p>\u300c\u7ffb\u8bd1\u4e3a\u6c49\u8bed\u540e\u7684\u7ed3\u679c\u300d<\/p>\n<pre class=\"post-pre\"><code>#import \".\/messageFragment.gql\"\r\n\r\nsubscription messageChanged ($channelId: ID!) {\r\n  messageChanged (channelId: $channelId) {\r\n    type\r\n    message {\r\n      ...message\r\n    }\r\n  }\r\n}\r\n<\/code><\/pre>\n<p>\u66f4\u6539\u4e4b\u540e<\/p>\n<pre class=\"post-pre\"><code>#import \".\/messageFragment.gql\"\r\n\r\nfragment messageNullable on MessageNullable {\r\n  id\r\n  channelId\r\n  userId\r\n  content\r\n  dateAdded\r\n  dateUpdated\r\n}\r\nsubscription messageChanged ($channelId: String!) {\r\n  messageChanged (channelId: $channelId) {\r\n    type\r\n    message {\r\n      ...message\r\n    }\r\n    oldrec {\r\n      ...messageNullable\r\n    }\r\n  }\r\n}\r\n<\/code><\/pre>\n<h5>\u5173\u4e8e\u8ba2\u9605\u8bd5\u7528\u7684\u8bae\u9898<\/h5>\n<p>\u5728Altair GraphQL Client\u4e2d\uff0c\u53ea\u9700\u5728HEADERS\u4e2d\u8bbe\u7f6eJWT\u4ee4\u724c\uff0c\u5373\u53ef\u5c1d\u8bd5\u767b\u5f55\u540e\u7684\u67e5\u8be2\u548c\u53d8\u66f4\u64cd\u4f5c\uff0c\u4f46\u662f\uff0c\u8ba2\u9605\u64cd\u4f5c\u5374\u65e0\u6cd5\u8bc6\u522b\u8be5\u4ee4\u724c\u3002<br \/>\n\u5173\u4e8e\u8fd9\u4e2a\u95ee\u9898\uff0c\u53ef\u4ee5\u53c2\u8003\u4ee5\u4e0b\u63a8\u6587\uff1a<\/p>\n<blockquote><p>\u6211\u4e86\u89e3\u5230\u4e86Altair GraphQL Client\u7684\u5b58\u5728\u3002\u6211\u8bd5\u7740\u7acb\u523b\u4f7f\u7528\u5b83\u4e0ePostGraphile\u914d\u5408\uff0c\u5c06JWT\u4ee4\u724c\u8bbe\u7f6e\u5230\u5934\u90e8\u5e76\u5c1d\u8bd5\u4f7f\u7528\uff0c\u67e5\u8be2\u548c\u53d8\u66f4\u64cd\u4f5c\u90fd\u80fd\u8bc6\u522b\u4ee4\u724c\uff0c\u4f46\u662f\u8ba2\u9605\u64cd\u4f5c\uff08WebSocket\uff09\u5374\u4e0d\u80fd\u8bc6\u522b\u3002\u662f\u6211\u7684\u4f7f\u7528\u65b9\u6cd5\u51fa\u4e86\u95ee\u9898\u5417\uff1f\u4e0d\u8fc7\u8fd9\u4e2a\u5de5\u5177\u8fd8\u662f\u5f88\u65b9\u4fbf\u7684\u3002<\/p><\/blockquote>\n<p><script><\/script><\/p>\n<blockquote><p>\u563f\uff0c\u5bf9\u4e8e\u6df7\u6dc6\u611f\u5230\u62b1\u6b49\u3002\u5728\u8ba2\u9605\u4e2d\uff0c\u7531\u4e8e\u4f7f\u7528\u4e86Websockets\uff08\u4e0d\u5141\u8bb8\u6307\u5b9a\u5934\u4fe1\u606fhttps:\/\/t.co\/m4iPzKn7bV\uff09\uff0c\u4f60\u65e0\u6cd5\u6307\u5b9a\u5934\u4fe1\u606f\u3002\u5f53\u5f00\u59cb\u8ba2\u9605\u8fdb\u884c\u8eab\u4efd\u9a8c\u8bc1\u65f6\uff0c\u65e0\u6cd5\u5c06\u8fde\u63a5\u53c2\u6570\u4f20\u9012\u7ed9\u8ba2\u9605\u670d\u52a1\u5668\u3002<\/p><\/blockquote>\n<p><script><\/script><\/p>\n<blockquote><p>Apollo\u7684\u8ba2\u9605\u8ba4\u8bc1\u7684\u63cf\u8ff0\u6765\u81ea\u4e8ehttps:\/\/t.co\/QqE3fDtT7z\u2014 Sir Muel I ?? (@imolorhe) 2019\u5e746\u67087\u65e5\u3002<\/p><\/blockquote>\n<p><script><\/script><\/p>\n<p>\u7531\u4e8e\u8fd9\u79cd\u60c5\u51b5\uff0c\u6211\u4eec\u51b3\u5b9a\u5728\u540e\u7aef\u653e\u5bbd\u5b89\u5168\u6027\uff0c\u4ee5\u4fbf\u5373\u4f7f\u4e0d\u767b\u5f55\u4e5f\u53ef\u4ee5\u5c1d\u8bd5\u4f7f\u7528messageChanged\u8ba2\u9605\u3002\u6211\u4eec\u5c06\u5728PostGraphile\u63d2\u4ef6\u300cDemoSubscriptionPlugin.js\u300d\u4e2d\u5c06DEBUG_MODE\u8bbe\u7f6e\u4e3atrue\u3002<\/p>\n<pre class=\"post-pre\"><code><span class=\"o\">-<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">DEBUG_MODE<\/span> <span class=\"o\">=<\/span> <span class=\"kc\">false<\/span>\r\n<span class=\"o\">+<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">DEBUG_MODE<\/span> <span class=\"o\">=<\/span> <span class=\"kc\">true<\/span>\r\n<span class=\"p\">(<\/span><span class=\"nx\">\u5f8c\u7565<\/span><span class=\"p\">)<\/span>\r\n<\/code><\/pre>\n<p>\u4e3a\u4e86\u542f\u7528\u66f4\u6539\u540e\u7684DemoSubscriptionPlugin.js\uff0c\u9700\u8981\u91cd\u65b0\u542f\u52a8PostGraphile\u5bb9\u5668\u3002<\/p>\n<pre class=\"post-pre\"><code>cd ~\/work\/laradock\r\ndocker stop laradock_postgraphile_demo_1\r\ndocker-compose up -d postgres pgadmin workspace postgraphile_demo\r\n<\/code><\/pre>\n<p>\u73b0\u5728\u53ef\u4ee5\u7b49\u5f85\u63a5\u6536`messageChanged`\u7684\u6d88\u606f\u4e86\u3002<br \/>\n\u6b64\u5916\uff0c\u4e3a\u4e86\u8ba9`messageChanged`\u80fd\u591f\u5728\u521b\u5efa\u53d1\u9001\u6570\u636e\u65f6\u4ecePostgreSQL\u4e2d\u83b7\u53d6\u6570\u636e\uff0c\u6211\u4eec\u4f1a\u5728\u6682\u65f6\u964d\u4f4ePostgreSQL\u7684\u5b89\u5168\u6027\u3002<\/p>\n<p>\u8fdb\u5165Laradock\u7684workspace\uff0c\u7136\u540e\u8fdb\u5165psql\u3002<\/p>\n<pre class=\"post-pre\"><code>docker-compose exec --user=laradock workspace bash\r\npsql -h postgres -U default demodb\r\n<\/code><\/pre>\n<p>\u533f\u540d\u7528\u6237\u5c06\u88ab\u8d4b\u4e88\u6bcf\u4e2a\u8868\u7684select\u6743\u9650\u3002<\/p>\n<pre class=\"post-pre\"><code>grant select on table apollo_demo.messages to apollo_demo_anonymous;\r\ngrant select on table apollo_demo.users to apollo_demo_anonymous;\r\ngrant select on table apollo_demo.channels to apollo_demo_anonymous;\r\n<\/code><\/pre>\n<p>\u73b0\u5728\u53ef\u4ee5\u63a5\u6536\u5230messageChanged\u3002<br \/>\n\u7531\u4e8e\u4ecd\u7136\u9700\u8981\u4f7f\u7528psql\uff0c\u8bf7\u4fdd\u6301\u5176\u8fd0\u884c\u72b6\u6001\u3002<\/p>\n<p>\u90a3\u4e48\uff0c\u8ba9\u6211\u4eec\u5c06\u4ee5\u4e0b\u5185\u5bb9\u8bbe\u7f6e\u5230VARIABLES\u4e2d\uff0c\u5e76\u5c1d\u8bd5\u5728Altair GraphQL Client\u4e2d\u6267\u884c\u3002<\/p>\n<pre class=\"post-pre\"><code>{\r\n  \"channelId\": \"general\"\r\n}\r\n<\/code><\/pre>\n<p>\u7b49\u5f85\u63a5\u6536\u4fe1\u606f\u7684\u5c4f\u5e55\u3002<\/p>\n<div><img decoding=\"async\" class=\"post-images\" title=\"\" src=\"https:\/\/cdn.silicloud.com\/blog-img\/blog\/img\/657d82d5913a08637a6b1920\/254-0.png\" alt=\"\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8 2019-06-18 17.01.36.png\" \/><\/div>\n<p>\u5728\u8fd9\u4e2a\u72b6\u6001\u4e0b\uff0c\u4f7f\u7528psql\u53d1\u51fa\u4ee5\u4e0b\u7684INSERT\u8bed\u53e5\u3002<\/p>\n<pre class=\"post-pre\"><code>INSERT INTO apollo_demo.messages (channel_id, user_id, content) VALUES\r\n    ('general', 1, '\u7f8e\u5473\u3057\u3044\uff01');\r\n<\/code><\/pre>\n<div><img decoding=\"async\" class=\"post-images\" title=\"\" src=\"https:\/\/cdn.silicloud.com\/blog-img\/blog\/img\/657d82d5913a08637a6b1920\/257-0.png\" alt=\"\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8 2019-06-18 17.03.57.png\" \/><\/div>\n<p>\u56de\u5e94<\/p>\n<pre class=\"post-pre\"><code>{\r\n  \"data\": {\r\n    \"messageChanged\": {\r\n      \"type\": \"INSERT\",\r\n      \"message\": {\r\n        \"id\": 3,\r\n        \"content\": \"\u7f8e\u5473\u3057\u3044\uff01\",\r\n        \"userByUserId\": {\r\n          \"id\": 1,\r\n          \"nickname\": \"foo\"\r\n        },\r\n        \"dateAdded\": \"2019-06-18T08:02:26.293796\",\r\n        \"dateUpdated\": null\r\n      },\r\n      \"oldrec\": null\r\n    }\r\n  }\r\n}\r\n<\/code><\/pre>\n<p>\u901a\u8fc7psql INSERT\u547d\u4ee4\u8fd4\u56de\u4e86\u4e00\u4e2a\u6570\u636e\u54cd\u5e94\u3002<br \/>\n\u5bf9\u4e8e\u8fd9\u4e2a\u6570\u636e\uff0c\u4f7f\u7528psql\u53d1\u51faUPDATE\u8bed\u53e5\u3002<\/p>\n<pre class=\"post-pre\"><code>UPDATE apollo_demo.messages SET\r\n    content = '\u7f8e\u5473\u3057\u3044\uff01'\r\n    WHERE id = 3;\r\n<\/code><\/pre>\n<div><img decoding=\"async\" class=\"post-images\" title=\"\" src=\"https:\/\/cdn.silicloud.com\/blog-img\/blog\/img\/657d82d5913a08637a6b1920\/262-0.png\" alt=\"\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8 2019-06-18 17.08.37.png\" \/><\/div>\n<p>\u56de\u5e94<\/p>\n<pre class=\"post-pre\"><code>{\r\n  \"data\": {\r\n    \"messageChanged\": {\r\n      \"type\": \"UPDATE\",\r\n      \"message\": {\r\n        \"id\": 3,\r\n        \"content\": \"\u7f8e\u5473\u3057\u3044\uff01\",\r\n        \"userByUserId\": {\r\n          \"id\": 1,\r\n          \"nickname\": \"foo\"\r\n        },\r\n        \"dateAdded\": \"2019-06-18T08:02:26.293796\",\r\n        \"dateUpdated\": \"2019-06-18T08:07:13.937846\"\r\n      },\r\n      \"oldrec\": {\r\n        \"id\": 3,\r\n        \"channelId\": null,\r\n        \"userId\": null,\r\n        \"content\": \"\u7f8e\u5473\u3057\u3044\uff01\",\r\n        \"dateAdded\": null,\r\n        \"dateUpdated\": null\r\n      }\r\n    }\r\n  }\r\n}\r\n<\/code><\/pre>\n<p>\u66f4\u65b0\u540e\u7684\u6570\u636e\u5728\u54cd\u5e94\u4e2d\u8fd4\u56de\u4e86\u3002<br \/>\n\u6211\u5bf9oldrec\uff08UPDATE\u524d\u7684\u884c\uff09\u7684\u591a\u6570\u5217\u4e3a\u7a7a\u503c\u611f\u5230\u4e0d\u6ee1\u3002\u8fd9\u6b21\u5728PostgreSQL\u4e2d\u5b9a\u4e49\u6a21\u5f0f\u65f6\uff0c\u5217\u540d\u91c7\u7528\u4e86\u86c7\u5f62\u547d\u540d\u6cd5\u3002\u5f53PostGraphile\u81ea\u52a8\u751f\u6210GraphQL\u6a21\u5f0f\u65f6\uff0c\u5b83\u4f1a\u5c06\u86c7\u5f62\u547d\u540d\u6cd5\u8f6c\u6362\u4e3a\u9a7c\u5cf0\u547d\u540d\u6cd5\u3002\u4f3c\u4e4e\u5bf9\u4e8e\u8fd9\u4e9b\u5df2\u7ecf\u8fdb\u884c\u4e86\u8f6c\u6362\u7684\u5217\uff0c\u6ca1\u6709\u4f20\u9012\u4efb\u4f55\u503c\uff0c\u5bfc\u81f4\u5b83\u4eec\u53d8\u6210\u4e86\u7a7a\u503c\u3002\u5728AppolloChat\u4e2d\uff0c\u53ea\u4f7f\u7528\u4e86oldrec.id\uff0c\u6240\u4ee5\u6b63\u786e\u4f20\u9012\u503c\u7684\u5b9e\u73b0\u5c06\u6210\u4e3a\u4ee5\u540e\u7684\u8bfe\u9898\uff0c\u6211\u4eec\u5c06\u7ee7\u7eed\u8fdb\u884c\u4e0b\u53bb\u3002<\/p>\n<p>\u5bf9\u8fd4\u56de\u7684\u6570\u636e\uff0c\u4f7f\u7528psql\u6267\u884cDELETE\u8bed\u53e5\u3002<\/p>\n<pre class=\"post-pre\"><code>DELETE FROM apollo_demo.messages\r\n    WHERE id = 3;\r\n<\/code><\/pre>\n<div><img decoding=\"async\" class=\"post-images\" title=\"\" src=\"https:\/\/cdn.silicloud.com\/blog-img\/blog\/img\/657d82d5913a08637a6b1920\/268-0.png\" alt=\"\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8 2019-06-18 17.09.44.png\" \/><\/div>\n<p>\u5bf9\u4e8e&#8221;\u30ec\u30b9\u30dd\u30f3\u30b9&#8221;\u8fd9\u4e2a\u8bcd\u53ef\u4ee5\u6709\u4ee5\u4e0b\u7ffb\u8bd1\uff1a<\/p>\n<p>&#8211; \u56de\u5e94<br \/>\n&#8211; \u54cd\u5e94<br \/>\n&#8211; \u53cd\u5e94<br \/>\n&#8211; \u5e94\u7b54<br \/>\n&#8211; \u53cd\u9988<br \/>\n&#8211; \u56de\u5e94\u65f6\u95f4<\/p>\n<pre class=\"post-pre\"><code>{\r\n  \"data\": {\r\n    \"messageChanged\": {\r\n      \"type\": \"DELETE\",\r\n      \"message\": null,\r\n      \"oldrec\": {\r\n        \"id\": 3,\r\n        \"channelId\": null,\r\n        \"userId\": null,\r\n        \"content\": \"\u7f8e\u5473\u3057\u3044\uff01\",\r\n        \"dateAdded\": null,\r\n        \"dateUpdated\": null\r\n      }\r\n    }\r\n  }\r\n}\r\n<\/code><\/pre>\n<p>\u6211\u4e5f\u6536\u5230\u4e86\u5173\u4e8e\u5220\u9664\u7684\u901a\u77e5\u3002<\/p>\n<p>\u901a\u8fc7\u6267\u884c\u4ee5\u4e0bREVOKE\u8bed\u53e5\uff0c\u5728\u5b8c\u6210messageChanged\u8ba2\u9605\u7684\u5c1d\u8bd5\u540e\uff0c\u6062\u590d\u4e4b\u524d\u653e\u5bbd\u7684\u5b89\u5168\u63aa\u65bd\u3002<\/p>\n<pre class=\"post-pre\"><code>revoke select on table apollo_demo.messages from apollo_demo_anonymous;\r\nrevoke select on table apollo_demo.users from apollo_demo_anonymous;\r\nrevoke select on table apollo_demo.channels from apollo_demo_anonymous;\r\n<\/code><\/pre>\n<p>\u5728\u8fd9\u79cd\u72b6\u6001\u4e0b\uff0c\u5982\u679c\u4ecepsql\u518d\u6b21\u6267\u884c\u4e0a\u8ff0\u7684INSERT\u8bed\u53e5\uff0c\u4f1a\u6536\u5230\u4ee5\u4e0b\u9519\u8bef\uff0c\u5e76\u4e14\u53ef\u4ee5\u4e86\u89e3\u5230PostgreSQL\u65b9\u9762\u7684\u5b89\u5168\u5df2\u7ecf\u6062\u590d\u3002<\/p>\n<div><img decoding=\"async\" class=\"post-images\" title=\"\" src=\"https:\/\/cdn.silicloud.com\/blog-img\/blog\/img\/657d82d5913a08637a6b1920\/275-0.png\" alt=\"\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8 2019-06-18 17.14.12.png\" \/><\/div>\n<p>\u56de\u5e94<\/p>\n<pre class=\"post-pre\"><code>{\r\n  \"errors\": [\r\n    {\r\n      \"message\": \"permission denied for table messages\",\r\n      \"locations\": [\r\n        {\r\n          \"line\": 25,\r\n          \"column\": 5\r\n        }\r\n      ],\r\n      \"path\": [\r\n        \"messageChanged\",\r\n        \"message\"\r\n      ]\r\n    }\r\n  ],\r\n  \"data\": {\r\n    \"messageChanged\": {\r\n      \"type\": \"INSERT\",\r\n      \"message\": null,\r\n      \"oldrec\": null\r\n    }\r\n  }\r\n}\r\n<\/code><\/pre>\n<p>\u6211\u4f1a\u9000\u51fapsql\u548cLaradock\u7684workspace\uff0c\u7136\u540e\u8fd4\u56de\u5230\u4e3b\u673a\u3002<br \/>\n\u6211\u4f1a\u70b9\u51fbAltair GraphQL Client\u7684\u505c\u6b62\u6309\u94ae\u6765\u505c\u6b62messageChanged\u8ba2\u9605\u3002<\/p>\n<p>\u63a5\u4e0b\u6765\uff0c\u6211\u4eec\u5c06\u4fee\u590dPostGraphile\u63d2\u4ef6&#8221;DemoSubscriptionPlugin.js&#8221;\u7684\u5b89\u5168\u95ee\u9898\u3002<br \/>\n\u5c06DEBUG_MODE\u8bbe\u7f6e\u4e3afalse\uff0c\u5982\u4e0b\u6240\u793a\u3002<\/p>\n<pre class=\"post-pre\"><code><span class=\"o\">-<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">DEBUG_MODE<\/span> <span class=\"o\">=<\/span> <span class=\"kc\">true<\/span>\r\n<span class=\"o\">+<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">DEBUG_MODE<\/span> <span class=\"o\">=<\/span> <span class=\"kc\">false<\/span>\r\n<span class=\"p\">(<\/span><span class=\"nx\">\u5f8c\u7565<\/span><span class=\"p\">)<\/span>\r\n<\/code><\/pre>\n<p>\u4e3a\u4e86\u4f7f\u4fee\u6539\u540e\u7684DemoSubscriptionPlugin.js\u751f\u6548\uff0c\u9700\u8981\u91cd\u65b0\u542f\u52a8PostGraphile\u5bb9\u5668\u3002<\/p>\n<pre class=\"post-pre\"><code>cd ~\/work\/laradock\r\ndocker stop laradock_postgraphile_demo_1\r\ndocker-compose up -d postgres pgadmin workspace postgraphile_demo\r\n<\/code><\/pre>\n<p>\u5373\u4f7f\u5728\u8fd9\u79cd\u60c5\u51b5\u4e0b\u4f7f\u7528Altair GraphQL\u5ba2\u6237\u7aef\u5c1d\u8bd5\u542f\u52a8messageChanged\uff0c\u4e5f\u4f1a\u8fd4\u56de\u9519\u8bef\u6d88\u606f\u5e76\u65e0\u6cd5\u7b49\u5f85\u63a5\u6536\uff0c\u6211\u4eec\u786e\u8ba4\u4e86\u5b89\u5168\u6027\u5f97\u5230\u6062\u590d\u3002<\/p>\n<div><img decoding=\"async\" class=\"post-images\" title=\"\" src=\"https:\/\/cdn.silicloud.com\/blog-img\/blog\/img\/657d82d5913a08637a6b1920\/284-0.png\" alt=\"\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8 2019-06-18 17.20.17.png\" \/><\/div>\n<p>\u56de\u590d<\/p>\n<pre class=\"post-pre\"><code>{\r\n  \"errors\": [\r\n    {\r\n      \"message\": \"Cannot read property 'user_id' of null\",\r\n      \"locations\": [\r\n        {\r\n          \"line\": 23,\r\n          \"column\": 3\r\n        }\r\n      ],\r\n      \"path\": [\r\n        \"messageChanged\"\r\n      ]\r\n    }\r\n  ]\r\n}\r\n<\/code><\/pre>\n<h4>~\/\u5de5\u4f5c\/vue-apollo\/tests\/demo\/src\/components\/MockSendMessage.vue<\/h4>\n<p>\u9664\u4e86\u73b0\u6709\u7684gql\u6587\u4ef6\u5916\uff0cVue\u7ec4\u4ef6\u7684\u6e90\u4ee3\u7801\u4e2d\u8fd8\u6709\u76f4\u63a5\u7f16\u5199\u7684\u67e5\u8be2\uff0c\u8bf7\u5728\u4fee\u6539\u67e5\u8be2\u7684\u540c\u65f6\u8fdb\u884c\u5c1d\u8bd5\u3002<\/p>\n<p>\u67e5\u8be2\u529f\u80fd\uff1a\u5728general\u901a\u9053\u53d1\u5e03\u6d88\u606f\u201c\u4f60\u597d\u5417\uff1f+\u8fde\u7eed\u7f16\u53f7\u201d\u3002<\/p>\n<p>\u539f\u4ef6<\/p>\n<pre class=\"post-pre\"><code><span class=\"nt\">&lt;<\/span><span class=\"k\">script<\/span><span class=\"nt\">&gt;<\/span>\r\n<span class=\"k\">import<\/span> <span class=\"nx\">gql<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">graphql-tag<\/span><span class=\"dl\">'<\/span>\r\n\r\n<span class=\"k\">export<\/span> <span class=\"k\">default<\/span> <span class=\"p\">{<\/span>\r\n  <span class=\"nx\">created<\/span> <span class=\"p\">()<\/span> <span class=\"p\">{<\/span>\r\n    <span class=\"k\">this<\/span><span class=\"p\">.<\/span><span class=\"nx\">SEND<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">gql<\/span><span class=\"s2\">`\r\n      mutation mockMessageSend {\r\n        mockMessageSend\r\n      }\r\n    `<\/span>\r\n  <span class=\"p\">},<\/span>\r\n<span class=\"p\">}<\/span>\r\n<span class=\"nt\">&lt;\/<\/span><span class=\"k\">script<\/span><span class=\"nt\">&gt;<\/span>\r\n\r\n<span class=\"nt\">&lt;<\/span><span class=\"k\">template<\/span><span class=\"nt\">&gt;<\/span>\r\n  <span class=\"nt\">&lt;ApolloMutation<\/span>\r\n    <span class=\"na\">:mutation=<\/span><span class=\"s\">\"SEND\"<\/span>\r\n    <span class=\"na\">class=<\/span><span class=\"s\">\"mock-send-message\"<\/span>\r\n  <span class=\"nt\">&gt;<\/span>\r\n    <span class=\"nt\">&lt;a<\/span>\r\n      <span class=\"na\">slot-scope=<\/span><span class=\"s\">\"<\/span>{ mutate }\"\r\n      type=\"button\"\r\n      @click=\"mutate\"\r\n    &gt;Send bot message<span class=\"nt\">&lt;\/a&gt;<\/span>\r\n  <span class=\"nt\">&lt;\/ApolloMutation&gt;<\/span>\r\n<span class=\"nt\">&lt;\/<\/span><span class=\"k\">template<\/span><span class=\"nt\">&gt;<\/span>\r\n\r\n<span class=\"nt\">&lt;<\/span><span class=\"k\">style<\/span> <span class=\"na\">lang=<\/span><span class=\"s\">\"stylus\"<\/span> <span class=\"na\">scoped<\/span><span class=\"nt\">&gt;<\/span>\r\n.mock-send-message\r\n  margin-top 8px\r\n  display flex\r\n  align-items center\r\n  justify-content center\r\n<span class=\"nt\">&lt;\/<\/span><span class=\"k\">style<\/span><span class=\"nt\">&gt;<\/span>\r\n<\/code><\/pre>\n<p>\u6539\u53d8\u540e<\/p>\n<pre class=\"post-pre\"><code><span class=\"nt\">&lt;<\/span><span class=\"k\">script<\/span><span class=\"nt\">&gt;<\/span>\r\n<span class=\"k\">import<\/span> <span class=\"nx\">gql<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">graphql-tag<\/span><span class=\"dl\">'<\/span>\r\n\r\n<span class=\"k\">export<\/span> <span class=\"k\">default<\/span> <span class=\"p\">{<\/span>\r\n  <span class=\"nx\">created<\/span> <span class=\"p\">()<\/span> <span class=\"p\">{<\/span>\r\n    <span class=\"k\">this<\/span><span class=\"p\">.<\/span><span class=\"nx\">SEND<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">gql<\/span><span class=\"s2\">`\r\n      mutation mockMessageSend ($input: MockMessageSendInput!) {\r\n        mockMessageSend (input: $input) {\r\n          boolean\r\n        }\r\n      }\r\n    `<\/span>\r\n  <span class=\"p\">},<\/span>\r\n<span class=\"p\">}<\/span>\r\n<span class=\"nt\">&lt;\/<\/span><span class=\"k\">script<\/span><span class=\"nt\">&gt;<\/span>\r\n\r\n<span class=\"nt\">&lt;<\/span><span class=\"k\">template<\/span><span class=\"nt\">&gt;<\/span>\r\n  <span class=\"nt\">&lt;ApolloMutation<\/span>\r\n    <span class=\"na\">:mutation=<\/span><span class=\"s\">\"SEND\"<\/span>\r\n    <span class=\"na\">:variables=<\/span><span class=\"s\">\"<\/span>{\r\n      input: {\r\n      },\r\n    }\"\r\n    class=\"mock-send-message\"\r\n  &gt;\r\n    <span class=\"nt\">&lt;a<\/span>\r\n      <span class=\"na\">slot-scope=<\/span><span class=\"s\">\"<\/span>{ mutate }\"\r\n      type=\"button\"\r\n      @click=\"mutate\"\r\n    &gt;Send bot message<span class=\"nt\">&lt;\/a&gt;<\/span>\r\n  <span class=\"nt\">&lt;\/ApolloMutation&gt;<\/span>\r\n<span class=\"nt\">&lt;\/<\/span><span class=\"k\">template<\/span><span class=\"nt\">&gt;<\/span>\r\n\r\n<span class=\"nt\">&lt;<\/span><span class=\"k\">style<\/span> <span class=\"na\">lang=<\/span><span class=\"s\">\"stylus\"<\/span> <span class=\"na\">scoped<\/span><span class=\"nt\">&gt;<\/span>\r\n.mock-send-message\r\n  margin-top 8px\r\n  display flex\r\n  align-items center\r\n  justify-content center\r\n<span class=\"nt\">&lt;\/<\/span><span class=\"k\">style<\/span><span class=\"nt\">&gt;<\/span>\r\n<\/code><\/pre>\n<p>\u6211\u8981\u4eceGraphQL\u67e5\u8be2\u4e2d\u63d0\u53d6\u51fa\u6587\u672c\uff0c\u5e76\u5728VARIABLES\u4e2d\u8bbe\u5b9a\u4ee5\u4e0b\u5185\u5bb9\uff0c\u7136\u540e\u7528Altair GraphQL Client\u8fdb\u884c\u5c1d\u8bd5\u3002<\/p>\n<pre class=\"post-pre\"><code>{\r\n  \"input\": {\r\n  }\r\n}\r\n<\/code><\/pre>\n<div><img decoding=\"async\" class=\"post-images\" title=\"\" src=\"https:\/\/cdn.silicloud.com\/blog-img\/blog\/img\/657d82d5913a08637a6b1920\/296-0.png\" alt=\"\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8 2019-06-18 17.26.20.png\" \/><\/div>\n<p>\u56de\u5e94<\/p>\n<pre class=\"post-pre\"><code>{\r\n  \"data\": {\r\n    \"mockMessageSend\": {\r\n      \"boolean\": true\r\n    }\r\n  }\r\n}\r\n<\/code><\/pre>\n<p>\u6211\u4eec\u5c06\u5728\u540e\u7eed\u542f\u52a8PostGraphile\u7248ApolloChat\u65f6\u786e\u8ba4\u662f\u5426\u5728\u6b64\u6b21\u8bd5\u884c\u4e2d\u5411general\u9891\u9053\u6295\u7a3f\u4e86\u201c&#8217;\u4f60\u597d\u5417\uff1f&#8217; + \u8fde\u7eed\u7f16\u53f7\u201d\u3002<\/p>\n<p>\u901a\u8fc7\u4ee5\u4e0a\u7684\u64cd\u4f5c\uff0c\u5df2\u7ecf\u5b8c\u6210\u4e86GraphQL\u67e5\u8be2\u8bed\u53e5\u7684\u4fee\u6539\u548c\u5c1d\u8bd5\u3002<\/p>\n<h2>\u9664\u4e86\u524d\u7aef\u7684GraphQL\u67e5\u8be2\u5916\uff0c\u5176\u4ed6\u6e90\u4ee3\u7801\u8fdb\u884c\u6539\u5199<\/h2>\n<p>\u90a3\u4e48\uff0c\u9664\u4e86\u67e5\u8be2\u6587\u4e4b\u5916\u7684\u66f4\u6539\u65b9\u9762\uff0c\u6211\u4eec\u5c06\u6309\u7167\u4ee5\u4e0b\u65b9\u5f0f\u521b\u5efa\u548c\u4fee\u6539\u6587\u4ef6\u3002<\/p>\n<h3>~\/work\/vue-apollo\/tests\/demo\/vue.config.js\u53d1\u751f\u4e86\u53d8\u5316\u3002<\/h3>\n<p>\u4e3a\u4e86\u907f\u514dCORS\u9519\u8bef\uff0c\u6211\u5728devServer\u7684proxy\u90e8\u5206\u8bbe\u7f6e\u4e86\u53cd\u5411\u4ee3\u7406\uff08\u540c\u65f6\u652f\u6301WebSocket\u548chttp\uff09\u3002<\/p>\n<pre class=\"post-pre\"><code><span class=\"nx\">module<\/span><span class=\"p\">.<\/span><span class=\"nx\">exports<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span>\r\n  <span class=\"na\">pluginOptions<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n    <span class=\"na\">graphqlMock<\/span><span class=\"p\">:<\/span> <span class=\"kc\">false<\/span><span class=\"p\">,<\/span>\r\n    <span class=\"na\">apolloEngine<\/span><span class=\"p\">:<\/span> <span class=\"kc\">false<\/span><span class=\"p\">,<\/span>\r\n  <span class=\"p\">},<\/span>\r\n<span class=\"o\">+<\/span>   <span class=\"na\">devServer<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n<span class=\"o\">+<\/span>     <span class=\"na\">host<\/span><span class=\"p\">:<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">0.0.0.0<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span>\r\n<span class=\"o\">+<\/span>     <span class=\"na\">disableHostCheck<\/span><span class=\"p\">:<\/span> <span class=\"kc\">true<\/span><span class=\"p\">,<\/span>\r\n<span class=\"o\">+<\/span>     <span class=\"na\">proxy<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n<span class=\"o\">+<\/span>       <span class=\"dl\">\"<\/span><span class=\"s2\">^\/postgraphile\/demo\/ws<\/span><span class=\"dl\">\"<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n<span class=\"o\">+<\/span>         <span class=\"na\">target<\/span><span class=\"p\">:<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">http:\/\/localhost:16000<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span>\r\n<span class=\"o\">+<\/span>         <span class=\"na\">ws<\/span><span class=\"p\">:<\/span> <span class=\"kc\">true<\/span><span class=\"p\">,<\/span>\r\n<span class=\"o\">+<\/span>         <span class=\"na\">changeOrigin<\/span><span class=\"p\">:<\/span> <span class=\"kc\">true<\/span><span class=\"p\">,<\/span>\r\n<span class=\"o\">+<\/span>         <span class=\"na\">pathRewrite<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n<span class=\"o\">+<\/span>           <span class=\"dl\">\"<\/span><span class=\"s2\">^\/postgraphile\/demo\/ws<\/span><span class=\"dl\">\"<\/span><span class=\"p\">:<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">\/<\/span><span class=\"dl\">\"<\/span>\r\n<span class=\"o\">+<\/span>         <span class=\"p\">}<\/span>\r\n<span class=\"o\">+<\/span>       <span class=\"p\">},<\/span>\r\n<span class=\"o\">+<\/span>       <span class=\"dl\">\"<\/span><span class=\"s2\">^\/postgraphile\/demo<\/span><span class=\"dl\">\"<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n<span class=\"o\">+<\/span>         <span class=\"na\">target<\/span><span class=\"p\">:<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">http:\/\/localhost:16000<\/span><span class=\"dl\">\"<\/span><span class=\"p\">,<\/span>\r\n<span class=\"o\">+<\/span>         <span class=\"na\">ws<\/span><span class=\"p\">:<\/span> <span class=\"kc\">false<\/span><span class=\"p\">,<\/span>\r\n<span class=\"o\">+<\/span>         <span class=\"na\">pathRewrite<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n<span class=\"o\">+<\/span>           <span class=\"dl\">\"<\/span><span class=\"s2\">^\/postgraphile\/demo<\/span><span class=\"dl\">\"<\/span><span class=\"p\">:<\/span> <span class=\"dl\">\"<\/span><span class=\"s2\">\/<\/span><span class=\"dl\">\"<\/span>\r\n<span class=\"o\">+<\/span>         <span class=\"p\">}<\/span>\r\n<span class=\"o\">+<\/span>       <span class=\"p\">},<\/span>\r\n<span class=\"o\">+<\/span>     <span class=\"p\">}<\/span>\r\n<span class=\"o\">+<\/span>   <span class=\"p\">},<\/span>\r\n<span class=\"o\">+<\/span>   <span class=\"na\">publicPath<\/span><span class=\"p\">:<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">\/demo<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span>\r\n\r\n  <span class=\"cm\">\/* Without vue-cli-plugin-apollo 0.20.0+ *\/<\/span>\r\n  <span class=\"c1\">\/\/ chainWebpack: config =&gt; {<\/span>\r\n  <span class=\"c1\">\/\/   config.module<\/span>\r\n  <span class=\"c1\">\/\/     .rule('vue')<\/span>\r\n  <span class=\"c1\">\/\/     .use('vue-loader')<\/span>\r\n  <span class=\"c1\">\/\/       .loader('vue-loader')<\/span>\r\n  <span class=\"c1\">\/\/       .tap(options =&gt; {<\/span>\r\n  <span class=\"c1\">\/\/         options.transpileOptions = {<\/span>\r\n  <span class=\"c1\">\/\/           transforms: {<\/span>\r\n  <span class=\"c1\">\/\/             dangerousTaggedTemplateString: true,<\/span>\r\n  <span class=\"c1\">\/\/           },<\/span>\r\n  <span class=\"c1\">\/\/         }<\/span>\r\n  <span class=\"c1\">\/\/         return options<\/span>\r\n  <span class=\"c1\">\/\/       })<\/span>\r\n  <span class=\"c1\">\/\/ }<\/span>\r\n<span class=\"p\">}<\/span>\r\n<\/code><\/pre>\n<p>\u5982\u679c\u60a8\u5e0c\u671b\u5728Laradock\u7684nginx\u4e2d\u8bbe\u7f6e\u53cd\u5411\u4ee3\u7406\u800c\u4e0d\u662f\u5728vue.config.js\u4e2d\u8fdb\u884c\u8bbe\u7f6e\uff0c\u53ef\u4ee5\u5728 &#8221; ~\/work\/laradock\/nginx\/site\/default.conf &#8221; \u4e2d\u6dfb\u52a0\u4ee5\u4e0b\u8bbe\u7f6e\u3002\u5728\u542f\u52a8docker-compose\u65f6\uff0c\u5c06\u540c\u65f6\u542f\u52a8nginx\u5bb9\u5668\uff0c\u7136\u540e\u8fdb\u5165workspace\u5bb9\u5668\u5e76\u4f7f\u7528yarn serve\u542f\u52a8Web\u670d\u52a1\u5668\u3002\u7136\u540e\uff0c\u901a\u8fc7\u4e3b\u673a\u6d4f\u89c8\u5668\u8bbf\u95ee &#8221; http:\/\/localhost\/demo\/ &#8221; \u6765\u542f\u52a8PostGraphile\u7248ApolloChat\u3002<\/p>\n<pre class=\"post-pre\"><code>   location \/demo\/ {\r\n       proxy_pass http:\/\/workspace:8080\/demo\/;\r\n   }\r\n\r\n   location \/postgraphile\/demo\/ws\/ {\r\n       proxy_pass http:\/\/postgraphile_demo:16000\/;\r\n       proxy_http_version 1.1;\r\n       proxy_set_header Upgrade $http_upgrade;\r\n       proxy_set_header Connection \"upgrade\";\r\n   }\r\n\r\n   location \/postgraphile\/demo\/ {\r\n       proxy_pass http:\/\/postgraphile_demo:16000\/;\r\n   }\r\n<\/code><\/pre>\n<h3>~\/work\/vue-apollo\/tests\/demo\/.env (New) &#8211; \u5de5\u4f5c\/\u89c6\u56fe-\u963f\u6ce2\u7f57\/\u6d4b\u8bd5\/\u6f14\u793a\/.env<\/h3>\n<p>\u5185\u5bb9\uff1a\u914d\u7f6e\u8054\u63a5PostGraphile\u7aef\u70b9\u3002<\/p>\n<pre class=\"post-pre\"><code>VUE_APP_GRAPHQL_HTTP=http:\/\/localhost:8080\/postgraphile\/demo\/graphql\r\nVUE_APP_GRAPHQL_WS=ws:\/\/localhost:8080\/postgraphile\/demo\/ws\/graphql\r\n<\/code><\/pre>\n<p>\u5f15\u7528\u5185\u5bb9\u5c06\u4ece~\/work\/vue-apollo\/tests\/demo\/src\/vue-apollo.js\u5f15\u7528\u3002<br \/>\n\u4f9b\u53c2\u8003\uff0c\u5982\u679c\u8981\u5728Laradock\u7684nginx\u4e2d\u8fdb\u884c\u53cd\u5411\u4ee3\u7406\u8bbe\u7f6e\u800c\u4e0d\u662f\u5728vue.config.js\u4e2d\u8bbe\u7f6e\uff0c\u53ef\u4ee5\u5c06.env\u6587\u4ef6\u8bbe\u7f6e\u5982\u4e0b\u3002<\/p>\n<pre class=\"post-pre\"><code>VUE_APP_GRAPHQL_HTTP=http:\/\/localhost\/postgraphile\/demo\/graphql\r\nVUE_APP_GRAPHQL_WS=ws:\/\/localhost\/postgraphile\/demo\/ws\/graphql\r\n<\/code><\/pre>\n<h3>~\/work\/vue-apollo\/tests\/demo\/.graphqlconfig.yml\uff08\u6539\u53d8\uff09<\/h3>\n<p>\u6211\u4f1a\u544a\u8bc9ApolloChat\u81ea\u52a8\u751f\u6210\u7684PostGraphile GraphQL\u6a21\u5f0f\u6587\u4ef6\u7684\u4f4d\u7f6e\u3002<\/p>\n<pre class=\"post-pre\"><code><span class=\"na\">projects<\/span><span class=\"pi\">:<\/span>\r\n  <span class=\"na\">app<\/span><span class=\"pi\">:<\/span>\r\n<span class=\"pi\">-<\/span>     <span class=\"na\">schemaPath<\/span><span class=\"pi\">:<\/span> <span class=\"s\">apollo-server\/schema.graphql<\/span>\r\n<span class=\"s\">+     schemaPath<\/span><span class=\"pi\">:<\/span> <span class=\"s\">postgraphile-server\/schema.graphql<\/span>\r\n    <span class=\"s\">includes<\/span><span class=\"pi\">:<\/span>\r\n      <span class=\"pi\">-<\/span> <span class=\"s1\">'<\/span><span class=\"s\">**\/*.gql'<\/span>\r\n    <span class=\"na\">extensions<\/span><span class=\"pi\">:<\/span>\r\n      <span class=\"na\">endpoints<\/span><span class=\"pi\">:<\/span>\r\n        <span class=\"na\">default<\/span><span class=\"pi\">:<\/span> <span class=\"s1\">'<\/span><span class=\"s\">http:\/\/localhost:4000\/graphql'<\/span>\r\n<\/code><\/pre>\n<h3>~\/work\/vue-apollo\/tests\/demo\/src\/router.js (\u6539\u52a8)<\/h3>\n<p>\u4e3a\u4e86\u5728demo\u5b50\u76ee\u5f55\u4e2d\u53d1\u5e03Vue.js\u9879\u76ee\uff0c\u53ea\u9700\u8981\u6dfb\u52a0\u4e00\u884c\u4ee3\u7801\u3002<\/p>\n<pre class=\"post-pre\"><code><span class=\"k\">import<\/span> <span class=\"nx\">Vue<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">vue<\/span><span class=\"dl\">'<\/span>\r\n<span class=\"k\">import<\/span> <span class=\"nx\">Router<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">vue-router<\/span><span class=\"dl\">'<\/span>\r\n<span class=\"k\">import<\/span> <span class=\"nx\">UserLogin<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">.\/components\/UserLogin.vue<\/span><span class=\"dl\">'<\/span>\r\n<span class=\"k\">import<\/span> <span class=\"nx\">WelcomeView<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">.\/components\/WelcomeView.vue<\/span><span class=\"dl\">'<\/span>\r\n<span class=\"k\">import<\/span> <span class=\"nx\">ChannelView<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">.\/components\/ChannelView.vue<\/span><span class=\"dl\">'<\/span>\r\n\r\n<span class=\"nx\">Vue<\/span><span class=\"p\">.<\/span><span class=\"nx\">use<\/span><span class=\"p\">(<\/span><span class=\"nx\">Router<\/span><span class=\"p\">)<\/span>\r\n\r\n<span class=\"k\">export<\/span> <span class=\"k\">default<\/span> <span class=\"k\">new<\/span> <span class=\"nx\">Router<\/span><span class=\"p\">({<\/span>\r\n  <span class=\"na\">mode<\/span><span class=\"p\">:<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">history<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span>\r\n<span class=\"o\">+<\/span>   <span class=\"na\">base<\/span><span class=\"p\">:<\/span> <span class=\"nx\">process<\/span><span class=\"p\">.<\/span><span class=\"nx\">env<\/span><span class=\"p\">.<\/span><span class=\"nx\">BASE_URL<\/span><span class=\"p\">,<\/span>\r\n  <span class=\"na\">routes<\/span><span class=\"p\">:<\/span> <span class=\"p\">[<\/span>\r\n    <span class=\"p\">{<\/span>\r\n      <span class=\"na\">path<\/span><span class=\"p\">:<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">\/<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span>\r\n      <span class=\"na\">name<\/span><span class=\"p\">:<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">home<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span>\r\n      <span class=\"na\">component<\/span><span class=\"p\">:<\/span> <span class=\"nx\">WelcomeView<\/span><span class=\"p\">,<\/span>\r\n    <span class=\"p\">},<\/span>\r\n    <span class=\"p\">{<\/span>\r\n      <span class=\"na\">path<\/span><span class=\"p\">:<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">\/login<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span>\r\n      <span class=\"na\">name<\/span><span class=\"p\">:<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">login<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span>\r\n      <span class=\"na\">component<\/span><span class=\"p\">:<\/span> <span class=\"nx\">UserLogin<\/span><span class=\"p\">,<\/span>\r\n    <span class=\"p\">},<\/span>\r\n    <span class=\"p\">{<\/span>\r\n      <span class=\"na\">path<\/span><span class=\"p\">:<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">\/chan\/:id<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span>\r\n      <span class=\"na\">name<\/span><span class=\"p\">:<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">channel<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span>\r\n      <span class=\"na\">component<\/span><span class=\"p\">:<\/span> <span class=\"nx\">ChannelView<\/span><span class=\"p\">,<\/span>\r\n      <span class=\"na\">props<\/span><span class=\"p\">:<\/span> <span class=\"kc\">true<\/span><span class=\"p\">,<\/span>\r\n    <span class=\"p\">},<\/span>\r\n  <span class=\"p\">],<\/span>\r\n<span class=\"p\">})<\/span>\r\n<\/code><\/pre>\n<h3>~\/work\/vue-apollo\/tests\/demo\/src\/vue-apollo.js \uff08Modified\uff09<\/h3>\n<pre class=\"post-pre\"><code><span class=\"k\">import<\/span> <span class=\"nx\">Vue<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">vue<\/span><span class=\"dl\">'<\/span>\r\n<span class=\"k\">import<\/span> <span class=\"nx\">VueApollo<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">..\/..\/..\/<\/span><span class=\"dl\">'<\/span>\r\n<span class=\"k\">import<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">createApolloClient<\/span><span class=\"p\">,<\/span> <span class=\"nx\">restartWebsockets<\/span> <span class=\"p\">}<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">vue-cli-plugin-apollo\/graphql-client<\/span><span class=\"dl\">'<\/span>\r\n<span class=\"o\">+<\/span> <span class=\"k\">import<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">InMemoryCache<\/span> <span class=\"p\">}<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">apollo-cache-inmemory<\/span><span class=\"dl\">'<\/span>\r\n\r\n<span class=\"c1\">\/\/ Install the vue plugin<\/span>\r\n<span class=\"nx\">Vue<\/span><span class=\"p\">.<\/span><span class=\"nx\">use<\/span><span class=\"p\">(<\/span><span class=\"nx\">VueApollo<\/span><span class=\"p\">)<\/span>\r\n\r\n<span class=\"c1\">\/\/ Name of the localStorage item<\/span>\r\n<span class=\"o\">-<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">AUTH_TOKEN<\/span> <span class=\"o\">=<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">apollo-token<\/span><span class=\"dl\">'<\/span>\r\n<span class=\"o\">+<\/span> <span class=\"kd\">const<\/span> <span class=\"nx\">AUTH_TOKEN<\/span> <span class=\"o\">=<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">postgraphile-demo-token<\/span><span class=\"dl\">'<\/span>\r\n\r\n<span class=\"c1\">\/\/ Config<\/span>\r\n<span class=\"kd\">const<\/span> <span class=\"nx\">defaultOptions<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span>\r\n  <span class=\"c1\">\/\/ You can use `https` for secure connection (recommended in production)<\/span>\r\n  <span class=\"na\">httpEndpoint<\/span><span class=\"p\">:<\/span> <span class=\"nx\">process<\/span><span class=\"p\">.<\/span><span class=\"nx\">env<\/span><span class=\"p\">.<\/span><span class=\"nx\">VUE_APP_GRAPHQL_HTTP<\/span> <span class=\"o\">||<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">http:\/\/localhost:4000\/graphql<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span>\r\n  <span class=\"c1\">\/\/ You can use `wss` for secure connection (recommended in production)<\/span>\r\n  <span class=\"c1\">\/\/ Use `null` to disable subscriptions<\/span>\r\n  <span class=\"na\">wsEndpoint<\/span><span class=\"p\">:<\/span> <span class=\"nx\">process<\/span><span class=\"p\">.<\/span><span class=\"nx\">env<\/span><span class=\"p\">.<\/span><span class=\"nx\">VUE_APP_GRAPHQL_WS<\/span> <span class=\"o\">||<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">ws:\/\/localhost:4000\/graphql<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span>\r\n  <span class=\"c1\">\/\/ LocalStorage token<\/span>\r\n  <span class=\"na\">tokenName<\/span><span class=\"p\">:<\/span> <span class=\"nx\">AUTH_TOKEN<\/span><span class=\"p\">,<\/span>\r\n  <span class=\"c1\">\/\/ Enable Automatic Query persisting with Apollo Engine<\/span>\r\n  <span class=\"na\">persisting<\/span><span class=\"p\">:<\/span> <span class=\"kc\">false<\/span><span class=\"p\">,<\/span>\r\n  <span class=\"c1\">\/\/ Use websockets for everything (no HTTP)<\/span>\r\n  <span class=\"c1\">\/\/ You need to pass a `wsEndpoint` for this to work<\/span>\r\n  <span class=\"na\">websocketsOnly<\/span><span class=\"p\">:<\/span> <span class=\"kc\">false<\/span><span class=\"p\">,<\/span>\r\n  <span class=\"c1\">\/\/ Is being rendered on the server?<\/span>\r\n  <span class=\"na\">ssr<\/span><span class=\"p\">:<\/span> <span class=\"kc\">false<\/span><span class=\"p\">,<\/span>\r\n  <span class=\"c1\">\/\/ Override default http link<\/span>\r\n  <span class=\"c1\">\/\/ link: myLink,<\/span>\r\n  <span class=\"c1\">\/\/ Override default cache<\/span>\r\n  <span class=\"c1\">\/\/ cache: myCache,<\/span>\r\n<span class=\"o\">+<\/span>   <span class=\"na\">cache<\/span><span class=\"p\">:<\/span> <span class=\"k\">new<\/span> <span class=\"nx\">InMemoryCache<\/span><span class=\"p\">(),<\/span>\r\n  <span class=\"c1\">\/\/ Additional ApolloClient options<\/span>\r\n  <span class=\"c1\">\/\/ apollo: { ... }<\/span>\r\n  <span class=\"na\">getAuth<\/span><span class=\"p\">:<\/span> <span class=\"nx\">tokenName<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\r\n    <span class=\"c1\">\/\/ get the authentication token from local storage if it exists<\/span>\r\n    <span class=\"kd\">const<\/span> <span class=\"nx\">token<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">localStorage<\/span><span class=\"p\">.<\/span><span class=\"nx\">getItem<\/span><span class=\"p\">(<\/span><span class=\"nx\">tokenName<\/span><span class=\"p\">)<\/span>\r\n    <span class=\"c1\">\/\/ return the headers to the context so httpLink can read them<\/span>\r\n<span class=\"o\">-<\/span>     <span class=\"k\">return<\/span> <span class=\"nx\">token<\/span> <span class=\"o\">||<\/span> <span class=\"dl\">''<\/span>\r\n<span class=\"o\">+<\/span>     <span class=\"k\">return<\/span> <span class=\"nx\">token<\/span> <span class=\"p\">?<\/span> <span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">Bearer <\/span><span class=\"dl\">'<\/span> <span class=\"o\">+<\/span> <span class=\"nx\">token<\/span><span class=\"p\">)<\/span> <span class=\"p\">:<\/span> <span class=\"dl\">''<\/span>\r\n  <span class=\"p\">},<\/span>\r\n<span class=\"p\">}<\/span>\r\n\r\n<span class=\"c1\">\/\/ Call this in the Vue app file<\/span>\r\n<span class=\"k\">export<\/span> <span class=\"kd\">function<\/span> <span class=\"nx\">createProvider<\/span> <span class=\"p\">(<\/span><span class=\"nx\">options<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{},<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">router<\/span> <span class=\"p\">})<\/span> <span class=\"p\">{<\/span>\r\n  <span class=\"c1\">\/\/ Create apollo client<\/span>\r\n  <span class=\"kd\">const<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">apolloClient<\/span><span class=\"p\">,<\/span> <span class=\"nx\">wsClient<\/span> <span class=\"p\">}<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">createApolloClient<\/span><span class=\"p\">({<\/span>\r\n    <span class=\"p\">...<\/span><span class=\"nx\">defaultOptions<\/span><span class=\"p\">,<\/span>\r\n    <span class=\"p\">...<\/span><span class=\"nx\">options<\/span><span class=\"p\">,<\/span>\r\n  <span class=\"p\">})<\/span>\r\n  <span class=\"nx\">apolloClient<\/span><span class=\"p\">.<\/span><span class=\"nx\">wsClient<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">wsClient<\/span>\r\n\r\n  <span class=\"c1\">\/\/ Create vue apollo provider<\/span>\r\n  <span class=\"kd\">const<\/span> <span class=\"nx\">apolloProvider<\/span> <span class=\"o\">=<\/span> <span class=\"k\">new<\/span> <span class=\"nx\">VueApollo<\/span><span class=\"p\">({<\/span>\r\n    <span class=\"na\">defaultClient<\/span><span class=\"p\">:<\/span> <span class=\"nx\">apolloClient<\/span><span class=\"p\">,<\/span>\r\n    <span class=\"na\">defaultOptions<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n      <span class=\"na\">$query<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n        <span class=\"na\">fetchPolicy<\/span><span class=\"p\">:<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">cache-and-network<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span>\r\n      <span class=\"p\">},<\/span>\r\n    <span class=\"p\">},<\/span>\r\n    <span class=\"nx\">errorHandler<\/span> <span class=\"p\">(<\/span><span class=\"nx\">error<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\r\n      <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"nx\">isUnauthorizedError<\/span><span class=\"p\">(<\/span><span class=\"nx\">error<\/span><span class=\"p\">))<\/span> <span class=\"p\">{<\/span>\r\n        <span class=\"c1\">\/\/ Redirect to login page<\/span>\r\n        <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"nx\">router<\/span><span class=\"p\">.<\/span><span class=\"nx\">currentRoute<\/span><span class=\"p\">.<\/span><span class=\"nx\">name<\/span> <span class=\"o\">!==<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">login<\/span><span class=\"dl\">'<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\r\n          <span class=\"nx\">router<\/span><span class=\"p\">.<\/span><span class=\"nx\">replace<\/span><span class=\"p\">({<\/span>\r\n            <span class=\"na\">name<\/span><span class=\"p\">:<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">login<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span>\r\n            <span class=\"na\">params<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n              <span class=\"na\">wantedRoute<\/span><span class=\"p\">:<\/span> <span class=\"nx\">router<\/span><span class=\"p\">.<\/span><span class=\"nx\">currentRoute<\/span><span class=\"p\">.<\/span><span class=\"nx\">fullPath<\/span><span class=\"p\">,<\/span>\r\n            <span class=\"p\">},<\/span>\r\n          <span class=\"p\">})<\/span>\r\n        <span class=\"p\">}<\/span>\r\n      <span class=\"p\">}<\/span> <span class=\"k\">else<\/span> <span class=\"p\">{<\/span>\r\n        <span class=\"nx\">console<\/span><span class=\"p\">.<\/span><span class=\"nx\">log<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">%cError<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">background: red; color: white; padding: 2px 4px; border-radius: 3px; font-weight: bold;<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span> <span class=\"nx\">error<\/span><span class=\"p\">.<\/span><span class=\"nx\">message<\/span><span class=\"p\">)<\/span>\r\n      <span class=\"p\">}<\/span>\r\n    <span class=\"p\">},<\/span>\r\n  <span class=\"p\">})<\/span>\r\n\r\n  <span class=\"k\">return<\/span> <span class=\"nx\">apolloProvider<\/span>\r\n<span class=\"p\">}<\/span>\r\n\r\n<span class=\"c1\">\/\/ Manually call this when user log in<\/span>\r\n<span class=\"k\">export<\/span> <span class=\"k\">async<\/span> <span class=\"kd\">function<\/span> <span class=\"nx\">onLogin<\/span> <span class=\"p\">(<\/span><span class=\"nx\">apolloClient<\/span><span class=\"p\">,<\/span> <span class=\"nx\">token<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\r\n<span class=\"o\">-<\/span>   <span class=\"nx\">localStorage<\/span><span class=\"p\">.<\/span><span class=\"nx\">setItem<\/span><span class=\"p\">(<\/span><span class=\"nx\">AUTH_TOKEN<\/span><span class=\"p\">,<\/span> <span class=\"nx\">JSON<\/span><span class=\"p\">.<\/span><span class=\"nx\">stringify<\/span><span class=\"p\">(<\/span><span class=\"nx\">token<\/span><span class=\"p\">))<\/span>\r\n<span class=\"o\">+<\/span>   <span class=\"nx\">localStorage<\/span><span class=\"p\">.<\/span><span class=\"nx\">setItem<\/span><span class=\"p\">(<\/span><span class=\"nx\">AUTH_TOKEN<\/span><span class=\"p\">,<\/span> <span class=\"nx\">token<\/span><span class=\"p\">)<\/span>\r\n  <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"nx\">apolloClient<\/span><span class=\"p\">.<\/span><span class=\"nx\">wsClient<\/span><span class=\"p\">)<\/span> <span class=\"nx\">restartWebsockets<\/span><span class=\"p\">(<\/span><span class=\"nx\">apolloClient<\/span><span class=\"p\">.<\/span><span class=\"nx\">wsClient<\/span><span class=\"p\">)<\/span>\r\n  <span class=\"k\">try<\/span> <span class=\"p\">{<\/span>\r\n    <span class=\"k\">await<\/span> <span class=\"nx\">apolloClient<\/span><span class=\"p\">.<\/span><span class=\"nx\">resetStore<\/span><span class=\"p\">()<\/span>\r\n  <span class=\"p\">}<\/span> <span class=\"k\">catch<\/span> <span class=\"p\">(<\/span><span class=\"nx\">e<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\r\n    <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"o\">!<\/span><span class=\"nx\">isUnauthorizedError<\/span><span class=\"p\">(<\/span><span class=\"nx\">e<\/span><span class=\"p\">))<\/span> <span class=\"p\">{<\/span>\r\n      <span class=\"nx\">console<\/span><span class=\"p\">.<\/span><span class=\"nx\">log<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">%cError on cache reset (login)<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">color: orange;<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span> <span class=\"nx\">e<\/span><span class=\"p\">.<\/span><span class=\"nx\">message<\/span><span class=\"p\">)<\/span>\r\n    <span class=\"p\">}<\/span>\r\n  <span class=\"p\">}<\/span>\r\n<span class=\"p\">}<\/span>\r\n\r\n<span class=\"c1\">\/\/ Manually call this when user log out<\/span>\r\n<span class=\"k\">export<\/span> <span class=\"k\">async<\/span> <span class=\"kd\">function<\/span> <span class=\"nx\">onLogout<\/span> <span class=\"p\">(<\/span><span class=\"nx\">apolloClient<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\r\n  <span class=\"nx\">localStorage<\/span><span class=\"p\">.<\/span><span class=\"nx\">removeItem<\/span><span class=\"p\">(<\/span><span class=\"nx\">AUTH_TOKEN<\/span><span class=\"p\">)<\/span>\r\n  <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"nx\">apolloClient<\/span><span class=\"p\">.<\/span><span class=\"nx\">wsClient<\/span><span class=\"p\">)<\/span> <span class=\"nx\">restartWebsockets<\/span><span class=\"p\">(<\/span><span class=\"nx\">apolloClient<\/span><span class=\"p\">.<\/span><span class=\"nx\">wsClient<\/span><span class=\"p\">)<\/span>\r\n  <span class=\"k\">try<\/span> <span class=\"p\">{<\/span>\r\n    <span class=\"k\">await<\/span> <span class=\"nx\">apolloClient<\/span><span class=\"p\">.<\/span><span class=\"nx\">resetStore<\/span><span class=\"p\">()<\/span>\r\n  <span class=\"p\">}<\/span> <span class=\"k\">catch<\/span> <span class=\"p\">(<\/span><span class=\"nx\">e<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\r\n    <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"o\">!<\/span><span class=\"nx\">isUnauthorizedError<\/span><span class=\"p\">(<\/span><span class=\"nx\">e<\/span><span class=\"p\">))<\/span> <span class=\"p\">{<\/span>\r\n      <span class=\"nx\">console<\/span><span class=\"p\">.<\/span><span class=\"nx\">log<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">%cError on cache reset (logout)<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">color: orange;<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span> <span class=\"nx\">e<\/span><span class=\"p\">.<\/span><span class=\"nx\">message<\/span><span class=\"p\">)<\/span>\r\n    <span class=\"p\">}<\/span>\r\n  <span class=\"p\">}<\/span>\r\n<span class=\"p\">}<\/span>\r\n\r\n<span class=\"kd\">function<\/span> <span class=\"nx\">isUnauthorizedError<\/span> <span class=\"p\">(<\/span><span class=\"nx\">error<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\r\n<span class=\"o\">-<\/span>   <span class=\"kd\">const<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">graphQLErrors<\/span> <span class=\"p\">}<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">error<\/span>\r\n<span class=\"o\">-<\/span>   <span class=\"k\">return<\/span> <span class=\"p\">(<\/span><span class=\"nx\">graphQLErrors<\/span> <span class=\"o\">&amp;&amp;<\/span> <span class=\"nx\">graphQLErrors<\/span><span class=\"p\">.<\/span><span class=\"nx\">some<\/span><span class=\"p\">(<\/span><span class=\"nx\">e<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"nx\">e<\/span><span class=\"p\">.<\/span><span class=\"nx\">message<\/span> <span class=\"o\">===<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">Unauthorized<\/span><span class=\"dl\">'<\/span><span class=\"p\">))<\/span>\r\n<span class=\"o\">+<\/span>   <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"nx\">error<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\r\n<span class=\"o\">+<\/span>     <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"nx\">error<\/span><span class=\"p\">.<\/span><span class=\"nx\">message<\/span><span class=\"p\">.<\/span><span class=\"nx\">indexOf<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">Unauthorized<\/span><span class=\"dl\">'<\/span><span class=\"p\">)<\/span> <span class=\"o\">&gt;=<\/span> <span class=\"mi\">0<\/span> <span class=\"o\">||<\/span> <span class=\"nx\">error<\/span><span class=\"p\">.<\/span><span class=\"nx\">message<\/span><span class=\"p\">.<\/span><span class=\"nx\">indexOf<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">permission denied<\/span><span class=\"dl\">'<\/span><span class=\"p\">)<\/span> <span class=\"o\">&gt;=<\/span> <span class=\"mi\">0<\/span> <span class=\"o\">||<\/span> <span class=\"nx\">error<\/span><span class=\"p\">.<\/span><span class=\"nx\">message<\/span><span class=\"p\">.<\/span><span class=\"nx\">indexOf<\/span><span class=\"p\">(<\/span><span class=\"dl\">'<\/span><span class=\"s1\">status code 401<\/span><span class=\"dl\">'<\/span><span class=\"p\">)<\/span> <span class=\"o\">&gt;=<\/span> <span class=\"mi\">0<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\r\n<span class=\"o\">+<\/span>       <span class=\"k\">return<\/span> <span class=\"kc\">true<\/span>\r\n<span class=\"o\">+<\/span>     <span class=\"p\">}<\/span>\r\n<span class=\"o\">+<\/span>   <span class=\"p\">}<\/span>\r\n<span class=\"o\">+<\/span>   <span class=\"k\">return<\/span> <span class=\"kc\">false<\/span>\r\n<span class=\"p\">}<\/span>\r\n<\/code><\/pre>\n<h3>~\/work\/vue-apollo\/tests\/demo\/src\/components\/UserLogin.vue (\u4fee\u6539)<\/h3>\n<pre class=\"post-pre\"><code><span class=\"nt\">&lt;<\/span><span class=\"k\">script<\/span><span class=\"nt\">&gt;<\/span>\r\n<span class=\"k\">import<\/span> <span class=\"nx\">UserCurrent<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">..\/mixins\/UserCurrent<\/span><span class=\"dl\">'<\/span>\r\n<span class=\"k\">import<\/span> <span class=\"nx\">USER_CURRENT<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">..\/graphql\/userCurrent.gql<\/span><span class=\"dl\">'<\/span>\r\n<span class=\"k\">import<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">onLogin<\/span> <span class=\"p\">}<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">..\/vue-apollo<\/span><span class=\"dl\">'<\/span>\r\n\r\n<span class=\"k\">export<\/span> <span class=\"k\">default<\/span> <span class=\"p\">{<\/span>\r\n  <span class=\"na\">name<\/span><span class=\"p\">:<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">UserLogin<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span>\r\n\r\n  <span class=\"na\">mixins<\/span><span class=\"p\">:<\/span> <span class=\"p\">[<\/span>\r\n    <span class=\"nx\">UserCurrent<\/span><span class=\"p\">,<\/span>\r\n  <span class=\"p\">],<\/span>\r\n\r\n  <span class=\"nx\">data<\/span> <span class=\"p\">()<\/span> <span class=\"p\">{<\/span>\r\n    <span class=\"k\">return<\/span> <span class=\"p\">{<\/span>\r\n      <span class=\"na\">showRegister<\/span><span class=\"p\">:<\/span> <span class=\"kc\">false<\/span><span class=\"p\">,<\/span>\r\n      <span class=\"na\">email<\/span><span class=\"p\">:<\/span> <span class=\"dl\">''<\/span><span class=\"p\">,<\/span>\r\n      <span class=\"na\">password<\/span><span class=\"p\">:<\/span> <span class=\"dl\">''<\/span><span class=\"p\">,<\/span>\r\n      <span class=\"na\">nickname<\/span><span class=\"p\">:<\/span> <span class=\"dl\">''<\/span><span class=\"p\">,<\/span>\r\n    <span class=\"p\">}<\/span>\r\n  <span class=\"p\">},<\/span>\r\n\r\n  <span class=\"na\">watch<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n    <span class=\"c1\">\/\/ If already logged in redirect to other page<\/span>\r\n    <span class=\"nx\">userCurrent<\/span> <span class=\"p\">(<\/span><span class=\"nx\">value<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\r\n      <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"nx\">value<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\r\n        <span class=\"k\">this<\/span><span class=\"p\">.<\/span><span class=\"nx\">redirect<\/span><span class=\"p\">()<\/span>\r\n      <span class=\"p\">}<\/span>\r\n    <span class=\"p\">},<\/span>\r\n  <span class=\"p\">},<\/span>\r\n\r\n  <span class=\"na\">methods<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n    <span class=\"k\">async<\/span> <span class=\"nx\">onDone<\/span> <span class=\"p\">(<\/span><span class=\"nx\">result<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\r\n      <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"k\">this<\/span><span class=\"p\">.<\/span><span class=\"nx\">showRegister<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\r\n        <span class=\"k\">this<\/span><span class=\"p\">.<\/span><span class=\"nx\">showRegister<\/span> <span class=\"o\">=<\/span> <span class=\"kc\">false<\/span>\r\n      <span class=\"p\">}<\/span> <span class=\"k\">else<\/span> <span class=\"p\">{<\/span>\r\n        <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"o\">!<\/span><span class=\"nx\">result<\/span><span class=\"p\">.<\/span><span class=\"nx\">data<\/span><span class=\"p\">.<\/span><span class=\"nx\">userLogin<\/span><span class=\"p\">)<\/span> <span class=\"k\">return<\/span>\r\n        <span class=\"kd\">const<\/span> <span class=\"nx\">apolloClient<\/span> <span class=\"o\">=<\/span> <span class=\"k\">this<\/span><span class=\"p\">.<\/span><span class=\"nx\">$apollo<\/span><span class=\"p\">.<\/span><span class=\"nx\">provider<\/span><span class=\"p\">.<\/span><span class=\"nx\">defaultClient<\/span>\r\n        <span class=\"c1\">\/\/ Update token and reset cache<\/span>\r\n<span class=\"o\">-<\/span>         <span class=\"kd\">const<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">id<\/span><span class=\"p\">,<\/span> <span class=\"nx\">userId<\/span><span class=\"p\">,<\/span> <span class=\"nx\">expiration<\/span> <span class=\"p\">}<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">result<\/span><span class=\"p\">.<\/span><span class=\"nx\">data<\/span><span class=\"p\">.<\/span><span class=\"nx\">userLogin<\/span><span class=\"p\">.<\/span><span class=\"nx\">token<\/span>\r\n<span class=\"o\">-<\/span>         <span class=\"k\">await<\/span> <span class=\"nx\">onLogin<\/span><span class=\"p\">(<\/span><span class=\"nx\">apolloClient<\/span><span class=\"p\">,<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">id<\/span><span class=\"p\">,<\/span> <span class=\"nx\">userId<\/span><span class=\"p\">,<\/span> <span class=\"nx\">expiration<\/span> <span class=\"p\">})<\/span>\r\n<span class=\"o\">+<\/span>         <span class=\"k\">await<\/span> <span class=\"nx\">onLogin<\/span><span class=\"p\">(<\/span><span class=\"nx\">apolloClient<\/span><span class=\"p\">,<\/span> <span class=\"nx\">result<\/span><span class=\"p\">.<\/span><span class=\"nx\">data<\/span><span class=\"p\">.<\/span><span class=\"nx\">userLogin<\/span><span class=\"p\">.<\/span><span class=\"nx\">usrAndToken<\/span><span class=\"p\">.<\/span><span class=\"nx\">token<\/span><span class=\"p\">)<\/span>\r\n        <span class=\"c1\">\/\/ Update cache<\/span>\r\n        <span class=\"nx\">apolloClient<\/span><span class=\"p\">.<\/span><span class=\"nx\">writeQuery<\/span><span class=\"p\">({<\/span>\r\n          <span class=\"na\">query<\/span><span class=\"p\">:<\/span> <span class=\"nx\">USER_CURRENT<\/span><span class=\"p\">,<\/span>\r\n          <span class=\"na\">data<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n<span class=\"o\">-<\/span>             <span class=\"na\">userCurrent<\/span><span class=\"p\">:<\/span> <span class=\"nx\">result<\/span><span class=\"p\">.<\/span><span class=\"nx\">data<\/span><span class=\"p\">.<\/span><span class=\"nx\">userLogin<\/span><span class=\"p\">.<\/span><span class=\"nx\">user<\/span><span class=\"p\">,<\/span>\r\n<span class=\"o\">+<\/span>             <span class=\"na\">userCurrent<\/span><span class=\"p\">:<\/span> <span class=\"nx\">result<\/span><span class=\"p\">.<\/span><span class=\"nx\">data<\/span><span class=\"p\">.<\/span><span class=\"nx\">userLogin<\/span><span class=\"p\">.<\/span><span class=\"nx\">usrAndToken<\/span><span class=\"p\">.<\/span><span class=\"nx\">usr<\/span><span class=\"p\">,<\/span>\r\n          <span class=\"p\">},<\/span>\r\n        <span class=\"p\">})<\/span>\r\n      <span class=\"p\">}<\/span>\r\n    <span class=\"p\">},<\/span>\r\n\r\n    <span class=\"nx\">redirect<\/span> <span class=\"p\">()<\/span> <span class=\"p\">{<\/span>\r\n      <span class=\"k\">this<\/span><span class=\"p\">.<\/span><span class=\"nx\">$router<\/span><span class=\"p\">.<\/span><span class=\"nx\">replace<\/span><span class=\"p\">(<\/span><span class=\"k\">this<\/span><span class=\"p\">.<\/span><span class=\"nx\">$route<\/span><span class=\"p\">.<\/span><span class=\"nx\">params<\/span><span class=\"p\">.<\/span><span class=\"nx\">wantedRoute<\/span> <span class=\"o\">||<\/span> <span class=\"p\">{<\/span> <span class=\"na\">name<\/span><span class=\"p\">:<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">home<\/span><span class=\"dl\">'<\/span> <span class=\"p\">})<\/span>\r\n    <span class=\"p\">},<\/span>\r\n  <span class=\"p\">},<\/span>\r\n<span class=\"p\">}<\/span>\r\n<span class=\"nt\">&lt;\/<\/span><span class=\"k\">script<\/span><span class=\"nt\">&gt;<\/span>\r\n\r\n<span class=\"nt\">&lt;<\/span><span class=\"k\">template<\/span><span class=\"nt\">&gt;<\/span>\r\n  <span class=\"nt\">&lt;div<\/span> <span class=\"na\">class=<\/span><span class=\"s\">\"user-login\"<\/span><span class=\"nt\">&gt;<\/span>\r\n    <span class=\"nt\">&lt;div<\/span> <span class=\"na\">class=<\/span><span class=\"s\">\"logo\"<\/span><span class=\"nt\">&gt;<\/span>\r\n      <span class=\"nt\">&lt;i<\/span> <span class=\"na\">class=<\/span><span class=\"s\">\"material-icons icon\"<\/span><span class=\"nt\">&gt;<\/span>chat<span class=\"nt\">&lt;\/i&gt;<\/span>\r\n    <span class=\"nt\">&lt;\/div&gt;<\/span>\r\n    <span class=\"nt\">&lt;div<\/span> <span class=\"na\">class=<\/span><span class=\"s\">\"app-name\"<\/span><span class=\"nt\">&gt;<\/span>\r\n      Apollo<span class=\"nt\">&lt;b&gt;<\/span>Chat<span class=\"nt\">&lt;\/b&gt;<\/span>\r\n    <span class=\"nt\">&lt;\/div&gt;<\/span>\r\n    <span class=\"nt\">&lt;ApolloMutation<\/span>\r\n      <span class=\"na\">:mutation=<\/span><span class=\"s\">\"showRegister\r\n        ? require('..\/graphql\/userRegister.gql')\r\n        : require('..\/graphql\/userLogin.gql')\"<\/span>\r\n      <span class=\"na\">:variables=<\/span><span class=\"s\">\"showRegister\r\n        ? <\/span>{\r\n          input: {\r\n            email,\r\n            password,\r\n            nickname,\r\n          },\r\n        }\r\n        : {\r\n-           email,\r\n-           password,\r\n+           input: {\r\n+             email,\r\n+             password,\r\n+           },\r\n        }\"\r\n      class=\"wrapper\"\r\n      @done=\"onDone\"\r\n    &gt;\r\n      <span class=\"nt\">&lt;form<\/span>\r\n        <span class=\"na\">slot-scope=<\/span><span class=\"s\">\"<\/span>{ mutate, loading, gqlError: error }\"\r\n        :key=\"showRegister\"\r\n        class=\"form\"\r\n        @submit.prevent=\"mutate()\"\r\n      &gt;\r\n        <span class=\"nt\">&lt;input<\/span>\r\n          <span class=\"na\">v-model=<\/span><span class=\"s\">\"email\"<\/span>\r\n          <span class=\"na\">class=<\/span><span class=\"s\">\"form-input\"<\/span>\r\n          <span class=\"na\">type=<\/span><span class=\"s\">\"email\"<\/span>\r\n          <span class=\"na\">name=<\/span><span class=\"s\">\"email\"<\/span>\r\n          <span class=\"na\">placeholder=<\/span><span class=\"s\">\"Email\"<\/span>\r\n          <span class=\"na\">required<\/span>\r\n        <span class=\"nt\">&gt;<\/span>\r\n        <span class=\"nt\">&lt;input<\/span>\r\n          <span class=\"na\">v-model=<\/span><span class=\"s\">\"password\"<\/span>\r\n          <span class=\"na\">class=<\/span><span class=\"s\">\"form-input\"<\/span>\r\n          <span class=\"na\">type=<\/span><span class=\"s\">\"password\"<\/span>\r\n          <span class=\"na\">name=<\/span><span class=\"s\">\"password\"<\/span>\r\n          <span class=\"na\">placeholder=<\/span><span class=\"s\">\"Password\"<\/span>\r\n          <span class=\"na\">required<\/span>\r\n        <span class=\"nt\">&gt;<\/span>\r\n        <span class=\"nt\">&lt;input<\/span>\r\n          <span class=\"na\">v-if=<\/span><span class=\"s\">\"showRegister\"<\/span>\r\n          <span class=\"na\">v-model=<\/span><span class=\"s\">\"nickname\"<\/span>\r\n          <span class=\"na\">class=<\/span><span class=\"s\">\"form-input\"<\/span>\r\n          <span class=\"na\">name=<\/span><span class=\"s\">\"nickname\"<\/span>\r\n          <span class=\"na\">placeholder=<\/span><span class=\"s\">\"Nickname\"<\/span>\r\n          <span class=\"na\">required<\/span>\r\n        <span class=\"nt\">&gt;<\/span>\r\n        <span class=\"nt\">&lt;div<\/span> <span class=\"na\">v-if=<\/span><span class=\"s\">\"error\"<\/span> <span class=\"na\">class=<\/span><span class=\"s\">\"error\"<\/span><span class=\"nt\">&gt;<\/span><span class=\"si\">{{<\/span> <span class=\"nx\">error<\/span><span class=\"p\">.<\/span><span class=\"nx\">message<\/span> <span class=\"si\">}}<\/span><span class=\"nt\">&lt;\/div&gt;<\/span>\r\n        <span class=\"nt\">&lt;template<\/span> <span class=\"na\">v-if=<\/span><span class=\"s\">\"!showRegister\"<\/span><span class=\"nt\">&gt;<\/span>\r\n          <span class=\"nt\">&lt;button<\/span>\r\n            <span class=\"na\">type=<\/span><span class=\"s\">\"submit\"<\/span>\r\n            <span class=\"na\">:disabled=<\/span><span class=\"s\">\"loading\"<\/span>\r\n            <span class=\"na\">class=<\/span><span class=\"s\">\"button\"<\/span>\r\n            <span class=\"na\">data-id=<\/span><span class=\"s\">\"login\"<\/span>\r\n          <span class=\"nt\">&gt;<\/span>Login<span class=\"nt\">&lt;\/button&gt;<\/span>\r\n          <span class=\"nt\">&lt;div<\/span> <span class=\"na\">class=<\/span><span class=\"s\">\"actions\"<\/span><span class=\"nt\">&gt;<\/span>\r\n            <span class=\"nt\">&lt;a<\/span>\r\n              <span class=\"na\">data-id=<\/span><span class=\"s\">\"create-account\"<\/span>\r\n              <span class=\"err\">@<\/span><span class=\"na\">click=<\/span><span class=\"s\">\"showRegister = true\"<\/span>\r\n            <span class=\"nt\">&gt;<\/span>Create an account<span class=\"nt\">&lt;\/a&gt;<\/span>\r\n          <span class=\"nt\">&lt;\/div&gt;<\/span>\r\n        <span class=\"nt\">&lt;\/<\/span><span class=\"k\">template<\/span><span class=\"nt\">&gt;<\/span>\r\n        <span class=\"nt\">&lt;<\/span><span class=\"k\">template<\/span> <span class=\"na\">v-else<\/span><span class=\"nt\">&gt;<\/span>\r\n          <span class=\"nt\">&lt;button<\/span>\r\n            <span class=\"na\">type=<\/span><span class=\"s\">\"submit\"<\/span>\r\n            <span class=\"na\">:disabled=<\/span><span class=\"s\">\"loading\"<\/span>\r\n            <span class=\"na\">class=<\/span><span class=\"s\">\"button\"<\/span>\r\n            <span class=\"na\">data-id=<\/span><span class=\"s\">\"submit-new-account\"<\/span>\r\n          <span class=\"nt\">&gt;<\/span>Create new account<span class=\"nt\">&lt;\/button&gt;<\/span>\r\n          <span class=\"nt\">&lt;div<\/span> <span class=\"na\">class=<\/span><span class=\"s\">\"actions\"<\/span><span class=\"nt\">&gt;<\/span>\r\n            <span class=\"nt\">&lt;a<\/span> <span class=\"err\">@<\/span><span class=\"na\">click=<\/span><span class=\"s\">\"showRegister = false\"<\/span><span class=\"nt\">&gt;<\/span>Go back<span class=\"nt\">&lt;\/a&gt;<\/span>\r\n          <span class=\"nt\">&lt;\/div&gt;<\/span>\r\n        <span class=\"nt\">&lt;\/<\/span><span class=\"k\">template<\/span><span class=\"nt\">&gt;<\/span>\r\n      <span class=\"nt\">&lt;\/form&gt;<\/span>\r\n    <span class=\"nt\">&lt;\/ApolloMutation&gt;<\/span>\r\n  <span class=\"nt\">&lt;\/div&gt;<\/span>\r\n<span class=\"nt\">&lt;\/template&gt;<\/span>\r\n\r\n<span class=\"nt\">&lt;<\/span><span class=\"k\">style<\/span> <span class=\"na\">lang=<\/span><span class=\"s\">\"stylus\"<\/span> <span class=\"na\">scoped<\/span><span class=\"nt\">&gt;<\/span>\r\n@import '~@\/style\/imports'\r\n\r\n.user-login\r\n  height 100%\r\n  display flex\r\n  flex-direction column\r\n  align-items center\r\n  justify-content center\r\n\r\n.logo\r\n  .icon\r\n    font-size 80px\r\n    color $color\r\n\r\n.app-name\r\n  font-size 42px\r\n  font-weight lighter\r\n  margin-bottom 32px\r\n\r\n.wrapper\r\n  flex auto 0 0\r\n\r\n.form\r\n  width 100vw\r\n  max-width 300px\r\n\r\n.form-input,\r\n.button\r\n  display block\r\n  width 100%\r\n  box-sizing border-box\r\n\r\n.form-input\r\n  margin-bottom 12px\r\n\r\n.actions\r\n  margin-top 12px\r\n  text-align center\r\n  font-size 12px\r\n<span class=\"nt\">&lt;\/<\/span><span class=\"k\">style<\/span><span class=\"nt\">&gt;<\/span>\r\n<\/code><\/pre>\n<h3>~\/work\/vue-apollo\/tests\/demo\/src\/components\/UserCurrent.vue\uff08\u88ab\u66f4\u6539\uff09<\/h3>\n<pre class=\"post-pre\"><code><span class=\"nt\">&lt;<\/span><span class=\"k\">script<\/span><span class=\"nt\">&gt;<\/span>\r\n<span class=\"k\">import<\/span> <span class=\"nx\">UserCurrent<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">..\/mixins\/UserCurrent<\/span><span class=\"dl\">'<\/span>\r\n<span class=\"k\">import<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">onLogout<\/span> <span class=\"p\">}<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">..\/vue-apollo<\/span><span class=\"dl\">'<\/span>\r\n<span class=\"k\">import<\/span> <span class=\"nx\">USER_LOGOUT<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">..\/graphql\/userLogout.gql<\/span><span class=\"dl\">'<\/span>\r\n\r\n<span class=\"k\">export<\/span> <span class=\"k\">default<\/span> <span class=\"p\">{<\/span>\r\n  <span class=\"na\">name<\/span><span class=\"p\">:<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">MessageForm<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span>\r\n\r\n  <span class=\"na\">mixins<\/span><span class=\"p\">:<\/span> <span class=\"p\">[<\/span>\r\n    <span class=\"nx\">UserCurrent<\/span><span class=\"p\">,<\/span>\r\n  <span class=\"p\">],<\/span>\r\n\r\n  <span class=\"na\">methods<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n    <span class=\"k\">async<\/span> <span class=\"nx\">logout<\/span> <span class=\"p\">()<\/span> <span class=\"p\">{<\/span>\r\n<span class=\"o\">+<\/span>       <span class=\"kd\">const<\/span> <span class=\"nx\">apolloClient<\/span> <span class=\"o\">=<\/span> <span class=\"k\">this<\/span><span class=\"p\">.<\/span><span class=\"nx\">$apollo<\/span><span class=\"p\">.<\/span><span class=\"nx\">provider<\/span><span class=\"p\">.<\/span><span class=\"nx\">defaultClient<\/span>\r\n<span class=\"o\">+<\/span>       <span class=\"nx\">onLogout<\/span><span class=\"p\">(<\/span><span class=\"nx\">apolloClient<\/span><span class=\"p\">)<\/span>\r\n      <span class=\"k\">await<\/span> <span class=\"k\">this<\/span><span class=\"p\">.<\/span><span class=\"nx\">$apollo<\/span><span class=\"p\">.<\/span><span class=\"nx\">mutate<\/span><span class=\"p\">({<\/span>\r\n        <span class=\"na\">mutation<\/span><span class=\"p\">:<\/span> <span class=\"nx\">USER_LOGOUT<\/span><span class=\"p\">,<\/span>\r\n<span class=\"o\">+<\/span>         <span class=\"na\">variables<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n<span class=\"o\">+<\/span>           <span class=\"na\">input<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n<span class=\"o\">+<\/span>           <span class=\"p\">},<\/span>\r\n<span class=\"o\">+<\/span>         <span class=\"p\">},<\/span>\r\n      <span class=\"p\">})<\/span>\r\n<span class=\"o\">-<\/span>       <span class=\"kd\">const<\/span> <span class=\"nx\">apolloClient<\/span> <span class=\"o\">=<\/span> <span class=\"k\">this<\/span><span class=\"p\">.<\/span><span class=\"nx\">$apollo<\/span><span class=\"p\">.<\/span><span class=\"nx\">provider<\/span><span class=\"p\">.<\/span><span class=\"nx\">defaultClient<\/span>\r\n<span class=\"o\">-<\/span>       <span class=\"nx\">onLogout<\/span><span class=\"p\">(<\/span><span class=\"nx\">apolloClient<\/span><span class=\"p\">)<\/span>\r\n    <span class=\"p\">},<\/span>\r\n  <span class=\"p\">},<\/span>\r\n<span class=\"p\">}<\/span>\r\n<span class=\"nt\">&lt;\/<\/span><span class=\"k\">script<\/span><span class=\"nt\">&gt;<\/span>\r\n\r\n<span class=\"nt\">&lt;<\/span><span class=\"k\">template<\/span><span class=\"nt\">&gt;<\/span>\r\n  <span class=\"nt\">&lt;div<\/span> <span class=\"na\">class=<\/span><span class=\"s\">\"user-current\"<\/span><span class=\"nt\">&gt;<\/span>\r\n    <span class=\"nt\">&lt;template<\/span> <span class=\"na\">v-if=<\/span><span class=\"s\">\"userCurrent\"<\/span><span class=\"nt\">&gt;<\/span>\r\n      <span class=\"nt\">&lt;i<\/span> <span class=\"na\">class=<\/span><span class=\"s\">\"material-icons user-icon\"<\/span><span class=\"nt\">&gt;<\/span>account_circle<span class=\"nt\">&lt;\/i&gt;<\/span>\r\n      <span class=\"nt\">&lt;div<\/span> <span class=\"na\">class=<\/span><span class=\"s\">\"info\"<\/span><span class=\"nt\">&gt;<\/span>\r\n        <span class=\"nt\">&lt;div<\/span> <span class=\"na\">class=<\/span><span class=\"s\">\"nickname\"<\/span><span class=\"nt\">&gt;<\/span><span class=\"si\">{{<\/span> <span class=\"nx\">userCurrent<\/span><span class=\"p\">.<\/span><span class=\"nx\">nickname<\/span> <span class=\"si\">}}<\/span><span class=\"nt\">&lt;\/div&gt;<\/span>\r\n        <span class=\"nt\">&lt;div<\/span> <span class=\"na\">class=<\/span><span class=\"s\">\"email\"<\/span><span class=\"nt\">&gt;<\/span><span class=\"si\">{{<\/span> <span class=\"nx\">userCurrent<\/span><span class=\"p\">.<\/span><span class=\"nx\">email<\/span> <span class=\"si\">}}<\/span><span class=\"nt\">&lt;\/div&gt;<\/span>\r\n      <span class=\"nt\">&lt;\/div&gt;<\/span>\r\n      <span class=\"nt\">&lt;button<\/span>\r\n        <span class=\"na\">class=<\/span><span class=\"s\">\"icon-button\"<\/span>\r\n        <span class=\"na\">data-id=<\/span><span class=\"s\">\"logout\"<\/span>\r\n        <span class=\"err\">@<\/span><span class=\"na\">click=<\/span><span class=\"s\">\"logout()\"<\/span>\r\n      <span class=\"nt\">&gt;<\/span>\r\n        <span class=\"nt\">&lt;i<\/span> <span class=\"na\">class=<\/span><span class=\"s\">\"material-icons\"<\/span><span class=\"nt\">&gt;<\/span>power_settings_new<span class=\"nt\">&lt;\/i&gt;<\/span>\r\n      <span class=\"nt\">&lt;\/button&gt;<\/span>\r\n    <span class=\"nt\">&lt;\/<\/span><span class=\"k\">template<\/span><span class=\"nt\">&gt;<\/span>\r\n  <span class=\"nt\">&lt;\/div&gt;<\/span>\r\n<span class=\"nt\">&lt;\/template&gt;<\/span>\r\n\r\n<span class=\"nt\">&lt;<\/span><span class=\"k\">style<\/span> <span class=\"na\">lang=<\/span><span class=\"s\">\"stylus\"<\/span> <span class=\"na\">scoped<\/span><span class=\"nt\">&gt;<\/span>\r\n@import '~@\/style\/imports'\r\n\r\n.user-current\r\n  color white\r\n  display grid\r\n  grid-template-columns auto 1fr auto\r\n  grid-template-rows auto\r\n  grid-gap 12px\r\n  align-items center\r\n  margin-bottom 20px\r\n  padding 12px 0 12px 12px\r\n\r\n.email\r\n  font-size 12px\r\n\r\n.icon-button\r\n  &amp;:not(:hover)\r\n    background none\r\n<span class=\"nt\">&lt;\/<\/span><span class=\"k\">style<\/span><span class=\"nt\">&gt;<\/span>\r\n<\/code><\/pre>\n<h3>~\/work\/vue-apollo\/tests\/demo\/src\/components\/MessageForm.vue (\u6539\u53d8)<\/h3>\n<pre class=\"post-pre\"><code><span class=\"nt\">&lt;<\/span><span class=\"k\">script<\/span><span class=\"nt\">&gt;<\/span>\r\n<span class=\"o\">-<\/span> <span class=\"k\">import<\/span> <span class=\"nx\">MESSAGE_FRAGMENT<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">..\/graphql\/messageFragment.gql<\/span><span class=\"dl\">'<\/span>\r\n<span class=\"o\">-<\/span> <span class=\"k\">import<\/span> <span class=\"nx\">USER_FRAGMENT<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">..\/graphql\/userFragment.gql<\/span><span class=\"dl\">'<\/span>\r\n<span class=\"o\">-<\/span> \r\n<span class=\"k\">export<\/span> <span class=\"k\">default<\/span> <span class=\"p\">{<\/span>\r\n  <span class=\"na\">props<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n    <span class=\"na\">channelId<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n      <span class=\"na\">type<\/span><span class=\"p\">:<\/span> <span class=\"nb\">String<\/span><span class=\"p\">,<\/span>\r\n      <span class=\"na\">required<\/span><span class=\"p\">:<\/span> <span class=\"kc\">true<\/span><span class=\"p\">,<\/span>\r\n    <span class=\"p\">},<\/span>\r\n  <span class=\"p\">},<\/span>\r\n\r\n  <span class=\"nx\">data<\/span> <span class=\"p\">()<\/span> <span class=\"p\">{<\/span>\r\n    <span class=\"k\">return<\/span> <span class=\"p\">{<\/span>\r\n      <span class=\"na\">newMessage<\/span><span class=\"p\">:<\/span> <span class=\"dl\">''<\/span><span class=\"p\">,<\/span>\r\n    <span class=\"p\">}<\/span>\r\n  <span class=\"p\">},<\/span>\r\n\r\n  <span class=\"na\">methods<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n    <span class=\"nx\">onDone<\/span> <span class=\"p\">()<\/span> <span class=\"p\">{<\/span>\r\n      <span class=\"k\">this<\/span><span class=\"p\">.<\/span><span class=\"nx\">newMessage<\/span> <span class=\"o\">=<\/span> <span class=\"dl\">''<\/span>\r\n      <span class=\"k\">this<\/span><span class=\"p\">.<\/span><span class=\"nx\">$refs<\/span><span class=\"p\">.<\/span><span class=\"nx\">input<\/span><span class=\"p\">.<\/span><span class=\"nx\">focus<\/span><span class=\"p\">()<\/span>\r\n    <span class=\"p\">},<\/span>\r\n  <span class=\"p\">},<\/span>\r\n<span class=\"o\">-<\/span> \r\n<span class=\"o\">-<\/span>   <span class=\"na\">fragments<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n<span class=\"o\">-<\/span>     <span class=\"na\">message<\/span><span class=\"p\">:<\/span> <span class=\"nx\">MESSAGE_FRAGMENT<\/span><span class=\"p\">,<\/span>\r\n<span class=\"o\">-<\/span>     <span class=\"na\">user<\/span><span class=\"p\">:<\/span> <span class=\"nx\">USER_FRAGMENT<\/span><span class=\"p\">,<\/span>\r\n<span class=\"o\">-<\/span>   <span class=\"p\">},<\/span>\r\n<span class=\"p\">}<\/span>\r\n<span class=\"nt\">&lt;\/<\/span><span class=\"k\">script<\/span><span class=\"nt\">&gt;<\/span>\r\n\r\n<span class=\"nt\">&lt;<\/span><span class=\"k\">template<\/span><span class=\"nt\">&gt;<\/span>\r\n  <span class=\"nt\">&lt;ApolloMutation<\/span>\r\n<span class=\"na\">-<\/span>     <span class=\"na\">:mutation=<\/span><span class=\"s\">\"gql =&gt; gql`\r\n-       mutation messageAdd ($input: MessageAdd!) <\/span>{\r\n-         messageAdd (input: $input) {\r\n-           ...message\r\n-         }\r\n-       }\r\n-       ${$options.fragments.message}\r\n-       ${$options.fragments.user}\r\n-     `\"\r\n+     :mutation=\"require('..\/graphql\/messageAdd.gql')\"\r\n    :variables=\"{\r\n      input: {\r\n        channelId,\r\n        content: newMessage,\r\n      },\r\n    }\"\r\n    class=\"message-form\"\r\n    @done=\"onDone\"\r\n  &gt;\r\n    <span class=\"nt\">&lt;input<\/span>\r\n      <span class=\"na\">slot-scope=<\/span><span class=\"s\">\"<\/span>{ mutate, loading, error }\"\r\n      ref=\"input\"\r\n      v-model=\"newMessage\"\r\n      :disabled=\"loading\"\r\n      class=\"form-input\"\r\n      placeholder=\"Type a message\"\r\n      @keyup.enter=\"newMessage <span class=\"err\">&amp;&amp;<\/span> mutate()\"\r\n    &gt;\r\n  <span class=\"nt\">&lt;\/ApolloMutation&gt;<\/span>\r\n<span class=\"nt\">&lt;\/<\/span><span class=\"k\">template<\/span><span class=\"nt\">&gt;<\/span>\r\n\r\n<span class=\"nt\">&lt;<\/span><span class=\"k\">style<\/span> <span class=\"na\">lang=<\/span><span class=\"s\">\"stylus\"<\/span> <span class=\"na\">scoped<\/span><span class=\"nt\">&gt;<\/span>\r\n@import '~@\/style\/imports'\r\n\r\n.message-form\r\n  padding 12px\r\n  width 100%\r\n  box-sizing border-box\r\n\r\n  .form-input\r\n    display block\r\n    box-sizing border-box\r\n    width 100%\r\n<span class=\"nt\">&lt;\/<\/span><span class=\"k\">style<\/span><span class=\"nt\">&gt;<\/span>\r\n<\/code><\/pre>\n<h3>~\/work\/vue-apollo\/tests\/demo\/src\/components\/MessageItem.vue\uff08\u53d8\u66f4\uff09<\/h3>\n<pre class=\"post-pre\"><code><span class=\"nt\">&lt;<\/span><span class=\"k\">script<\/span><span class=\"nt\">&gt;<\/span>\r\n<span class=\"k\">import<\/span> <span class=\"nx\">marked<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">marked<\/span><span class=\"dl\">'<\/span>\r\n\r\n<span class=\"c1\">\/\/ Open links in new tab<\/span>\r\n<span class=\"kd\">const<\/span> <span class=\"nx\">renderer<\/span> <span class=\"o\">=<\/span> <span class=\"k\">new<\/span> <span class=\"nx\">marked<\/span><span class=\"p\">.<\/span><span class=\"nx\">Renderer<\/span><span class=\"p\">()<\/span>\r\n<span class=\"kd\">const<\/span> <span class=\"nx\">linkRenderer<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">renderer<\/span><span class=\"p\">.<\/span><span class=\"nx\">link<\/span>\r\n<span class=\"nx\">renderer<\/span><span class=\"p\">.<\/span><span class=\"nx\">link<\/span> <span class=\"o\">=<\/span> <span class=\"p\">(<\/span><span class=\"nx\">href<\/span><span class=\"p\">,<\/span> <span class=\"nx\">title<\/span><span class=\"p\">,<\/span> <span class=\"nx\">text<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"p\">{<\/span>\r\n  <span class=\"kd\">const<\/span> <span class=\"nx\">html<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">linkRenderer<\/span><span class=\"p\">.<\/span><span class=\"nx\">call<\/span><span class=\"p\">(<\/span><span class=\"nx\">renderer<\/span><span class=\"p\">,<\/span> <span class=\"nx\">href<\/span><span class=\"p\">,<\/span> <span class=\"nx\">title<\/span><span class=\"p\">,<\/span> <span class=\"nx\">text<\/span><span class=\"p\">)<\/span>\r\n  <span class=\"k\">return<\/span> <span class=\"nx\">html<\/span><span class=\"p\">.<\/span><span class=\"nx\">replace<\/span><span class=\"p\">(<\/span><span class=\"sr\">\/^&lt;a \/<\/span><span class=\"p\">,<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">&lt;a target=\"_blank\" rel=\"nofollow\" <\/span><span class=\"dl\">'<\/span><span class=\"p\">)<\/span>\r\n<span class=\"p\">}<\/span>\r\n\r\n<span class=\"k\">export<\/span> <span class=\"k\">default<\/span> <span class=\"p\">{<\/span>\r\n  <span class=\"na\">props<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n    <span class=\"na\">message<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n      <span class=\"na\">type<\/span><span class=\"p\">:<\/span> <span class=\"nb\">Object<\/span><span class=\"p\">,<\/span>\r\n      <span class=\"na\">required<\/span><span class=\"p\">:<\/span> <span class=\"kc\">true<\/span><span class=\"p\">,<\/span>\r\n    <span class=\"p\">},<\/span>\r\n  <span class=\"p\">},<\/span>\r\n\r\n  <span class=\"na\">computed<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n    <span class=\"nx\">html<\/span> <span class=\"p\">()<\/span> <span class=\"p\">{<\/span>\r\n      <span class=\"k\">return<\/span> <span class=\"nx\">marked<\/span><span class=\"p\">(<\/span><span class=\"k\">this<\/span><span class=\"p\">.<\/span><span class=\"nx\">message<\/span><span class=\"p\">.<\/span><span class=\"nx\">content<\/span><span class=\"p\">,<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">renderer<\/span> <span class=\"p\">})<\/span>\r\n    <span class=\"p\">},<\/span>\r\n  <span class=\"p\">},<\/span>\r\n<span class=\"p\">}<\/span>\r\n<span class=\"nt\">&lt;\/<\/span><span class=\"k\">script<\/span><span class=\"nt\">&gt;<\/span>\r\n\r\n<span class=\"nt\">&lt;<\/span><span class=\"k\">template<\/span><span class=\"nt\">&gt;<\/span>\r\n  <span class=\"nt\">&lt;div<\/span> <span class=\"na\">class=<\/span><span class=\"s\">\"message-item\"<\/span><span class=\"nt\">&gt;<\/span>\r\n-     <span class=\"nt\">&lt;div<\/span> <span class=\"na\">class=<\/span><span class=\"s\">\"user\"<\/span><span class=\"nt\">&gt;<\/span><span class=\"si\">{{<\/span> <span class=\"nx\">message<\/span><span class=\"p\">.<\/span><span class=\"nx\">user<\/span><span class=\"p\">.<\/span><span class=\"nx\">nickname<\/span> <span class=\"si\">}}<\/span><span class=\"nt\">&lt;\/div&gt;<\/span>\r\n+     <span class=\"nt\">&lt;div<\/span> <span class=\"na\">class=<\/span><span class=\"s\">\"user\"<\/span><span class=\"nt\">&gt;<\/span><span class=\"si\">{{<\/span> <span class=\"nx\">message<\/span><span class=\"p\">.<\/span><span class=\"nx\">userByUserId<\/span><span class=\"p\">.<\/span><span class=\"nx\">nickname<\/span> <span class=\"si\">}}<\/span><span class=\"nt\">&lt;\/div&gt;<\/span>\r\n    <span class=\"nt\">&lt;div<\/span> <span class=\"na\">class=<\/span><span class=\"s\">\"content\"<\/span> <span class=\"na\">v-html=<\/span><span class=\"s\">\"html\"<\/span><span class=\"nt\">\/&gt;<\/span>\r\n  <span class=\"nt\">&lt;\/div&gt;<\/span>\r\n<span class=\"nt\">&lt;\/<\/span><span class=\"k\">template<\/span><span class=\"nt\">&gt;<\/span>\r\n\r\n<span class=\"nt\">&lt;<\/span><span class=\"k\">style<\/span> <span class=\"na\">lang=<\/span><span class=\"s\">\"stylus\"<\/span> <span class=\"na\">scoped<\/span><span class=\"nt\">&gt;<\/span>\r\n@import '~@\/style\/imports'\r\n\r\n.message-item\r\n  padding 12px 12px\r\n  &amp;:hover\r\n    background #f8f8f8\r\n\r\n.user\r\n  color #777\r\n  font-size 13px\r\n  margin-bottom 2px\r\n\r\n.content\r\n  word-wrap break-word\r\n\r\n  &gt;&gt;&gt;\r\n    p\r\n      margin 0\r\n\r\n    img\r\n      max-width 500px\r\n      max-height 500px\r\n<span class=\"nt\">&lt;\/<\/span><span class=\"k\">style<\/span><span class=\"nt\">&gt;<\/span>\r\n<\/code><\/pre>\n<h3>~\/work\/vue-apollo\/tests\/demo\/src\/components\/ChannelList.vue\uff08\u6539\u53d8\uff09<\/h3>\n<pre class=\"post-pre\"><code><span class=\"nt\">&lt;<\/span><span class=\"k\">script<\/span><span class=\"nt\">&gt;<\/span>\r\n<span class=\"k\">import<\/span> <span class=\"nx\">UserCurrent<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">.\/UserCurrent.vue<\/span><span class=\"dl\">'<\/span>\r\n<span class=\"k\">import<\/span> <span class=\"nx\">MockSendMessage<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">.\/MockSendMessage.vue<\/span><span class=\"dl\">'<\/span>\r\n<span class=\"o\">-<\/span> <span class=\"k\">import<\/span> <span class=\"nx\">gql<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">graphql-tag<\/span><span class=\"dl\">'<\/span>\r\n\r\n<span class=\"k\">export<\/span> <span class=\"k\">default<\/span> <span class=\"p\">{<\/span>\r\n  <span class=\"na\">name<\/span><span class=\"p\">:<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">ChannelList<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span>\r\n\r\n  <span class=\"na\">components<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n    <span class=\"nx\">UserCurrent<\/span><span class=\"p\">,<\/span>\r\n    <span class=\"nx\">MockSendMessage<\/span><span class=\"p\">,<\/span>\r\n  <span class=\"p\">},<\/span>\r\n<span class=\"o\">-<\/span> \r\n<span class=\"o\">-<\/span>   <span class=\"na\">fragments<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n<span class=\"o\">-<\/span>     <span class=\"na\">channel<\/span><span class=\"p\">:<\/span> <span class=\"nx\">gql<\/span><span class=\"s2\">`\r\n-       fragment channel on Channel {\r\n-         id\r\n-         name\r\n-       }\r\n-     `<\/span><span class=\"p\">,<\/span>\r\n<span class=\"o\">-<\/span>   <span class=\"p\">},<\/span>\r\n<span class=\"p\">}<\/span>\r\n<span class=\"nt\">&lt;\/<\/span><span class=\"k\">script<\/span><span class=\"nt\">&gt;<\/span>\r\n\r\n<span class=\"nt\">&lt;<\/span><span class=\"k\">template<\/span><span class=\"nt\">&gt;<\/span>\r\n  <span class=\"nt\">&lt;div<\/span> <span class=\"na\">class=<\/span><span class=\"s\">\"channel-list\"<\/span><span class=\"nt\">&gt;<\/span>\r\n    <span class=\"nt\">&lt;UserCurrent<\/span> <span class=\"nt\">\/&gt;<\/span>\r\n\r\n-     <span class=\"nt\">&lt;ApolloQuery<\/span> <span class=\"na\">:query=<\/span><span class=\"s\">\"gql =&gt; gql`\r\n-       query channels <\/span>{\r\n-         channels {\r\n-           ...channel\r\n-         }\r\n-       }\r\n-       ${$options.fragments.channel}\r\n-     `\"&gt;\r\n+     <span class=\"nt\">&lt;ApolloQuery<\/span> <span class=\"na\">:query=<\/span><span class=\"s\">\"require('..\/graphql\/channels.gql')\"<\/span><span class=\"nt\">&gt;<\/span>\r\n      <span class=\"nt\">&lt;template<\/span> <span class=\"na\">slot-scope=<\/span><span class=\"s\">\"<\/span>{ result: { data, loading } }\"&gt;\r\n        <span class=\"nt\">&lt;div<\/span> <span class=\"na\">v-if=<\/span><span class=\"s\">\"loading\"<\/span> <span class=\"na\">class=<\/span><span class=\"s\">\"loading\"<\/span><span class=\"nt\">&gt;<\/span>Loading...<span class=\"nt\">&lt;\/div&gt;<\/span>\r\n        <span class=\"nt\">&lt;div<\/span> <span class=\"na\">v-else-if=<\/span><span class=\"s\">\"data\"<\/span> <span class=\"na\">class=<\/span><span class=\"s\">\"channels\"<\/span><span class=\"nt\">&gt;<\/span>\r\n          <span class=\"nt\">&lt;router-link<\/span>\r\n<span class=\"na\">-<\/span>             <span class=\"na\">v-for=<\/span><span class=\"s\">\"channel of data.channels\"<\/span>\r\n<span class=\"err\">+<\/span>             <span class=\"na\">v-for=<\/span><span class=\"s\">\"channel of data.allChannels.nodes\"<\/span>\r\n            <span class=\"na\">:key=<\/span><span class=\"s\">\"channel.id\"<\/span>\r\n            <span class=\"na\">:to=<\/span><span class=\"s\">\"<\/span>{ name: 'channel', params: { id: channel.id } }\"\r\n            class=\"channel\"\r\n          &gt;\r\n            <span class=\"nt\">&lt;div<\/span> <span class=\"na\">class=<\/span><span class=\"s\">\"id\"<\/span><span class=\"nt\">&gt;<\/span>#<span class=\"si\">{{<\/span> <span class=\"nx\">channel<\/span><span class=\"p\">.<\/span><span class=\"nx\">id<\/span> <span class=\"si\">}}<\/span><span class=\"nt\">&lt;\/div&gt;<\/span>\r\n            <span class=\"nt\">&lt;div<\/span> <span class=\"na\">class=<\/span><span class=\"s\">\"name\"<\/span><span class=\"nt\">&gt;<\/span><span class=\"si\">{{<\/span> <span class=\"nx\">channel<\/span><span class=\"p\">.<\/span><span class=\"nx\">name<\/span> <span class=\"si\">}}<\/span><span class=\"nt\">&lt;\/div&gt;<\/span>\r\n          <span class=\"nt\">&lt;\/router-link&gt;<\/span>\r\n        <span class=\"nt\">&lt;\/div&gt;<\/span>\r\n      <span class=\"nt\">&lt;\/<\/span><span class=\"k\">template<\/span><span class=\"nt\">&gt;<\/span>\r\n    <span class=\"nt\">&lt;\/ApolloQuery&gt;<\/span>\r\n\r\n    <span class=\"nt\">&lt;MockSendMessage\/&gt;<\/span>\r\n  <span class=\"nt\">&lt;\/div&gt;<\/span>\r\n<span class=\"nt\">&lt;\/template&gt;<\/span>\r\n\r\n<span class=\"nt\">&lt;<\/span><span class=\"k\">style<\/span> <span class=\"na\">lang=<\/span><span class=\"s\">\"stylus\"<\/span> <span class=\"na\">scoped<\/span><span class=\"nt\">&gt;<\/span>\r\n@import '~@\/style\/imports'\r\n\r\n.channel-list\r\n  background desaturate(darken($color, 60%), 95%)\r\n  color white\r\n  padding 12px\r\n\r\n.channel\r\n  display block\r\n  padding 12px\r\n  border-radius 4px\r\n  &amp;:hover\r\n    background rgba($color, .3)\r\n  &amp;.router-link-active\r\n    background $color\r\n    color white\r\n    font-weight bold\r\n\r\n.id\r\n  font-family monospace\r\n  margin-bottom 4px\r\n  font-size 14px\r\n\r\n.name\r\n  font-size 12px\r\n  opacity .9\r\n<span class=\"nt\">&lt;\/<\/span><span class=\"k\">style<\/span><span class=\"nt\">&gt;<\/span>\r\n<\/code><\/pre>\n<h3>~\/work\/vue-apollo\/tests\/demo\/src\/components\/ChannelView.vue (\u53d8\u66f4)<\/h3>\n<pre class=\"post-pre\"><code><span class=\"nt\">&lt;<\/span><span class=\"k\">script<\/span><span class=\"nt\">&gt;<\/span>\r\n<span class=\"k\">import<\/span> <span class=\"nx\">MessageItem<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">.\/MessageItem.vue<\/span><span class=\"dl\">'<\/span>\r\n<span class=\"k\">import<\/span> <span class=\"nx\">MessageForm<\/span> <span class=\"k\">from<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">.\/MessageForm.vue<\/span><span class=\"dl\">'<\/span>\r\n\r\n<span class=\"k\">export<\/span> <span class=\"k\">default<\/span> <span class=\"p\">{<\/span>\r\n  <span class=\"na\">name<\/span><span class=\"p\">:<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">ChannelView<\/span><span class=\"dl\">'<\/span><span class=\"p\">,<\/span>\r\n\r\n  <span class=\"na\">components<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n    <span class=\"nx\">MessageItem<\/span><span class=\"p\">,<\/span>\r\n    <span class=\"nx\">MessageForm<\/span><span class=\"p\">,<\/span>\r\n  <span class=\"p\">},<\/span>\r\n\r\n  <span class=\"na\">props<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n    <span class=\"na\">id<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n      <span class=\"na\">type<\/span><span class=\"p\">:<\/span> <span class=\"nb\">String<\/span><span class=\"p\">,<\/span>\r\n      <span class=\"na\">required<\/span><span class=\"p\">:<\/span> <span class=\"kc\">true<\/span><span class=\"p\">,<\/span>\r\n    <span class=\"p\">},<\/span>\r\n  <span class=\"p\">},<\/span>\r\n\r\n  <span class=\"na\">watch<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n    <span class=\"na\">id<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n      <span class=\"nx\">handler<\/span> <span class=\"p\">()<\/span> <span class=\"p\">{<\/span>\r\n        <span class=\"k\">this<\/span><span class=\"p\">.<\/span><span class=\"nx\">$_init<\/span> <span class=\"o\">=<\/span> <span class=\"kc\">false<\/span>\r\n      <span class=\"p\">},<\/span>\r\n      <span class=\"na\">immediate<\/span><span class=\"p\">:<\/span> <span class=\"kc\">true<\/span><span class=\"p\">,<\/span>\r\n    <span class=\"p\">},<\/span>\r\n  <span class=\"p\">},<\/span>\r\n\r\n  <span class=\"na\">methods<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n    <span class=\"nx\">onMessageChanged<\/span> <span class=\"p\">(<\/span><span class=\"nx\">previousResult<\/span><span class=\"p\">,<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">subscriptionData<\/span> <span class=\"p\">})<\/span> <span class=\"p\">{<\/span>\r\n<span class=\"o\">-<\/span>       <span class=\"kd\">const<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">type<\/span><span class=\"p\">,<\/span> <span class=\"nx\">message<\/span> <span class=\"p\">}<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">subscriptionData<\/span><span class=\"p\">.<\/span><span class=\"nx\">data<\/span><span class=\"p\">.<\/span><span class=\"nx\">messageChanged<\/span>\r\n<span class=\"o\">-<\/span> \r\n<span class=\"o\">-<\/span>       <span class=\"c1\">\/\/ No list change<\/span>\r\n<span class=\"o\">-<\/span>       <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"nx\">type<\/span> <span class=\"o\">===<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">updated<\/span><span class=\"dl\">'<\/span><span class=\"p\">)<\/span> <span class=\"k\">return<\/span> <span class=\"nx\">previousResult<\/span>\r\n<span class=\"o\">-<\/span> \r\n<span class=\"o\">-<\/span>       <span class=\"kd\">const<\/span> <span class=\"nx\">messages<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">previousResult<\/span><span class=\"p\">.<\/span><span class=\"nx\">channel<\/span><span class=\"p\">.<\/span><span class=\"nx\">messages<\/span><span class=\"p\">.<\/span><span class=\"nx\">slice<\/span><span class=\"p\">()<\/span>\r\n<span class=\"o\">-<\/span>       <span class=\"c1\">\/\/ Add or remove item<\/span>\r\n<span class=\"o\">-<\/span>       <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"nx\">type<\/span> <span class=\"o\">===<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">added<\/span><span class=\"dl\">'<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\r\n<span class=\"o\">-<\/span>         <span class=\"nx\">messages<\/span><span class=\"p\">.<\/span><span class=\"nx\">push<\/span><span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">)<\/span>\r\n<span class=\"o\">-<\/span>       <span class=\"p\">}<\/span> <span class=\"k\">else<\/span> <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"nx\">type<\/span> <span class=\"o\">===<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">removed<\/span><span class=\"dl\">'<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\r\n<span class=\"o\">-<\/span>         <span class=\"kd\">const<\/span> <span class=\"nx\">index<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">messages<\/span><span class=\"p\">.<\/span><span class=\"nx\">findIndex<\/span><span class=\"p\">(<\/span><span class=\"nx\">m<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"nx\">m<\/span><span class=\"p\">.<\/span><span class=\"nx\">id<\/span> <span class=\"o\">===<\/span> <span class=\"nx\">message<\/span><span class=\"p\">.<\/span><span class=\"nx\">id<\/span><span class=\"p\">)<\/span>\r\n<span class=\"o\">-<\/span>         <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"nx\">index<\/span> <span class=\"o\">!==<\/span> <span class=\"o\">-<\/span><span class=\"mi\">1<\/span><span class=\"p\">)<\/span> <span class=\"nx\">messages<\/span><span class=\"p\">.<\/span><span class=\"nx\">splice<\/span><span class=\"p\">(<\/span><span class=\"nx\">index<\/span><span class=\"p\">,<\/span> <span class=\"mi\">1<\/span><span class=\"p\">)<\/span>\r\n<span class=\"o\">-<\/span>       <span class=\"p\">}<\/span>\r\n<span class=\"o\">-<\/span> \r\n<span class=\"o\">-<\/span>       <span class=\"c1\">\/\/ New query result<\/span>\r\n<span class=\"o\">-<\/span>       <span class=\"k\">return<\/span> <span class=\"p\">{<\/span>\r\n<span class=\"o\">-<\/span>         <span class=\"na\">channel<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n<span class=\"o\">-<\/span>           <span class=\"p\">...<\/span><span class=\"nx\">previousResult<\/span><span class=\"p\">.<\/span><span class=\"nx\">channel<\/span><span class=\"p\">,<\/span>\r\n<span class=\"o\">-<\/span>           <span class=\"nx\">messages<\/span><span class=\"p\">,<\/span>\r\n<span class=\"o\">-<\/span>         <span class=\"p\">},<\/span>\r\n<span class=\"o\">-<\/span>       <span class=\"p\">}<\/span>\r\n<span class=\"o\">+<\/span>       <span class=\"kd\">const<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">message<\/span><span class=\"p\">,<\/span> <span class=\"nx\">oldrec<\/span><span class=\"p\">,<\/span> <span class=\"nx\">type<\/span> <span class=\"p\">}<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">subscriptionData<\/span><span class=\"p\">.<\/span><span class=\"nx\">data<\/span><span class=\"p\">.<\/span><span class=\"nx\">messageChanged<\/span>\r\n<span class=\"o\">+<\/span> \r\n<span class=\"o\">+<\/span>       <span class=\"kd\">const<\/span> <span class=\"nx\">nodes<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">previousResult<\/span><span class=\"p\">.<\/span><span class=\"nx\">channelById<\/span><span class=\"p\">.<\/span><span class=\"nx\">messagesByChannelId<\/span><span class=\"p\">.<\/span><span class=\"nx\">nodes<\/span><span class=\"p\">.<\/span><span class=\"nx\">slice<\/span><span class=\"p\">()<\/span>\r\n<span class=\"o\">+<\/span>       <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"nx\">type<\/span> <span class=\"o\">===<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">INSERT<\/span><span class=\"dl\">'<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\r\n<span class=\"o\">+<\/span>         <span class=\"nx\">nodes<\/span><span class=\"p\">.<\/span><span class=\"nx\">push<\/span><span class=\"p\">(<\/span><span class=\"nx\">message<\/span><span class=\"p\">)<\/span>\r\n<span class=\"o\">+<\/span>       <span class=\"p\">}<\/span> <span class=\"k\">else<\/span> <span class=\"p\">{<\/span>\r\n<span class=\"o\">+<\/span>         <span class=\"kd\">const<\/span> <span class=\"nx\">index<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">nodes<\/span><span class=\"p\">.<\/span><span class=\"nx\">findIndex<\/span><span class=\"p\">(<\/span><span class=\"nx\">m<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"nx\">m<\/span><span class=\"p\">.<\/span><span class=\"nx\">id<\/span> <span class=\"o\">===<\/span> <span class=\"nx\">oldrec<\/span><span class=\"p\">.<\/span><span class=\"nx\">id<\/span><span class=\"p\">)<\/span>\r\n<span class=\"o\">+<\/span>         <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"nx\">index<\/span> <span class=\"o\">!==<\/span> <span class=\"o\">-<\/span><span class=\"mi\">1<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\r\n<span class=\"o\">+<\/span>           <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"nx\">type<\/span> <span class=\"o\">===<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">DELETE<\/span><span class=\"dl\">'<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\r\n<span class=\"o\">+<\/span>             <span class=\"nx\">nodes<\/span><span class=\"p\">.<\/span><span class=\"nx\">splice<\/span><span class=\"p\">(<\/span><span class=\"nx\">index<\/span><span class=\"p\">,<\/span> <span class=\"mi\">1<\/span><span class=\"p\">)<\/span>\r\n<span class=\"o\">+<\/span>           <span class=\"p\">}<\/span> <span class=\"k\">else<\/span> <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"nx\">type<\/span> <span class=\"o\">===<\/span> <span class=\"dl\">'<\/span><span class=\"s1\">UPDATE<\/span><span class=\"dl\">'<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\r\n<span class=\"o\">+<\/span>             <span class=\"nx\">nodes<\/span><span class=\"p\">.<\/span><span class=\"nx\">splice<\/span><span class=\"p\">(<\/span><span class=\"nx\">index<\/span><span class=\"p\">,<\/span> <span class=\"mi\">1<\/span><span class=\"p\">,<\/span> <span class=\"nx\">message<\/span><span class=\"p\">)<\/span>\r\n<span class=\"o\">+<\/span>           <span class=\"p\">}<\/span>\r\n<span class=\"o\">+<\/span>         <span class=\"p\">}<\/span>\r\n<span class=\"o\">+<\/span>       <span class=\"p\">}<\/span>\r\n<span class=\"o\">+<\/span> \r\n<span class=\"o\">+<\/span>       <span class=\"c1\">\/\/ New query result<\/span>\r\n<span class=\"o\">+<\/span>       <span class=\"k\">return<\/span> <span class=\"p\">{<\/span>\r\n<span class=\"o\">+<\/span>         <span class=\"na\">channelById<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n<span class=\"o\">+<\/span>           <span class=\"p\">...<\/span><span class=\"nx\">previousResult<\/span><span class=\"p\">.<\/span><span class=\"nx\">channelById<\/span><span class=\"p\">,<\/span>\r\n<span class=\"o\">+<\/span>           <span class=\"na\">messagesByChannelId<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span>\r\n<span class=\"o\">+<\/span>             <span class=\"p\">...<\/span><span class=\"nx\">previousResult<\/span><span class=\"p\">.<\/span><span class=\"nx\">channelById<\/span><span class=\"p\">.<\/span><span class=\"nx\">messagesByChannelId<\/span><span class=\"p\">,<\/span>\r\n<span class=\"o\">+<\/span>             <span class=\"nx\">nodes<\/span><span class=\"p\">,<\/span>\r\n<span class=\"o\">+<\/span>           <span class=\"p\">},<\/span>\r\n<span class=\"o\">+<\/span>         <span class=\"p\">},<\/span>\r\n<span class=\"o\">+<\/span>       <span class=\"p\">}<\/span>\r\n    <span class=\"p\">},<\/span>\r\n\r\n    <span class=\"k\">async<\/span> <span class=\"nx\">scrollToBottom<\/span> <span class=\"p\">(<\/span><span class=\"nx\">force<\/span> <span class=\"o\">=<\/span> <span class=\"kc\">false<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\r\n      <span class=\"kd\">let<\/span> <span class=\"nx\">el<\/span> <span class=\"o\">=<\/span> <span class=\"k\">this<\/span><span class=\"p\">.<\/span><span class=\"nx\">$refs<\/span><span class=\"p\">.<\/span><span class=\"nx\">body<\/span>\r\n\r\n      <span class=\"c1\">\/\/ No body element yet<\/span>\r\n      <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"o\">!<\/span><span class=\"nx\">el<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\r\n        <span class=\"nx\">setTimeout<\/span><span class=\"p\">(()<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"k\">this<\/span><span class=\"p\">.<\/span><span class=\"nx\">scrollToBottom<\/span><span class=\"p\">(<\/span><span class=\"nx\">force<\/span><span class=\"p\">),<\/span> <span class=\"mi\">100<\/span><span class=\"p\">)<\/span>\r\n        <span class=\"k\">return<\/span>\r\n      <span class=\"p\">}<\/span>\r\n      <span class=\"c1\">\/\/ User is scrolling up =&gt; no auto scroll<\/span>\r\n      <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"o\">!<\/span><span class=\"nx\">force<\/span> <span class=\"o\">&amp;&amp;<\/span> <span class=\"nx\">el<\/span><span class=\"p\">.<\/span><span class=\"nx\">scrollTop<\/span> <span class=\"o\">+<\/span> <span class=\"nx\">el<\/span><span class=\"p\">.<\/span><span class=\"nx\">clientHeight<\/span> <span class=\"o\">&lt;<\/span> <span class=\"nx\">el<\/span><span class=\"p\">.<\/span><span class=\"nx\">scrollHeight<\/span> <span class=\"o\">-<\/span> <span class=\"mi\">100<\/span><span class=\"p\">)<\/span> <span class=\"k\">return<\/span>\r\n\r\n      <span class=\"c1\">\/\/ Scroll to bottom<\/span>\r\n      <span class=\"k\">await<\/span> <span class=\"k\">this<\/span><span class=\"p\">.<\/span><span class=\"nx\">$nextTick<\/span><span class=\"p\">()<\/span>\r\n      <span class=\"nx\">el<\/span><span class=\"p\">.<\/span><span class=\"nx\">scrollTop<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">el<\/span><span class=\"p\">.<\/span><span class=\"nx\">scrollHeight<\/span>\r\n    <span class=\"p\">},<\/span>\r\n\r\n    <span class=\"nx\">onResult<\/span> <span class=\"p\">(<\/span><span class=\"nx\">result<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\r\n      <span class=\"c1\">\/\/ The first time we load a channel, we force scroll to bottom<\/span>\r\n      <span class=\"k\">this<\/span><span class=\"p\">.<\/span><span class=\"nx\">scrollToBottom<\/span><span class=\"p\">(<\/span><span class=\"o\">!<\/span><span class=\"k\">this<\/span><span class=\"p\">.<\/span><span class=\"nx\">$_init<\/span><span class=\"p\">)<\/span>\r\n      <span class=\"k\">this<\/span><span class=\"p\">.<\/span><span class=\"nx\">$_init<\/span> <span class=\"o\">=<\/span> <span class=\"kc\">true<\/span>\r\n    <span class=\"p\">},<\/span>\r\n  <span class=\"p\">},<\/span>\r\n<span class=\"p\">}<\/span>\r\n<span class=\"nt\">&lt;\/<\/span><span class=\"k\">script<\/span><span class=\"nt\">&gt;<\/span>\r\n\r\n<span class=\"nt\">&lt;<\/span><span class=\"k\">template<\/span><span class=\"nt\">&gt;<\/span>\r\n  <span class=\"nt\">&lt;div<\/span> <span class=\"na\">class=<\/span><span class=\"s\">\"channel-view\"<\/span><span class=\"nt\">&gt;<\/span>\r\n    <span class=\"nt\">&lt;ApolloQuery<\/span>\r\n      <span class=\"na\">:query=<\/span><span class=\"s\">\"require('..\/graphql\/channel.gql')\"<\/span>\r\n      <span class=\"na\">:variables=<\/span><span class=\"s\">\"<\/span>{\r\n        id\r\n      }\"\r\n      @result=\"onResult\"\r\n    &gt;\r\n      <span class=\"nt\">&lt;template<\/span> <span class=\"na\">slot-scope=<\/span><span class=\"s\">\"<\/span>{ result: { data, loading } }\"&gt;\r\n        <span class=\"nt\">&lt;div<\/span> <span class=\"na\">v-if=<\/span><span class=\"s\">\"!data &amp;&amp; loading\"<\/span> <span class=\"na\">class=<\/span><span class=\"s\">\"loading\"<\/span><span class=\"nt\">&gt;<\/span>Loading...<span class=\"nt\">&lt;\/div&gt;<\/span>\r\n\r\n        <span class=\"nt\">&lt;div<\/span> <span class=\"na\">v-else-if=<\/span><span class=\"s\">\"data\"<\/span><span class=\"nt\">&gt;<\/span>\r\n          <span class=\"c\">&lt;!-- Websockets --&gt;<\/span>\r\n          <span class=\"nt\">&lt;ApolloSubscribeToMore<\/span>\r\n            <span class=\"na\">:document=<\/span><span class=\"s\">\"require('..\/graphql\/messageChanged.gql')\"<\/span>\r\n            <span class=\"na\">:variables=<\/span><span class=\"s\">\"<\/span>{\r\n              channelId: id,\r\n            }\"\r\n            :updateQuery=\"onMessageChanged\"\r\n          \/&gt;\r\n\r\n          <span class=\"nt\">&lt;div<\/span> <span class=\"na\">class=<\/span><span class=\"s\">\"wrapper\"<\/span><span class=\"nt\">&gt;<\/span>\r\n            <span class=\"nt\">&lt;div<\/span> <span class=\"na\">class=<\/span><span class=\"s\">\"header\"<\/span><span class=\"nt\">&gt;<\/span>\r\n-               <span class=\"nt\">&lt;div<\/span> <span class=\"na\">class=<\/span><span class=\"s\">\"id\"<\/span><span class=\"nt\">&gt;<\/span>#<span class=\"si\">{{<\/span> <span class=\"nx\">data<\/span><span class=\"p\">.<\/span><span class=\"nx\">channel<\/span><span class=\"p\">.<\/span><span class=\"nx\">id<\/span> <span class=\"si\">}}<\/span><span class=\"nt\">&lt;\/div&gt;<\/span>\r\n-               <span class=\"nt\">&lt;div<\/span> <span class=\"na\">class=<\/span><span class=\"s\">\"name\"<\/span><span class=\"nt\">&gt;<\/span><span class=\"si\">{{<\/span> <span class=\"nx\">data<\/span><span class=\"p\">.<\/span><span class=\"nx\">channel<\/span><span class=\"p\">.<\/span><span class=\"nx\">name<\/span> <span class=\"si\">}}<\/span><span class=\"nt\">&lt;\/div&gt;<\/span>\r\n+               <span class=\"nt\">&lt;div<\/span> <span class=\"na\">class=<\/span><span class=\"s\">\"id\"<\/span><span class=\"nt\">&gt;<\/span>#<span class=\"si\">{{<\/span> <span class=\"nx\">data<\/span><span class=\"p\">.<\/span><span class=\"nx\">channelById<\/span><span class=\"p\">.<\/span><span class=\"nx\">id<\/span> <span class=\"si\">}}<\/span><span class=\"nt\">&lt;\/div&gt;<\/span>\r\n+               <span class=\"nt\">&lt;div<\/span> <span class=\"na\">class=<\/span><span class=\"s\">\"name\"<\/span><span class=\"nt\">&gt;<\/span><span class=\"si\">{{<\/span> <span class=\"nx\">data<\/span><span class=\"p\">.<\/span><span class=\"nx\">channelById<\/span><span class=\"p\">.<\/span><span class=\"nx\">name<\/span> <span class=\"si\">}}<\/span><span class=\"nt\">&lt;\/div&gt;<\/span>\r\n            <span class=\"nt\">&lt;\/div&gt;<\/span>\r\n\r\n            <span class=\"nt\">&lt;div<\/span> <span class=\"na\">ref=<\/span><span class=\"s\">\"body\"<\/span> <span class=\"na\">class=<\/span><span class=\"s\">\"body\"<\/span><span class=\"nt\">&gt;<\/span>\r\n              <span class=\"nt\">&lt;MessageItem<\/span>\r\n<span class=\"na\">-<\/span>                 <span class=\"na\">v-for=<\/span><span class=\"s\">\"message in data.channel.messages\"<\/span>\r\n<span class=\"err\">+<\/span>                 <span class=\"na\">v-for=<\/span><span class=\"s\">\"message in data.channelById.messagesByChannelId.nodes\"<\/span>\r\n                <span class=\"na\">:key=<\/span><span class=\"s\">\"message.id\"<\/span>\r\n                <span class=\"na\">:message=<\/span><span class=\"s\">\"message\"<\/span>\r\n              <span class=\"nt\">\/&gt;<\/span>\r\n            <span class=\"nt\">&lt;\/div&gt;<\/span>\r\n\r\n            <span class=\"nt\">&lt;div<\/span> <span class=\"na\">class=<\/span><span class=\"s\">\"footer\"<\/span><span class=\"nt\">&gt;<\/span>\r\n              <span class=\"nt\">&lt;MessageForm<\/span> <span class=\"na\">:channel-id=<\/span><span class=\"s\">\"id\"<\/span> <span class=\"nt\">\/&gt;<\/span>\r\n            <span class=\"nt\">&lt;\/div&gt;<\/span>\r\n          <span class=\"nt\">&lt;\/div&gt;<\/span>\r\n        <span class=\"nt\">&lt;\/div&gt;<\/span>\r\n      <span class=\"nt\">&lt;\/<\/span><span class=\"k\">template<\/span><span class=\"nt\">&gt;<\/span>\r\n    <span class=\"nt\">&lt;\/ApolloQuery&gt;<\/span>\r\n  <span class=\"nt\">&lt;\/div&gt;<\/span>\r\n<span class=\"nt\">&lt;\/template&gt;<\/span>\r\n\r\n<span class=\"nt\">&lt;<\/span><span class=\"k\">style<\/span> <span class=\"na\">lang=<\/span><span class=\"s\">\"stylus\"<\/span> <span class=\"na\">scoped<\/span><span class=\"nt\">&gt;<\/span>\r\n@import '~@\/style\/imports'\r\n\r\n.wrapper\r\n  height 100vh\r\n  display grid\r\n  grid-template-columns 1fr\r\n  grid-template-rows auto 1fr auto\r\n\r\n.header\r\n  padding 12px\r\n  border-bottom $border\r\n\r\n.id\r\n  font-family monospace\r\n  margin-bottom 4px\r\n\r\n.name\r\n  color #555\r\n\r\n.body\r\n  overflow-x hidden\r\n  overflow-y auto\r\n\r\n.footer\r\n  border-top $border\r\n<span class=\"nt\">&lt;\/<\/span><span class=\"k\">style<\/span><span class=\"nt\">&gt;<\/span>\r\n<\/code><\/pre>\n<p>\u4ee5\u4e0a\u662f\u6e90\u4ee3\u7801\u4fee\u6539\u5b8c\u6210\u3002<\/p>\n<h1>\u8fd0\u884cPostGraphile\u7248ApolloChat<\/h1>\n<p>\u90a3\u4e48\u6211\u5c06\u8bd5\u7740\u8fd0\u884c\u5b83\u3002<br \/>\n\u4ee5\u4e0b\u662f\u6267\u884c\u64cd\u4f5c\uff0c\u5c06\u542f\u52a8Web\u670d\u52a1\u5668\uff08Vue.js\u5e94\u7528\u7a0b\u5e8f\u4f20\u8f93\uff09\u3002<br \/>\n\u7531\u4e8ePostGraphile\u5df2\u5728Docker\u5bb9\u5668\u4e2d\u542f\u52a8\uff0c\u56e0\u6b64\u4e0d\u9700\u8981\u5355\u72ec\u542f\u52a8GraphQL\u670d\u52a1\u5668\u3002<\/p>\n<pre class=\"post-pre\"><code>cd ~\/work\/vue-apollo\/tests\/demo\r\nyarn serve\r\n<\/code><\/pre>\n<p>\u786e\u8ba4\u80fd\u6210\u529f\u7f16\u8bd1\u3002<br \/>\n\u7f16\u8bd1\u540e\u663e\u793a\u5982\u4e0b\u3002<\/p>\n<pre class=\"post-pre\"><code> DONE  Compiled successfully in 3593ms                                       10:31:39\r\n\r\n\r\n  App running at:\r\n  - Local:   http:\/\/localhost:8080\/demo\/ \r\n  - Network: unavailable\r\n\r\n  Note that the development build is not optimized.\r\n  To create a production build, run yarn build.\r\n<\/code><\/pre>\n<p>\u6309\u7167\u6307\u793a\uff0c\u5728\u6d4f\u89c8\u5668\u4e2d\u8bbf\u95ee\u300chttp:\/\/localhost:8080\/demo\/\u300d\uff0c\u5c06\u542f\u52a8PostGraphile\u7248ApolloChat\u3002<br \/>\n\u9996\u5148\u4f1a\u51fa\u73b0\u767b\u5f55\u754c\u9762\u3002<\/p>\n<div><img decoding=\"async\" class=\"post-images\" title=\"\" src=\"https:\/\/cdn.silicloud.com\/blog-img\/blog\/img\/657d82d5913a08637a6b1920\/340-0.png\" alt=\"\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8 2019-06-19 10.33.31.png\" \/><\/div>\n<p>\u6211\u5c1d\u8bd5\u4f7f\u7528Altair GraphQL Client\u521b\u5efa\u7684\u5e10\u6237\u767b\u5f55\u3002<\/p>\n<pre class=\"post-pre\"><code>Email\uff1afoo@foo.com\r\nPassword\uff1ap@ss\r\n<\/code><\/pre>\n<div><img decoding=\"async\" class=\"post-images\" title=\"\" src=\"https:\/\/cdn.silicloud.com\/blog-img\/blog\/img\/657d82d5913a08637a6b1920\/343-0.png\" alt=\"\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8 2019-06-19 10.35.41.png\" \/><\/div>\n<p>\u6211\u8981\u8bd5\u7740\u8fdb\u5165\u4e00\u822c\u9891\u9053\u3002<\/p>\n<div><img decoding=\"async\" class=\"post-images\" title=\"\" src=\"https:\/\/cdn.silicloud.com\/blog-img\/blog\/img\/657d82d5913a08637a6b1920\/345-0.png\" alt=\"\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8 2019-06-19 10.36.12.png\" \/><\/div>\n<p>\u5f53\u8bd5\u56fe\u6267\u884cGraphQL\u67e5\u8be2\u65f6\uff0c\u7559\u4e0b\u4e86\u4e00\u6761\u6d88\u606f\u3002<\/p>\n<ul class=\"post-ul\">\n<li style=\"list-style-type: none;\">\n<ul class=\"post-ul\">messageChanged\u30b5\u30d6\u30b9\u30af\u30ea\u30d7\u30b7\u30e7\u30f3\u3092\u8a66\u884c\u3057\u305f\u6642\u306b\u3001psql\u304b\u3089INSERT\u3057\u305f\u300c\u7f8e\u5473\u3057\u3044\uff01\u300d\u30e1\u30c3\u30bb\u30fc\u30b8<\/ul>\n<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<ul class=\"post-ul\">mockMessageSend\u30df\u30e5\u30fc\u30c6\u30fc\u30b7\u30e7\u30f3\u3092Altair GraphQL Client\u3067\u8a66\u884c\u3057\u305f\u6642\u306b\u6295\u7a3f\u3055\u308c\u305f\u300c&#8221;How are you doing? &#8221; + \u9023\u756a\u300d<\/ul>\n<p>\u90a3\u4e48\uff0c\u6211\u4eec\u6765\u70b9\u51fb\u5de6\u4e0b\u89d2\u7684\u201c\u53d1\u9001\u673a\u5668\u4eba\u6d88\u606f\u201d\u6309\u94ae\u8bd5\u8bd5\u770b\u3002<\/p>\n<div><img decoding=\"async\" class=\"post-images\" title=\"\" src=\"https:\/\/cdn.silicloud.com\/blog-img\/blog\/img\/657d82d5913a08637a6b1920\/349-0.png\" alt=\"\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8 2019-06-19 10.45.23.png\" \/><\/div>\n<p>\u300c\u4f60\u597d\u5417\uff1f+ \u8fde\u756a\u300d\u589e\u52a0\u4e86\u4e00\u6761\u3002<br \/>\n\u6211\u5c06\u4ece\u53f3\u4e0b\u89d2\u7684\u6d88\u606f\u8f93\u5165\u680f\u53d1\u5e03\u4e00\u4e2a&#8221;\u4f60\u597d\u4e16\u754c&#8221;\u3002<\/p>\n<div><img decoding=\"async\" class=\"post-images\" title=\"\" src=\"https:\/\/cdn.silicloud.com\/blog-img\/blog\/img\/657d82d5913a08637a6b1920\/351-0.png\" alt=\"\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8 2019-06-19 10.46.54.png\" \/><\/div>\n<div><img decoding=\"async\" class=\"post-images\" title=\"\" src=\"https:\/\/cdn.silicloud.com\/blog-img\/blog\/img\/657d82d5913a08637a6b1920\/352-0.png\" alt=\"\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8 2019-06-19 11.34.41.png\" \/><\/div>\n<p>\u6709\u4eba\u53d1\u5e03\u4e86\u300c\u4f60\u597d\uff0c\u4e16\u754c\u300d\u3002<br \/>\n\u73b0\u5728\u6211\u4eec\u6765\u8bd5\u8bd5\u8ba2\u9605\u3002<br \/>\n\u4ece\u63a7\u5236\u53f0\u8fdb\u5165Laradock\u7684\u5de5\u4f5c\u533a\uff0c\u7136\u540e\u8fdb\u5165psql\u3002<\/p>\n<pre class=\"post-pre\"><code>cd ~\/work\/laradock\r\ndocker-compose exec --user=laradock workspace bash\r\npsql -h postgres -U default demodb\r\n<\/code><\/pre>\n<p>\u6211\u5c06\u6267\u884c\u4ee5\u4e0b\u7684SQL\u8bed\u53e5\uff0c\u5e76\u5c1d\u8bd5\u663e\u793a\u6d88\u606f\u5217\u8868\u3002<\/p>\n<pre class=\"post-pre\"><code>SELECT * FROM apollo_demo.messages\r\n  ORDER BY id;\r\n<\/code><\/pre>\n<p>\u663e\u793a\u5982\u4e0b\u3002<\/p>\n<pre class=\"post-pre\"><code> id | channel_id | user_id |       content        |         date_added         | date_updated \r\n----+------------+---------+----------------------+----------------------------+--------------\r\n  1 | general    |       0 | Welcome to the chat! | 2019-06-18 07:10:27.671412 | \r\n  4 | general    |       1 | \u7f8e\u5473\u3057\u3044\uff01           | 2019-06-18 08:13:57.466309 | \r\n  5 | general    |       0 | How are you doing? 1 | 2019-06-18 08:26:07.808588 | \r\n  6 | general    |       0 | How are you doing? 2 | 2019-06-19 01:44:42.855632 | \r\n  7 | general    |       1 | \u3053\u3093\u306b\u3061\u306f\u4e16\u754c       | 2019-06-19 02:33:33.951225 | \r\n<\/code><\/pre>\n<p>\u6211\u5c06\u66f4\u65b0id\u4e3a5\u7684\u884c\uff0c\u5e76\u5c1d\u8bd5\u66f4\u65b0ApolloChat\u7684\u6570\u636e\u3002\u6211\u4f1a\u6267\u884c\u4ee5\u4e0bSQL\u8bed\u53e5\u3002<\/p>\n<pre class=\"post-pre\"><code>UPDATE apollo_demo.messages SET\r\n  content = 'Thank you.'\r\n  WHERE id = 5;\r\n<\/code><\/pre>\n<p>ApolloChat\u7684\u754c\u9762\u4e5f\u8fdb\u884c\u4e86\u66f4\u65b0\u3002<\/p>\n<div><img decoding=\"async\" class=\"post-images\" title=\"\" src=\"https:\/\/cdn.silicloud.com\/blog-img\/blog\/img\/657d82d5913a08637a6b1920\/362-0.png\" alt=\"\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8 2019-06-19 11.46.57.png\" \/><\/div>\n<p>\u6211\u5011\u5c07\u5617\u8a66\u522a\u9664id=5\u7684\u884c\u3002\u8acb\u57f7\u884c\u4ee5\u4e0bSQL\u8a9e\u53e5\u3002<\/p>\n<pre class=\"post-pre\"><code>DELETE FROM apollo_demo.messages\r\n  WHERE id = 5;\r\n<\/code><\/pre>\n<p>ApolloChat\u7684\u5c4f\u5e55\u4e0a\u4e5f\u5df2\u7ecf\u88ab\u5220\u9664\u4e86\u3002<\/p>\n<div><img decoding=\"async\" class=\"post-images\" title=\"\" src=\"https:\/\/cdn.silicloud.com\/blog-img\/blog\/img\/657d82d5913a08637a6b1920\/366-0.png\" alt=\"\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8 2019-06-19 11.48.33.png\" \/><\/div>\n<p>\u6211\u5011\u63a5\u4e0b\u4f86\u8981\u63d2\u5165\u4e00\u500b\u65b0\u7684\u884c\uff0c\u4e26\u57f7\u884c\u4ee5\u4e0b\u7684SQL\u8a9e\u53e5\u3002<\/p>\n<pre class=\"post-pre\"><code>INSERT INTO apollo_demo.messages (channel_id, user_id, content) VALUES\r\n    ('general', 1, 'Hello, World!');\r\n<\/code><\/pre>\n<p>\u8fd9\u4e2a\u529f\u80fd\u8fd8\u88ab\u6dfb\u52a0\u5230\u4e86ApolloChat\u7684\u754c\u9762\u4e0a\u3002<\/p>\n<div><img decoding=\"async\" class=\"post-images\" title=\"\" src=\"https:\/\/cdn.silicloud.com\/blog-img\/blog\/img\/657d82d5913a08637a6b1920\/370-0.png\" alt=\"\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8 2019-06-19 11.51.20.png\" \/><\/div>\n","protected":false},"excerpt":{"rendered":"<p>\u7531\u4e8e\u5f53\u524d\u60c5\u51b5\u4e0b\u4ee3\u7801\u89e3\u91ca\u4e0d\u8db3\uff0c\u6211\u4f1a\u5728\u4e4b\u540e\u589e\u8865\u8bf4\u660e\u3002 \u9019\u7bc7\u6587\u7ae0\u4e2d\u8981\u505a\u7684\u4e8b\u60c5 \u5c06\u9644\u5e26\u7684 &#8220;ApolloC [&hellip;]<\/p>\n","protected":false},"author":9,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-48064","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v21.5 (Yoast SEO v21.5) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>\u6211\u5c1d\u8bd5\u7528PostGraphile\u66ff\u6362\u4e86\u9644\u5e26\u7684GraphQL\u540e\u7aef\uff0c\u5e76\u4e14\u8fd8\u6709Vue-Apollo\u9644\u5e26\u7684\u6f14\u793a\u804a\u5929\u5e94\u7528\u7a0b\u5e8f&quot; ApolloChat&quot; - Blog - Silicon Cloud<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.silicloud.com\/zh\/blog\/\u6211\u5c1d\u8bd5\u7528postgraphile\u66ff\u6362\u4e86\u9644\u5e26\u7684graphql\u540e\u7aef\uff0c\u5e76\u4e14\u8fd8\u6709vue-apollo\u9644\u5e26\/\" \/>\n<meta property=\"og:locale\" content=\"zh_CN\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"\u6211\u5c1d\u8bd5\u7528PostGraphile\u66ff\u6362\u4e86\u9644\u5e26\u7684GraphQL\u540e\u7aef\uff0c\u5e76\u4e14\u8fd8\u6709Vue-Apollo\u9644\u5e26\u7684\u6f14\u793a\u804a\u5929\u5e94\u7528\u7a0b\u5e8f&quot; ApolloChat&quot;\" \/>\n<meta property=\"og:description\" content=\"\u7531\u4e8e\u5f53\u524d\u60c5\u51b5\u4e0b\u4ee3\u7801\u89e3\u91ca\u4e0d\u8db3\uff0c\u6211\u4f1a\u5728\u4e4b\u540e\u589e\u8865\u8bf4\u660e\u3002 \u9019\u7bc7\u6587\u7ae0\u4e2d\u8981\u505a\u7684\u4e8b\u60c5 \u5c06\u9644\u5e26\u7684 &#8220;ApolloC [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.silicloud.com\/zh\/blog\/\u6211\u5c1d\u8bd5\u7528postgraphile\u66ff\u6362\u4e86\u9644\u5e26\u7684graphql\u540e\u7aef\uff0c\u5e76\u4e14\u8fd8\u6709vue-apollo\u9644\u5e26\/\" \/>\n<meta property=\"og:site_name\" content=\"Blog - Silicon Cloud\" \/>\n<meta property=\"article:published_time\" content=\"2023-01-09T03:13:11+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2024-04-29T13:05:53+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/cdn.silicloud.com\/blog-img\/blog\/img\/657d82d5913a08637a6b1920\/31-0.png\" \/>\n<meta name=\"author\" content=\"\u6e05, \u626c\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"\u4f5c\u8005\" \/>\n\t<meta name=\"twitter:data1\" content=\"\u6e05, \u626c\" \/>\n\t<meta name=\"twitter:label2\" content=\"\u9884\u8ba1\u9605\u8bfb\u65f6\u95f4\" \/>\n\t<meta name=\"twitter:data2\" content=\"33 \u5206\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.silicloud.com\/zh\/blog\/%e6%88%91%e5%b0%9d%e8%af%95%e7%94%a8postgraphile%e6%9b%bf%e6%8d%a2%e4%ba%86%e9%99%84%e5%b8%a6%e7%9a%84graphql%e5%90%8e%e7%ab%af%ef%bc%8c%e5%b9%b6%e4%b8%94%e8%bf%98%e6%9c%89vue-apollo%e9%99%84%e5%b8%a6\/\",\"url\":\"https:\/\/www.silicloud.com\/zh\/blog\/%e6%88%91%e5%b0%9d%e8%af%95%e7%94%a8postgraphile%e6%9b%bf%e6%8d%a2%e4%ba%86%e9%99%84%e5%b8%a6%e7%9a%84graphql%e5%90%8e%e7%ab%af%ef%bc%8c%e5%b9%b6%e4%b8%94%e8%bf%98%e6%9c%89vue-apollo%e9%99%84%e5%b8%a6\/\",\"name\":\"\u6211\u5c1d\u8bd5\u7528PostGraphile\u66ff\u6362\u4e86\u9644\u5e26\u7684GraphQL\u540e\u7aef\uff0c\u5e76\u4e14\u8fd8\u6709Vue-Apollo\u9644\u5e26\u7684\u6f14\u793a\u804a\u5929\u5e94\u7528\u7a0b\u5e8f\\\" ApolloChat\\\" - Blog - Silicon Cloud\",\"isPartOf\":{\"@id\":\"https:\/\/www.silicloud.com\/zh\/blog\/#website\"},\"datePublished\":\"2023-01-09T03:13:11+00:00\",\"dateModified\":\"2024-04-29T13:05:53+00:00\",\"author\":{\"@id\":\"https:\/\/www.silicloud.com\/zh\/blog\/#\/schema\/person\/cb5556d2501da73d864cac945e8d9461\"},\"breadcrumb\":{\"@id\":\"https:\/\/www.silicloud.com\/zh\/blog\/%e6%88%91%e5%b0%9d%e8%af%95%e7%94%a8postgraphile%e6%9b%bf%e6%8d%a2%e4%ba%86%e9%99%84%e5%b8%a6%e7%9a%84graphql%e5%90%8e%e7%ab%af%ef%bc%8c%e5%b9%b6%e4%b8%94%e8%bf%98%e6%9c%89vue-apollo%e9%99%84%e5%b8%a6\/#breadcrumb\"},\"inLanguage\":\"zh-Hans\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.silicloud.com\/zh\/blog\/%e6%88%91%e5%b0%9d%e8%af%95%e7%94%a8postgraphile%e6%9b%bf%e6%8d%a2%e4%ba%86%e9%99%84%e5%b8%a6%e7%9a%84graphql%e5%90%8e%e7%ab%af%ef%bc%8c%e5%b9%b6%e4%b8%94%e8%bf%98%e6%9c%89vue-apollo%e9%99%84%e5%b8%a6\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.silicloud.com\/zh\/blog\/%e6%88%91%e5%b0%9d%e8%af%95%e7%94%a8postgraphile%e6%9b%bf%e6%8d%a2%e4%ba%86%e9%99%84%e5%b8%a6%e7%9a%84graphql%e5%90%8e%e7%ab%af%ef%bc%8c%e5%b9%b6%e4%b8%94%e8%bf%98%e6%9c%89vue-apollo%e9%99%84%e5%b8%a6\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"\u9996\u9875\",\"item\":\"https:\/\/www.silicloud.com\/zh\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"\u6211\u5c1d\u8bd5\u7528PostGraphile\u66ff\u6362\u4e86\u9644\u5e26\u7684GraphQL\u540e\u7aef\uff0c\u5e76\u4e14\u8fd8\u6709Vue-Apollo\u9644\u5e26\u7684\u6f14\u793a\u804a\u5929\u5e94\u7528\u7a0b\u5e8f&#8221; ApolloChat&#8221;\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.silicloud.com\/zh\/blog\/#website\",\"url\":\"https:\/\/www.silicloud.com\/zh\/blog\/\",\"name\":\"Blog - Silicon Cloud\",\"description\":\"\",\"inLanguage\":\"zh-Hans\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.silicloud.com\/zh\/blog\/#\/schema\/person\/cb5556d2501da73d864cac945e8d9461\",\"name\":\"\u6e05, \u626c\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"zh-Hans\",\"@id\":\"https:\/\/www.silicloud.com\/zh\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/32a4239de8ff29adace466261d309424a1e5fe9f7e3036bf89fe03f2e3dbe717?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/32a4239de8ff29adace466261d309424a1e5fe9f7e3036bf89fe03f2e3dbe717?s=96&d=mm&r=g\",\"caption\":\"\u6e05, \u626c\"},\"url\":\"https:\/\/www.silicloud.com\/zh\/blog\/author\/qingyang\/\"},{\"@type\":\"ImageObject\",\"inLanguage\":\"zh-Hans\",\"@id\":\"https:\/\/www.silicloud.com\/zh\/blog\/%e6%88%91%e5%b0%9d%e8%af%95%e7%94%a8postgraphile%e6%9b%bf%e6%8d%a2%e4%ba%86%e9%99%84%e5%b8%a6%e7%9a%84graphql%e5%90%8e%e7%ab%af%ef%bc%8c%e5%b9%b6%e4%b8%94%e8%bf%98%e6%9c%89vue-apollo%e9%99%84%e5%b8%a6\/#local-main-organization-logo\",\"url\":\"\",\"contentUrl\":\"\",\"caption\":\"Blog - Silicon Cloud\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"\u6211\u5c1d\u8bd5\u7528PostGraphile\u66ff\u6362\u4e86\u9644\u5e26\u7684GraphQL\u540e\u7aef\uff0c\u5e76\u4e14\u8fd8\u6709Vue-Apollo\u9644\u5e26\u7684\u6f14\u793a\u804a\u5929\u5e94\u7528\u7a0b\u5e8f\" ApolloChat\" - Blog - Silicon Cloud","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.silicloud.com\/zh\/blog\/\u6211\u5c1d\u8bd5\u7528postgraphile\u66ff\u6362\u4e86\u9644\u5e26\u7684graphql\u540e\u7aef\uff0c\u5e76\u4e14\u8fd8\u6709vue-apollo\u9644\u5e26\/","og_locale":"zh_CN","og_type":"article","og_title":"\u6211\u5c1d\u8bd5\u7528PostGraphile\u66ff\u6362\u4e86\u9644\u5e26\u7684GraphQL\u540e\u7aef\uff0c\u5e76\u4e14\u8fd8\u6709Vue-Apollo\u9644\u5e26\u7684\u6f14\u793a\u804a\u5929\u5e94\u7528\u7a0b\u5e8f\" ApolloChat\"","og_description":"\u7531\u4e8e\u5f53\u524d\u60c5\u51b5\u4e0b\u4ee3\u7801\u89e3\u91ca\u4e0d\u8db3\uff0c\u6211\u4f1a\u5728\u4e4b\u540e\u589e\u8865\u8bf4\u660e\u3002 \u9019\u7bc7\u6587\u7ae0\u4e2d\u8981\u505a\u7684\u4e8b\u60c5 \u5c06\u9644\u5e26\u7684 &#8220;ApolloC [&hellip;]","og_url":"https:\/\/www.silicloud.com\/zh\/blog\/\u6211\u5c1d\u8bd5\u7528postgraphile\u66ff\u6362\u4e86\u9644\u5e26\u7684graphql\u540e\u7aef\uff0c\u5e76\u4e14\u8fd8\u6709vue-apollo\u9644\u5e26\/","og_site_name":"Blog - Silicon Cloud","article_published_time":"2023-01-09T03:13:11+00:00","article_modified_time":"2024-04-29T13:05:53+00:00","og_image":[{"url":"https:\/\/cdn.silicloud.com\/blog-img\/blog\/img\/657d82d5913a08637a6b1920\/31-0.png"}],"author":"\u6e05, \u626c","twitter_card":"summary_large_image","twitter_misc":{"\u4f5c\u8005":"\u6e05, \u626c","\u9884\u8ba1\u9605\u8bfb\u65f6\u95f4":"33 \u5206"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.silicloud.com\/zh\/blog\/%e6%88%91%e5%b0%9d%e8%af%95%e7%94%a8postgraphile%e6%9b%bf%e6%8d%a2%e4%ba%86%e9%99%84%e5%b8%a6%e7%9a%84graphql%e5%90%8e%e7%ab%af%ef%bc%8c%e5%b9%b6%e4%b8%94%e8%bf%98%e6%9c%89vue-apollo%e9%99%84%e5%b8%a6\/","url":"https:\/\/www.silicloud.com\/zh\/blog\/%e6%88%91%e5%b0%9d%e8%af%95%e7%94%a8postgraphile%e6%9b%bf%e6%8d%a2%e4%ba%86%e9%99%84%e5%b8%a6%e7%9a%84graphql%e5%90%8e%e7%ab%af%ef%bc%8c%e5%b9%b6%e4%b8%94%e8%bf%98%e6%9c%89vue-apollo%e9%99%84%e5%b8%a6\/","name":"\u6211\u5c1d\u8bd5\u7528PostGraphile\u66ff\u6362\u4e86\u9644\u5e26\u7684GraphQL\u540e\u7aef\uff0c\u5e76\u4e14\u8fd8\u6709Vue-Apollo\u9644\u5e26\u7684\u6f14\u793a\u804a\u5929\u5e94\u7528\u7a0b\u5e8f\" ApolloChat\" - Blog - Silicon Cloud","isPartOf":{"@id":"https:\/\/www.silicloud.com\/zh\/blog\/#website"},"datePublished":"2023-01-09T03:13:11+00:00","dateModified":"2024-04-29T13:05:53+00:00","author":{"@id":"https:\/\/www.silicloud.com\/zh\/blog\/#\/schema\/person\/cb5556d2501da73d864cac945e8d9461"},"breadcrumb":{"@id":"https:\/\/www.silicloud.com\/zh\/blog\/%e6%88%91%e5%b0%9d%e8%af%95%e7%94%a8postgraphile%e6%9b%bf%e6%8d%a2%e4%ba%86%e9%99%84%e5%b8%a6%e7%9a%84graphql%e5%90%8e%e7%ab%af%ef%bc%8c%e5%b9%b6%e4%b8%94%e8%bf%98%e6%9c%89vue-apollo%e9%99%84%e5%b8%a6\/#breadcrumb"},"inLanguage":"zh-Hans","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.silicloud.com\/zh\/blog\/%e6%88%91%e5%b0%9d%e8%af%95%e7%94%a8postgraphile%e6%9b%bf%e6%8d%a2%e4%ba%86%e9%99%84%e5%b8%a6%e7%9a%84graphql%e5%90%8e%e7%ab%af%ef%bc%8c%e5%b9%b6%e4%b8%94%e8%bf%98%e6%9c%89vue-apollo%e9%99%84%e5%b8%a6\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.silicloud.com\/zh\/blog\/%e6%88%91%e5%b0%9d%e8%af%95%e7%94%a8postgraphile%e6%9b%bf%e6%8d%a2%e4%ba%86%e9%99%84%e5%b8%a6%e7%9a%84graphql%e5%90%8e%e7%ab%af%ef%bc%8c%e5%b9%b6%e4%b8%94%e8%bf%98%e6%9c%89vue-apollo%e9%99%84%e5%b8%a6\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"\u9996\u9875","item":"https:\/\/www.silicloud.com\/zh\/blog\/"},{"@type":"ListItem","position":2,"name":"\u6211\u5c1d\u8bd5\u7528PostGraphile\u66ff\u6362\u4e86\u9644\u5e26\u7684GraphQL\u540e\u7aef\uff0c\u5e76\u4e14\u8fd8\u6709Vue-Apollo\u9644\u5e26\u7684\u6f14\u793a\u804a\u5929\u5e94\u7528\u7a0b\u5e8f&#8221; ApolloChat&#8221;"}]},{"@type":"WebSite","@id":"https:\/\/www.silicloud.com\/zh\/blog\/#website","url":"https:\/\/www.silicloud.com\/zh\/blog\/","name":"Blog - Silicon Cloud","description":"","inLanguage":"zh-Hans"},{"@type":"Person","@id":"https:\/\/www.silicloud.com\/zh\/blog\/#\/schema\/person\/cb5556d2501da73d864cac945e8d9461","name":"\u6e05, \u626c","image":{"@type":"ImageObject","inLanguage":"zh-Hans","@id":"https:\/\/www.silicloud.com\/zh\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/32a4239de8ff29adace466261d309424a1e5fe9f7e3036bf89fe03f2e3dbe717?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/32a4239de8ff29adace466261d309424a1e5fe9f7e3036bf89fe03f2e3dbe717?s=96&d=mm&r=g","caption":"\u6e05, \u626c"},"url":"https:\/\/www.silicloud.com\/zh\/blog\/author\/qingyang\/"},{"@type":"ImageObject","inLanguage":"zh-Hans","@id":"https:\/\/www.silicloud.com\/zh\/blog\/%e6%88%91%e5%b0%9d%e8%af%95%e7%94%a8postgraphile%e6%9b%bf%e6%8d%a2%e4%ba%86%e9%99%84%e5%b8%a6%e7%9a%84graphql%e5%90%8e%e7%ab%af%ef%bc%8c%e5%b9%b6%e4%b8%94%e8%bf%98%e6%9c%89vue-apollo%e9%99%84%e5%b8%a6\/#local-main-organization-logo","url":"","contentUrl":"","caption":"Blog - Silicon Cloud"}]}},"_links":{"self":[{"href":"https:\/\/www.silicloud.com\/zh\/blog\/wp-json\/wp\/v2\/posts\/48064","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.silicloud.com\/zh\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.silicloud.com\/zh\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.silicloud.com\/zh\/blog\/wp-json\/wp\/v2\/users\/9"}],"replies":[{"embeddable":true,"href":"https:\/\/www.silicloud.com\/zh\/blog\/wp-json\/wp\/v2\/comments?post=48064"}],"version-history":[{"count":2,"href":"https:\/\/www.silicloud.com\/zh\/blog\/wp-json\/wp\/v2\/posts\/48064\/revisions"}],"predecessor-version":[{"id":87639,"href":"https:\/\/www.silicloud.com\/zh\/blog\/wp-json\/wp\/v2\/posts\/48064\/revisions\/87639"}],"wp:attachment":[{"href":"https:\/\/www.silicloud.com\/zh\/blog\/wp-json\/wp\/v2\/media?parent=48064"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.silicloud.com\/zh\/blog\/wp-json\/wp\/v2\/categories?post=48064"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.silicloud.com\/zh\/blog\/wp-json\/wp\/v2\/tags?post=48064"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}