Apollo Server: dataSources vs context to use it with database - apollo-server

After see this documentation I'm not sure if to use a simple context, as I has done other times, or if it is better to use dataSources to handle the database.
DataSource is the correct way to comunicate with the database or it is better use it only to comunicate with a REST API?
Basically, does it have any advantage to use dataSources vs context in this case?

I think it's better to go with DataSource (as the name suggests) and it might be easy to add a caching layer on top of it. You can create a DBDataSource class extending the DataSource class since Apollo doesn't provide any DBDataSource class.
DataSource class is a generic Apollo data source class whereas RESTDataSource class that is responsible for fetching data from a REST API.
So to fetch data from rest APIs it's better to go with RESTDataSource.
Build a custom data source
Apollo doesn't have support for a SQL data source yet (although we'd love to help guide you if you're interested in contributing), so we will need to create a custom data source for our database by extending the generic Apollo data source class. You can create your own with the apollo-datasource package.
Here are some of the core concepts for creating your own data source:
The initialize method: You'll need to implement this method if you want to pass in any configuration options to your class. Here, we're using this method to access our graph API's context.
this.context: A graph API's context is an object that's shared among every resolver in a GraphQL request. We're going to explain this in more detail in the next section. Right now, all you need to know is that the context is useful for storing user information.
Caching: While the REST data source comes with its own built-in cache, the generic data source does not. You can use our cache primitives to build your own, however!

I generally keep resolvers very thin, pass the incoming args to dataSources and they make models and loaders communicate. Most of the logic and validations are in models. You may of course make your own rules like "don't call another dataSource inside a dataSource. Pass their outpus to make them communicate through resolvers" etc. This is only an example of course. Not a strict rule I follow.
There may be better solutions and actually for simple things, using models directly is much more straightforward. But dataSources help you keep things organised and if you want to add caching etc, you can create a BaseDataSource, put all the logic in that and just extend it with other dataSources.

If you look at the documentation that your pointed out. You add data Sources when initializing Apollo Server like this :
const server = new ApolloServer({
typeDefs,
dataSources: () => ({
launchAPI: new LaunchAPI(),
userAPI: new UserAPI({ store })
})
});
and it is because of this dataSources becomes the part of context. If you remember you do the de-structuring of context to expose dataSources as shown here
module.exports = {
Query: {
launches: (_, __, { dataSources }) =>
dataSources.launchAPI.getAllLaunches(),
launch: (_, { id }, { dataSources }) =>
dataSources.launchAPI.getLaunchById({ launchId: id }),
me: (_, __, { dataSources }) => dataSources.userAPI.findOrCreateUser()
}
};
If you want to access the instance of a dataSource such as UserAPI or LaunchAPI, you will have to do that with dataSources.userAPI

Related

How to access the complete runtime GraphQLSchema object with Netflix DGS?

I need to access the complete GraphQLSchema object outside GraphQL request handling. When I used graphql-java directly I was in full control and had the control of this. Right now I need to accomplish the same with Netflix DGS and can't find how to do so (and keep up with runtime changes/reloading later).
For more context - I need to do a few things with this - one is to create a complete, downloadable SDL version of the schema (i.e. not the same as federation _service { sdl }) and also gather and expose some directive-driven metadata differently, since I can't introspect it :( from the client...
You can use DgsDataFetchingEnvironment
#DgsQuery
public Student getStudent(DgsDataFetchingEnvironment dfe) {
GraphQLSchema schema = dfe.getGraphQLSchema();
//if you want to know which fields are selected in query
DataFetchingFieldSelectionSet fields = dfe.getSelectionSet();
}

GraphQL SPQR fetches all fields on the server side

