Resolving a Graphql query from different data sources - node.js

I have a graphql server I've created using ApolloServer and Type-Graphql.
I have two types defined on my graphql server:
User{
id:string;
name:string
email:string;
...
}
UserPrefrences{
userId:string;
theme: string;
color:string;
...
}
The data for the User type is saved in a database which I access through a different graphql server by forwarding the request I get from client.
The data for the UserPrefrences type is saved on a different database which I access directly from my graphql server.
I don't want my client side to need to know these two separate types and to need to run two separate queries.
I'm looking for a way to let my client run the following query on my graphql server:
query UserData($userId: String!) {
id
name
email
theme
color
}
But if I forward this request to the graphql server that I'm querying, I will get a response saying the fields 'theme' and 'color' are unknown to him.
I'm trying to find a way to forward only the relevant fields to the graphql server, and then resolving the rest within my graphql server. But I receive the query as a string which makes it a pain trying to use regex to only forward the fields I'm interested in.
I'd be more than happy for any ideas on how to solve this issue.

the only way I found is using a Graphql client in Node.js.
I'm using the npm library called graphql-request
https://www.npmjs.com/package/graphql-request
import { GraphQLClient, gql } from 'graphql-request';
const client = new GraphQLClient('http://localhost:4000/graphql');
const query = `
{
yourQuery {
id
name
}
}
`;
const response = client.request(query);

Related

NestJs for lambda with mongodb creating too many connections

We are using nestjs for lambda which connects with mongodb for data. We are using nestjs mongoose module. However on deployment for each invocation a new set of connection are made and the previous ones are not released.
We are using forRootAsync
MongooseModule.forRootAsync({
useClass: MongooseConfigService,
})
service looks like this:
#Injectable({ scope: Scope.REQUEST })
export class MongooseConfigService implements MongooseOptionsFactory {
constructor(#Inject(REQUEST) private readonly request: Request) {}
async createMongooseOptions(): Promise<MongooseModuleOptions> {
if (Buffer.isBuffer(this.request.body)) {
this.request.body = JSON.parse(this.request.body.toString());
}
const { db } = this.request.body;
console.log('connecting database', db);
return {
uri: process.env.MONGO_URL,
dbName: db || '',
};
}
}
I understand we need to reuse the same connection. We have achieved it in nodejs by simply checking if the connection already exists and if it does not connect again. Not sure how to achieve the same in nest.js
tried changing the scope of service to Scope.DEFAULT but that didn't help.
I would suggest that you make a connection proxy for MongoDB. Ever time a lambda gets invoked, it will open up a new connection to MongoDB. AWS generally provides a service that allows you to proxy requests through one connect, this is a common issue with RDS etc.
This may help you though: https://www.mongodb.com/docs/atlas/manage-connections-aws-lambda/
We were able to resolve the issue by using a durable provider. NestJs documentation we created a strategy at the root that would use a parameter coming in each request. NestJs would than call the connection module only when a new connection was required.
Note: When you use durable providers, Requests doesn't have all the parameters anymore and now only has the tenantId as per the example.

NestJS WebSocket Gateway - how get full path for initial connection?

I'm adding a socket.io "chat" implementation to our NestJS app, currently serving a range of HTTP REST APIs. We have fairly complex tenant-based auth using guards for our REST APIs. Users can belong to one or many tenants, and they target a given tenats via the API URL, which can be either subdomain or path-based, depending on the deployment environment, for example:
//Subdomain based
https://tenant1.api.server.com/endpoint
https://tenant2.api.server.com/endpoint
//Path based
https://api.server.com/tenant1/endpoint
https://api.server.com/tenant2/endpoint
This all works fine for REST APIs, allowing us to determine the intended tenant (and validate the user access to that tenant) within guards.
The new socket.io implementation is being exposed on the same port at endpoint "/socket", meaning that possible full paths for connection could be:
https://tenant1.api.server.com/socket
https://api.server.com/tenant1/socket
Ideally I want to validate the user (via JWT) and the access to the group during the connection of the websocket (and if they are not validated they get immediately disconnected). I have been struggling to implement with guards, so I have done JWT/user validate in the socket gateway, which works ok. For the tenant validation, as per the above, I need the FULL URL that was used for the connection, because I will either be looking at the subdomain OR the path, depending on the deployment. I can get the host from the client handshake headers, but cannot find any way to get at the path. Is there a way to get the full path either from the handshake, or perhaps from Nest? I think perhaps I am limited in what I have access to in the handleConnection method when implementing OnGatewayConnection.
Code so far:
#WebSocketGateway({
namespace: 'socket',
cors: {
origin: '*',
},
})
export class ChannelsGateway
implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect {
#WebSocketServer() public server: Server
//Init using separate socket service (allowing for other components to push messages)
afterInit(server: Server) {
this.socketService.socket = server
Logger.debug('Socket.io initialized')
}
//Method for handling the client initial connection
async handleConnection(client: Socket, ...args: any[]) {
//This line gets me the host, such as tenant1.api.server.com but without the path
const host = client.handshake.headers.host
//Get bearer token from authorizaton header and validate
//Disconnect and return if not validated
const bearerJwt = client.handshake.headers.authorization
const decodedToken = await validateJwt(bearerJwt).catch(error => {
client.disconnect()
})
if (!decodedToken) {
return
}
//What can I use to get at the path, such as:
//api.server.com/tenant1/socket
//tenant1.api.server.com/socket
//Then I can extract the "tenant1" with existing code and validate access
//Else disconnect the client
//Further code to establish a private room for the user, etc...
}
//other methods for receiving messages, etc...
}

