DataSources in NestJS GraphQL

WARNING: THE CODE BELOW IS INCORRECT, DATASOURCES NEED TO BE CREATED PER REQUEST.

PLEASE AVOID USING THE CODE BELOW

I am currently exploring using an apollo-rest-datasource within NestJS. One challenge I have encountered is that the DataSources do not integrate with NestJS' Dependency Injection system.

To work around this limitation, I had NestJS instantiate the singleton datasources and then utilized GraphQLModule.forRootAsync to inject these instances into the dataSources property of Apollo Server.

 GraphQLModule.forRootAsync({
      imports: [
        DataSourcesModule
      ],
      useFactory: (...args: DataSource[]) => {
        return {
          typePaths: ['./**/*.graphql'],
          context: ({req}: {req: Request}) => ({ token: req.headers.authorization }),
          playground: true,
          dataSources: () => {
            let dataInstances = {} as any;
            args.forEach(arg => {
              const dataSource = arg as any;
              dataInstances[dataSource.constructor.name] = arg;
            });
            return dataInstances;
          },
        };
      },
      inject: [...dataSources]

With this setup, I was able to achieve Dependency Injection functionality in my DataSource and utilize DI within the resolvers by including my DataSource instances (rather than accessing them from the GraphQL context). While this solution works, it does feel somewhat unorthodox.

Are there alternative approaches for integrating NestJS' DI with the Apollo GraphQL context?

Answer №1

It appears that the RESTDataSource behaves like a regular class. Simply applying the @Injectable() decorator allows you to treat them as normal Nest services. This enables you to inject dependencies into them and also inject the DataSources into your Resolvers without requiring any additional setup in the GraphQLModule as previously demonstrated.

const { RESTDataSource } = require('apollo-datasource-rest');
import { Injectable } from '@nestjs/common';

@Injectable()
class MoviesAPI extends RESTDataSource {
  // Inject any necessary Nest dependencies
  constructor(private readonly someDependency: SomeDependency) {
    super();
    this.baseURL = 'https://movies-api.example.com/';
  }

  async getMovie(id) {
    return this.get(`movies/${id}`);
  }

  async getMostViewedMovies(limit = 10) {
    const data = await this.get('movies', {
      per_page: limit,
      order_by: 'most_viewed',
    });
    return data.results;
  }
}

@Injectable()
class ResolverClass {
   // Injection of datasources
   constructor(private readonly moviesApi: MoviesAPI) { }
}

You just need to ensure that your DataSource classes are placed in the providers of the appropriate Nest module and potentially export them if required by other modules.

Update: To pass the dataSources into ApolloServer in a more Nest-oriented manner, you could create your own decorator for each DataSource and use reflection to discover all existing sources in your application. Although not well-documented, you can refer to examples from Nest's source code on how to achieve this. Here is the code that retrieves all classes decorated with @Resolver for the GraphQL module.

To simplify, it involves utilizing the ModulesContainer and MetadataScanner to identify all providers within the application and then filtering out those with your custom decorator applied (e.g. @DataSource()).

The current approach may not pose significant issues, but implementing it this way eliminates the need to manually add new dataSources each time.

Answer №2

To resolve the problem at hand, I implemented the @Context decorator on all resolver methods to access the necessary data sources. For a detailed explanation and demonstration, refer to this helpful link.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Can you share the appropriate tsconfig.json configuration for a service worker implementation?

Simply put: TypeScript's lib: ['DOM'] does not incorporate Service Worker types, despite @types/service_worker_api indicating otherwise. I have a functional TypeScript service worker. The only issue is that I need to use // @ts-nocheck at t ...

Issue with Angular 4 Routing: Links are opening in new window instead of within router-outlet

I am currently facing an issue while trying to display the SuburbDataComponent HTML on the DASHBOARD-SIDE-PANEL-COMPONENT.HTML. When I click on Dashboard, it opens a new window but only displays the SuburbDataComponent.html without showing the side panel ...

How to showcase a basic alert dialog using Material Angular

I'm currently utilizing Material Angular (found on Angular Material). However, I have noticed that the examples provided on the website appear to be overly complex. Additionally, most tutorials available online are either outdated or they focus on usi ...

Decoding enum interface attribute from response object in Angular 4 using typescript

From an API response, I am receiving an enum value represented as a string. This enum value is part of a typescript interface. Issue: Upon receiving the response, the TypeScript interface stores the value as a string, making it unusable directly as an en ...

Tips for importing React-Native types using TypeScript with Flow?

Within my react-native application, there is a TextInput component that reads various types from the following directory path: /Users/karl/Library/Caches/typescript/3.6/node_modules/@types/react-native/index.d.ts This file contains multiple types, one of ...

react-mock-store: Error - the middleware function is not defined

In my current setup, I am utilizing jest for testing with React and Typescript. import configureStore from "redux-mock-store"; import thunk from "redux-thunk"; const mockStore = configureStore([thunk]); it("should handle fetchCha ...

Tricks to avoid using useEffect dependency in custom hook

Take a look at this snippet of code I have written: const { getConversionList, conversionList } = useConversion(); useEffect(() => { getConversionList(); }, []); I am using useConversion as a GraphQL resolver hook, however, I am encountering a Linti ...

Sort elements based on an array of specified keys

Picture a scenario where you have an interface like this: interface Person { name: string; age: number; height: number; } Now, imagine having a function that accepts an array of keys from the interface and returns an object with only those spe ...

Failure to execute the guard condition

Currently, I am tackling a new project that involves Angular along with Firebase for Authentication and Firestore as the database. However, while implementing an Admin Guard to check user privileges, I encountered a perplexing issue where the guard conditi ...

An error occurred while trying to assign a value to a property that is undefined in Angular: attempting to set the

I am working with two interfaces export interface ClosureItem{ id:string; name:string; visibility?:boolean; } export interface ClosureAllItems{ [K:string]:ClosureItem; Financials:ClosureItem; Risk:ClosureItem; Iss ...

Exploring the automatic type inference functionality of Typescript for generic classes

Although it may seem simple, I am struggling to pinpoint the cause of this error. I have been searching for a solution for quite some time, but I have yet to find one. class MyClass<T > { property: T = 5 // Error Here: Type '5' is not as ...

Maximizing the potential of process.hrtime.bigint

Whenever I include the following code: const a = process.hrtime.bigint(); The linter says it's okay, but during compilation, I encounter this error message: error TS2339: Property 'bigint' does not exist on type 'HRTime'. This ...

Issue: Unsuccessful Response - Next.js GraphQL request returned status code 400

Currently, I am working on filtering data using GraphQL in Next.js while utilizing Strapi v4 as the backend. The specific code snippet I am focusing on is within the [slug].js file located in the pages folder, serving as a page for dynamic routing. import ...

Issue encountered with express-jwt and express-graphql: TypeScript error TS2339 - The 'user' property is not found on the 'Request' type

Implementing express-jwt and graphql together in typescript has been a challenge for me. import * as express from 'express' import * as expressGraphql from 'express-graphql' import * as expressJwt from 'express-jwt' import s ...

Error encountered while trying to assign an extended NextPage type to a page component

I encountered a type error related to const Page, as shown in the screenshot and code block below. https://i.sstatic.net/FrAuz.png The error message states that Type '{ (props: Props): JSX.Element; getLayout(page: ReactElement<any, string | JSXEle ...

Personalize the JSON output in a GraphQL query

Utilizing Express-js and the express GraphQL module, I have created my endpoint and web service; Currently, I am exploring methods to generate a customized response in GraphQL. My endpoint is quite straightforward: I'm retrieving books from the data ...

Transform an array of object's designated key values into a string that is separated by commas

Is there a way to convert specific key values of an object into a comma-separated string? I have been able to do this with arrays, but my current challenge is that my data is an array of objects. I want to convert each 'id' value into an array of ...

Can we guarantee the uniqueness of a function parameter value during compilation?

I have a set of static identifiers that I want to use to tag function calls. Instead of simply passing the identifiers as arguments, I would like to ensure that each identifier is unique and throws an error if the same identifier is passed more than once: ...

I am looking to input and store the Kannada text used in my Angular project

I am facing an issue with my html form where I call an API to save text fields in a database. The problem arises when I type Kannada words in the text field, as when rendering the list, the Kannada fonts are displayed as ??. I am seeking a solution to thi ...

Modify the dropdown menu title dynamically based on the selection made in Angular

My Angular web-application has a dropdown menu that looks like this: <div class="btn-group" dropdown> <button dropdownToggle type="button" class="btn btn-primary dropdown-toggle">NAMEOFDROPDOWN <span class="caret"></span>&l ...