I'm new to Spring Boot and I just started using graphql-spqr for Spring Boot since it allows for easy bootstrapping of Java projects.
However, as per my understanding, GraphQL basically allows the fetching of selected fields from the database. As per the examples, I've seen, this type of selection in the graphql-spqr library happens on the client side. Is there a way to do selection both client-side and server-side so as to speed up the queries?
I've looked into EntityGraph examples for GraphQL but they are mostly implemented for complex queries that involve JOINs. However, nothing exists for simple queries like findAll(), findById() etc.
I would like to use findAll() with the server fetching only the fields as requested by the client. How can I do that?
What was said in the comments is correct: GraphQL (and hence SPQR, as it's merely a tool to hook the schema up) does not know anything about SQL, databases, JOINs or anything else. It's a communication protocol, the rest is up to you.
As for your situation, you'd have to inject the subselection into the resolver and pass it down to SQL. In the simplest case, it can look like this (in pseudo code):
public List<Book> books(#GraphQLEnvironment Set<String> fields) {
//pass the requested field names further
return database.query("SELECT " + fields + " FROM book");
}
You can inject ResolutionEnvironment using the same annotation, in case you need the full context.

Mutation Input Split Across Two Apollo GraphQL Federated Services

I have a mutation in my Graph that uses two datasources. We are migrating one of the datasources out to a federated service. That datasource in the federated service uses a portion of the input from the mutation being called. The originating service also uses a portion of the input. For example:
mutation($verifyUserInput: VerifyUserInput!) {
verifyUser(input: $verifyUserInput) {
user {
specialId
}
otherServiceField
}
}
I need to pass part of the VerifyUserInput to the other service that is not the PK for the entity. I can't find the input in the __resolveReference function reference (obviously since reference only passes the typename and PK), context, or info args. Is the original input to the mutation available in the federated service? If so how can I retrieve it?
I ended up sniffing for the exact input I needed on every request in the gateway and adding that value to a header so it could propagate across the services. This definitely feels like a hack. I assume there is a more correct way to do this in Apollo Federation.

How to inject ConnectedSocket into a service in NestJS

I want to inject #ConnectedSocket into a nestjs service that provides an interface for event emitting. The goal is pretty clear - to avoid calling emit with raw event name and any data. SocketFacadeHost below is an example of such a service.
So far the only way I could think of is replacing each emit call by wrapping socket with a facade service factory - SocketFacade.
socket.facade.ts
class SocketFacadeHost {
constructor(private client: Socket) { }
syncDSL(dsl: CoreDSL): void {
this.client.emit('syncDSL', dsl);
}
}
export const SocketFacade = (client: Socket) => new SocketFacadeHost(client);
socket.gateway.ts
...
#SubscribeMessage('bind')
async bindEnvironment(
#MessageBody() data: BindingDTO,
#ConnectedSocket() client: Socket,
): Promise<void> {
const { pageUUID } = data;
const page = await this.pagesService.getByUUID(pageUUID);
SocketFacade(client).syncDSL(page.coreDSL);
}
But that is still kinda hackish. Any straightforward way of doing this?
Something like extending #ConnectedSocket decorator or WebSocketGateway?
--- Edit 1 ---
After going through tons of documentation, examples and issues on the subject, I believe the problem lies in the idea of making the service aware of context, that is by default incorrect. Thus guards, interceptors, pipes and all the ExecutionContext decorators only work on the controller/gateway level. Service should either be stateless or instantiated all over on each call (which is controlled by the #Injectable scope).
So my approach of using the facade factory is OK.
But then again, it would be convenient if it was possible to 'replace' #ConnectedSocket() decorator by a decorator that returns wrapped socket with an interface, that wouldn't mess with clean mvc.
In the end, I settled with hacking my own 'dependency injection'. Since request scope is not an option for WebSocketGateway, I decided to manually instantiate and cleanup the needed services and store them within Socket client with additional storage interface.
This way I have ConnectedSocket-scoped classes that can communicate to one another, while still having an access to global scoped injections (that have to be manually passed down to all the classes through gateway).
Given approach solves all the problems, while keeping everything dry and in sync with the idea that some day ConnectedSocket-scopes may be added to NestJS.

IdentityServer3: Dependency Injection with RegistrationMode.InstancePerHttpRequest

I am implementing a IdentityServer3 and want to use a custom IUserService to check against our user table in our database.
Our server handles more than one database (or "tenants"), so the database context is always different per HttpRequest.
var factory = new IdentityServerServiceFactory();
factory.UserService = new Registration<IUserService>(resolver => new MyUserService(resolver.Resolve<IDatabaseInstance>()));
So i want to use a dependency registation per HTTP-request.
I think this is the desired setting:
IdentityServer3.Core.Configuration.RegistrationMode.InstancePerHttpRequest
But how can I use this?
The neccessary database is not known on boot-time in the StartUp-class.
I know the database within the ApiController. So I would like to requester the IDatabaseInstance there.
Can I do this? How can I do this?
Is there an example?
This is not helping, because it covers only simple scenarios...
https://github.com/IdentityServer/IdentityServer3.Samples/tree/master/source/DependencyInjection

Resources