- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
首先,我对在 Stack Overflow 上实际发帖还比较陌生,但我当然会尽最大努力在此处提供所有相关信息,并在找到解决方案后分享解决方案,因为我可以想象更多人可能会遇到这个问题。
因此,我们从一个具有多个小型微服务作为后端的系统开始,我们发现 Apollo 服务器能够从 graphql 端点检索模式并将它们拼接在一起,这样我们就有了一个很好的入口点。我们已经做到了,但 apollo 服务器对整体架构没有任何帮助。那是我们发现 NestJS 的时候,因为我们在前端使用了 angular,而 NestJS 与它非常相似,看起来非常适合。
但我们遇到的问题是我们似乎无法使以下功能正常工作:- 我想要一个模块,其中包含一个可以提供多个端点的服务(uri 到微服务)- 有了给定的点,服务应该从这些端点检索 graphQL 模式并将它们放入 RemoteExecutableSchemas,然后合并它们。- 合并它们并使用(远程)链接信息制作 1 个大模式之后,以便 graphQL 知道从哪里获取数据。- 发生这种情况后,我们想添加一些拼接,以便所有关系都存在(但这不是我的问题所在)
我一直在浏览官方文档 ( https://docs.nestjs.com/graphql/quick-start ) 以及它们的示例 ( https://github.com/nestjs/nest/tree/master/sample/12-graphql-apollo ),当然还检查了 github 项目 ( https://github.com/nestjs/graphql ) 并一直在这个 repo 中查看以查看代码在后台执行。
我们已经尝试了几种方法来动态获取它们,但无法在实例化之前将模式放入 GraphQLModule 中。然后我们认为让服务从端点检索 graphqlSchema 并使用实际工作的 printSchema(schema) 将其写入文件可能是可以接受的,但后来我丢失了有效的链接信息,使其成为本地模式而不是远程模式.现在我们提出了以下内容,但再次陷入困境。
让我们从我的 package.json 中的一小段开始,这样人们就知道版本了:)
"dependencies": {
"@nestjs/common": "^5.4.0",
"@nestjs/core": "^5.4.0",
"@nestjs/graphql": "^5.5.1",
"apollo-link-http": "^1.5.9",
"apollo-server-express": "^2.3.2",
"graphql": "^14.1.1",
"reflect-metadata": "^0.1.12",
"rimraf": "^2.6.2",
"rxjs": "^6.2.2",
"typescript": "^3.0.1"
},
"devDependencies": {
"@nestjs/testing": "^5.1.0",
"@types/express": "^4.16.0",
"@types/jest": "^23.3.1",
"@types/node": "^10.7.1",
"@types/supertest": "^2.0.5",
"jest": "^23.5.0",
"nodemon": "^1.18.3",
"prettier": "^1.14.2",
"supertest": "^3.1.0",
"ts-jest": "^23.1.3",
"ts-loader": "^4.4.2",
"ts-node": "^7.0.1",
"tsconfig-paths": "^3.5.0",
"tslint": "5.11.0"
},
所以,目前我有一个模式处理程序模块,如下所示:
@Module({
imports: [GraphQLModule.forRootAsync({
useClass: GqlConfigService
})],
controllers: [SchemaHandlerController],
providers: [SchemaFetcherService, SchemaSticherService, GqlConfigService]
})
export class SchemaHandlerModule {
}
所以在这里我们导入 GraphQLModule 并让它使用 gql-config 服务来处理给它 GraphQLModuleOptions。
gql-config 服务如下所示:
@Injectable()
export class GqlConfigService implements GqlOptionsFactory {
async createGqlOptions(): Promise<GqlModuleOptions> {
try{
const countrySchema = this.createCountrySchema();
return {
typeDefs: [countrySchema]
};
} catch(err) {
console.log(err);
return {};
}
}
所以我正在异步创建 GqlModuleOptions 并等待结果。 createCountrySchema 函数如下所示:
public async createCountrySchema() : GraphQLSchema{
const uri = 'https://countries.trevorblades.com/Graphql';
try {
const link = new HttpLink({
uri: uri,
fetch
});
const remoteSchema = await introspectSchema(link);
return makeRemoteExecutableSchema({
schema: remoteSchema,
link
});
} catch (err) {
console.log('ERROR: exception when trying to connect to ' + uri + ' Error Message: ' + err);
}
};
为了 POC,我只获得了一个简单的公共(public) graphQL API 作为端点。此函数返回一个 GraphQLSchema 对象,然后我想将其添加(以某种方式)到 GqlOptions 并使其在 Playground 上可见。 我们还尝试让 createCountrySchema 返回一个 Promise 并在调用 createGqlOptions 中的函数时等待它,但这似乎没有什么不同。
我们得到的实际错误看起来像这样:
[Nest] 83 - 2/1/2019, 2:10:57 PM [RoutesResolver] SchemaHandlerController {/schema-handler}: +1ms
apollo_1 | (node:83) UnhandledPromiseRejectionWarning: Syntax Error: Unexpected [
apollo_1 |
apollo_1 | GraphQL request (2:9)
apollo_1 | 1:
apollo_1 | 2: [object Promise]
apollo_1 | ^
apollo_1 | 3:
apollo_1 |
apollo_1 | at syntaxError (/opt/node_modules/graphql/error/syntaxError.js:24:10)
apollo_1 | at unexpected (/opt/node_modules/graphql/language/parser.js:1483:33)
apollo_1 | at parseDefinition (/opt/node_modules/graphql/language/parser.js:155:9)
apollo_1 | at many (/opt/node_modules/graphql/language/parser.js:1513:16)
apollo_1 | at parseDocument (/opt/node_modules/graphql/language/parser.js:115:18)
apollo_1 | at parse (/opt/node_modules/graphql/language/parser.js:50:10)
apollo_1 | at parseDocument (/opt/node_modules/graphql-tag/src/index.js:129:16)
apollo_1 | at Object.gql (/opt/node_modules/graphql-tag/src/index.js:170:10)
apollo_1 | at GraphQLFactory.<anonymous> (/opt/node_modules/@nestjs/graphql/dist/graphql.factory.js:48:55)
apollo_1 | at Generator.next (<anonymous>)
apollo_1 | (node:83) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
apollo_1 | (node:83) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
我认为我很接近这种方法,但我不太确定。我收到的错误指出所有 Promises 都必须用 try/catch 处理,这样我们就不会得到未处理的 Promise,我相信我在任何地方都这样做,所以我不明白这个错误是从哪里来的......
如果有人有任何指示、解决方案或建议,我将非常非常高兴。一个多星期以来,我一直在努力让我们想要的功能安装在 nestjs 中,并且已经看到了大量的示例、片段和讨论,但我找不到一个示例来缝合远程模式并将它们交还给 nestjs。
如果对此有任何评论,我将不胜感激,并致以亲切的问候,蒂尔德
最佳答案
我已经使用转换方法解决了模式拼接问题。
看src/graphql.config/graphql.config.service.ts
here my code
link for the test
import { Injectable } from '@nestjs/common';
import { GqlOptionsFactory, GqlModuleOptions } from '@nestjs/graphql';
import * as ws from 'ws';
import {
makeRemoteExecutableSchema,
mergeSchemas,
introspectSchema
} from 'graphql-tools';
import { HttpLink } from 'apollo-link-http';
import nodeFetch from 'node-fetch';
import { split, from, NextLink, Observable, FetchResult, Operation } from 'apollo-link';
import { getMainDefinition } from 'apollo-utilities';
import { OperationTypeNode, buildSchema as buildSchemaGraphql, GraphQLSchema, printSchema } from 'graphql';
import { setContext } from 'apollo-link-context';
import { SubscriptionClient, ConnectionContext } from 'subscriptions-transport-ws';
import * as moment from 'moment';
import { extend } from 'lodash';
import { ConfigService } from '../config';
declare const module: any;
interface IDefinitionsParams {
operation?: OperationTypeNode;
kind: 'OperationDefinition' | 'FragmentDefinition';
}
interface IContext {
graphqlContext: {
subscriptionClient: SubscriptionClient,
};
}
@Injectable()
export class GqlConfigService implements GqlOptionsFactory {
private remoteLink: string = 'https://countries.trevorblades.com';
constructor(
private readonly config: ConfigService
) {}
async createGqlOptions(): Promise<GqlModuleOptions> {
const remoteExecutableSchema = await this.createRemoteSchema();
return {
autoSchemaFile: 'schema.gql',
transformSchema: async (schema: GraphQLSchema) => {
return mergeSchemas({
schemas: [
schema,
remoteExecutableSchema
]
});
},
debug: true,
playground: {
env: this.config.environment,
endpoint: '/graphql',
subscriptionEndpoint: '/subscriptions',
settings: {
'general.betaUpdates': false,
'editor.theme': 'dark' as any,
'editor.reuseHeaders': true,
'tracing.hideTracingResponse': true,
'editor.fontSize': 14,
// tslint:disable-next-line:quotemark
'editor.fontFamily': "'Source Code Pro', 'Consolas', 'Inconsolata', 'Droid Sans Mono', 'Monaco', monospace",
'request.credentials': 'include',
},
},
tracing: true,
installSubscriptionHandlers: true,
introspection: true,
subscriptions: {
path: '/subscriptions',
keepAlive: 10000,
onConnect: async (connectionParams, webSocket: any, context) => {
const subscriptionClient = new SubscriptionClient(this.config.get('HASURA_WS_URI'), {
connectionParams: {
...connectionParams,
...context.request.headers
},
reconnect: true,
lazy: true,
}, ws);
return {
subscriptionClient,
};
},
async onDisconnect(webSocket, context: ConnectionContext) {
const { subscriptionClient } = await context.initPromise;
if (subscriptionClient) {
subscriptionClient.close();
}
},
},
context(context) {
const contextModified: any = {
userRole: 'anonymous',
currentUTCTime: moment().utc().format()
};
if (context && context.connection && context.connection.context) {
contextModified.subscriptionClient = context.connection.context.subscriptionClient;
}
return contextModified;
},
};
}
private wsLink(operation: Operation, forward?: NextLink): Observable<FetchResult> | null {
const context = operation.getContext();
const { graphqlContext: { subscriptionClient } }: any = context;
return subscriptionClient.request(operation);
}
private async createRemoteSchema(): Promise<GraphQLSchema> {
const httpLink = new HttpLink({
uri: this.remoteLink,
fetch: nodeFetch as any,
});
const remoteIntrospectedSchema = await introspectSchema(httpLink);
const remoteSchema = printSchema(remoteIntrospectedSchema);
const link = split(
({ query }) => {
const { kind, operation }: IDefinitionsParams = getMainDefinition(query);
return kind === 'OperationDefinition' && operation === 'subscription';
},
this.wsLink,
httpLink,
);
const contextLink = setContext((request, prevContext) => {
extend(prevContext.headers, {
'X-hasura-Role': prevContext.graphqlContext.userRole,
'X-Hasura-Utc-Time': prevContext.graphqlContext.currentUTCTime,
});
return prevContext;
});
const buildedHasuraSchema = buildSchemaGraphql(remoteSchema);
const remoteExecutableSchema = makeRemoteExecutableSchema({
link: from([contextLink, link]),
schema: buildedHasuraSchema,
});
return remoteExecutableSchema;
}
}
关于typescript - 如何使用 Nestjs 和 apollo 服务器拼接多个远程模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54481645/
有没有全局loading react-apollo 客户端在任何地方都可以使用标志吗?我有一个“页面包装器”组件,我想在所有子组件都收到它们的数据后应用 ui 效果。 我已经使用 redux 设置了
我试图通过 React 了解 Apollo。如何将状态添加到我本地的 Apollo 商店? 我正在使用 meteor 。它在客户端提供了一个功能来测试用户是否登录并返回他们的 id 如果他们是 Met
我有一个关于 cacheRedirects 的问题在 Apollo 中,我有项目概览和详细 View 。我希望将 detailview 缓存重定向到概览中的一个项目(如文档中的 the book ex
我们可以使用查询和变异向服务器发出一些请求。在这些查询中,我们可以传递一些参数,并且在这两种情况下我们都会从服务器获得一些结果。唯一的一个强制性区别是,我们可以从 props 中调用突变,例如“thi
我正在使用 @apollo/client 实现,但我没有看到任何完整的 @apollo/client 示例与 react . 如果我搜索,我会得到示例 apollo-client和 apollo bo
我正在使用 apollo-client、apollo-link 和 react-apollo,我想完全禁用缓存,但不知道该怎么做。 我看了apollo-cache-inmemory的来源,它有一个 c
我正在构建基于 JWT 的身份验证系统. JWT已过期。当JWT过期,我 catch JWT使用 apollo-link-error 的过期错误.我想调用 apolloClient.resetStor
Apollo 文档 discusses the use of cacheRedirects 告诉 Apollo 如何从其他查询访问缓存中的数据。 它给出了一个例子: In some cases, a
react-apollo的Query组件可以使用Recompose吗? ? https://www.apollographql.com/docs/react/essentials/queries.ht
我正在尝试从“ apollo-server”更新:“^2.9.4”和 “阿波罗服务器 express ” :“^2.9.4”到 2.12.0 版本 在 typescript 中,在应用程序的构建过程中
http://dev.apollodata.com/react/mutations.html 我正在尝试使用 optimisticResponse,但我很困惑...无法让它在我的本地运行。 我的问题是
我正在制作一个社交网站。当任何用户在站点上更新或创建新内容时,我需要查看站点的任何其他用户来查看更改更新。 我有一些需要低延迟的评论,因此建议为此订阅。 我也有事件,但这些不需要这么低的延迟。每 10
我在一个简单的 React 应用程序中使用最新版本的 Apollo Client,我试图从用于显示返回的记录集大小的响应中提取 header 值。 我很欣赏这不是提供结果集大小的最优雅的方式,但这就是
我想在突变后使用乐观 UI 更新:https://www.apollographql.com/docs/react/basics/mutations.html 我对“乐观响应”和“更新”之间的关系感到
在 Apollo 服务器中,当客户端用户订阅订阅(使用 WebSocket)时,我们可以使用订阅解析器检测到这一点。 但是有没有办法检测取消订阅? 我看到 WebSocket 发送了一个 {"id":
我创建这个问题是为了防止有人对如何在 Apollo 中添加 union/多态类型感到好奇。希望这会让他们更容易。 在此示例中,我希望响应为 Worksheet 或 ApiError // typede
我的项目目录结构是这样的: - schema.graphql - package.json - packages -- types --- package.json --- src ---- grap
我想知道当订阅接收到新数据时是否有一种优雅的方式来触发react-apollo中查询的重新获取(数据在这里并不重要,将与前一个相同)。我只是在这里使用订阅作为通知触发器,告诉 Query 重新获取。
我使用 Apollo、React 和 Graphcool。我有一个查询来获取登录的用户 ID: const LoginServerQuery = gql` query LoginServerQ
出于分析目的,我想跟踪所有 graphql 操作的客户端(包括 @client 操作)。我无法在 API 中找到合适的选项,想知道这在 apollo 客户端级别是否可行,或者我是否需要引入一些代理来拦
我是一名优秀的程序员,十分优秀!