Angular Service with Azure Cosmos

I am really new to Angular. I am trying to create a service which i want to consume in my angular component. While doing so i am getting below error.
Below is my code which i am writing
import { Injectable } from '#angular/core';
import { HttpClient} from '#angular/common/http';
import { CosmosClient } from '#azure/cosmos';
import {Observable,of} from 'rxjs'
#Injectable({
providedIn: 'root'
})
export class ApiService {
databaseId='dbName';
containerId='Container Name';
constructor() { }
public async getProjects():Promise<Observable<any>>{
const endpoint = "https://AAAA.documents.azure.com:443/";
const key = "==";
const client = new CosmosClient({ endpoint, key });
const database = client.database(this.databaseId);
const container = database.container(this.containerId);
const querySpec = {query: "SELECT * from c where c.Category=\"Details\""};
const { resources:items } = await container.items.query(querySpec).fetchAll();
return of(items);
}
}
Any help is really appreciated.
There is an exception to every rule, but the use cases for connecting to a DB directly from a web browser are pretty close to zero. By doing so, you lose all fine grained control over what a user can do in your database and the only way to revoke access is to rotate your keys. You may not currently have anything in your database that is sensitive, but it is still considered bad practice.
For this reason, the CosmosDB library is compatible with NodeJS as a server-side framework. Whether or not it works with front end frameworks like Angular or React are incidental. There are some large changes in how Angular compiles projects in version 9, and it looks like the Cosmos client is not compatible with the new Ivy compiler.
You do have a couple options here.
(recommended) Use an API layer between your database and your front end. You can use Node to keep it within Javascript. If you are running on Azure, there are services like Azure Functions that can make this even easier to implement securely, or you can run it from the same App Service, VM, or whatever hosting solution you are using.
Disable the new Ivy compiler.You can do this by adding aot: false in your angular.json

GraphQL resolvers contained within a node process rather than a server?

I need to create something similar to a GraphQL server but fully contained within a node process rather than an actual server. So essentially a JavaScript function which you would call with a query or mutation as a string, and it would return an object or string with the response based on your resolvers.
It’s a weird requirement I know. We need to mock a GraphQL server at my company and due to some limitations in our build pipeline we can’t run an actual server.
Apologies that this quite an open question but I don't know where to start. What package contains the core functionality for GraphQL? If I was making a GraphQL server I'd use Apollo Server or GraphQL Yoga package, but it's hard to Google what I need as it's such an unusual requirement.
You just need the vanilla GraphQL.js package (graphql) to execute a query against a schema. The package exports a graphql function with the following signature:
graphql(
schema: GraphQLSchema,
requestString: string,
rootValue?: ?any,
contextValue?: ?any,
variableValues?: ?{[key: string]: any},
operationName?: ?string
): Promise<GraphQLResult>
From the docs:
The graphql function lexes, parses, validates and executes a GraphQL request. It requires a schema and a requestString. Optional arguments include a rootValue, which will get passed as the root value to the executor, a contextValue, which will get passed to all resolve functions, variableValues, which will get passed to the executor to provide values for any variables in requestString, and operationName, which allows the caller to specify which operation in requestString will be run, in cases where requestString contains multiple top-level operations.
So given a schema, you can just do:
const request = `
query MyQuery {
someField
}
`
const { data, errors } = graphql(schema, request)
Note: If you have typeDefs and resolvers that you'd normally pass to an ApolloServer config, you can create a GraphQLSchema object by passing them to graphql-tools' makeExecutableSchema instead (which is what apollo-server and graphql-yoga use under the hood).

How to establish connection between graphql server and neo4j database of version 3.2.0 ?

For my nodejs app, I am using neo4j 3.2.0 community edition as a primary database and I want to create a Graphql APIserver and establish connection between this api server and neo4j db. I looked into the official graphql-neo4j-graph-database-integration
(https://github.com/neo4j-graphql/neo4j-graphql), but according to its documentation it says that this works only on 3.0 and 3.1.
So, can anyone please explain how can I establish connection between the graphql server and neo4j db (3.2.0 community edition) ?
PS. I even looked into apollo express-graphql server and their documentation,I understood how the graphql endpoint can be used in an express web server if i create one, but what I am unable to grasp is even if I create an apollo express-graphql server, how am I supposed to integrate it with neo4j as graphql does not seem to have connector present for neo4j ?
Please let me know if I am missing something here regarding the architecture of graphql with neo4j and express in nodejs environment. (Thanks in Advance)
The neo4j-graphql library allows you to embed Cypher queries to be used as GraphQL resolvers, which indeed is very helpful when developing a Neo4j backed GraphQL server. But, you don't actually need it to achieve the same result.
Instead, all you have to do is write resolvers that talk to Neo4j yourself, and as long as your platform (Node.js) has the appropriate drivers (and it does) - you're good to go.
E.g. a very simple schema, defined programmatically (i.e. not via IDL) could look like this:
let schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'RootQueryType',
fields: {
numberOfRegisteredUsers: {
type: GraphQLInt,
resolve: function() {
return neo4j.queryForTheNumberOfUsers(); //this is your custom resolution logic
}
}
}
})
});
As you can see, apart from the usual Neo4j driver, you don't need anything extra. No special connectors or any GraphQL-specific bits.

Resources