Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RESTDataSource with TypeScript #2652

Closed
damianesteban opened this issue May 6, 2019 · 11 comments
Closed

RESTDataSource with TypeScript #2652

damianesteban opened this issue May 6, 2019 · 11 comments
Labels
🧬 typings Relates to TypeScript changes or improvements.

Comments

@damianesteban
Copy link

I'm using apollo-server-hapi and I'm defining my server like this:

  interface MyDataSources {
    injuryAPI: InjuryDataSource,
    appointmentAPI: AppointmentDataSource,
  }

  const server = new ApolloServer({
    schema,
    dataSources: (): DataSources<MyDataSources> => ({
      injuryAPI: new InjuryDataSource(),
      appointmentAPI: new AppointmentDataSource(),
    }),
    context: ({ request }): GraphQLCustomContext => ({
      credentials: { token: request.headers['Authorization'] || '' },
    }),
  });

This works fine, but then I lose type safety on the resolvers - dataSources is of type any.

If I create my server like this I get the type safety on the resolvers, but I receive the error fetch is undefined:

const server = new ApolloServer({
    schema,
    context: ({ request }): GraphQLCustomResolversContext => ({
      dataSources: {
        injuryAPI: new InjuryDataSource(),
        appointmentAPI: new AppointmentDataSource(),
      },
      credentials: { token: request.headers['Authorization'] || '' },
    }),
  });

Does anyone have a solution for using the RESTDataSource with TypeScript and getting type safety?

@abernix
Copy link
Member

abernix commented May 22, 2019

Do you mind building a reproduction of the second half of that problem into a runnable CodeSandbox reproduction using the latest version of Apollo Server and sharing the link to that CodeSandbox in this issue?

@damianesteban
Copy link
Author

I will post a code sandbox version later today, but for now this is how we got it working:

Interfaces:

export interface GraphQLCustomDataSources {
	myDataSource: MyDataSource
}

export interface GraphQLCustomContext {
  credentials: Credentials;
}

export interface GraphQLCustomResolversContext extends GraphQLCustomContext {
  dataSources: GraphQLCustomDataSources;
}

Server:

const bootstrap = async (): Promise<ApolloServer> => {
  const server = new ApolloServer({
    schema: await makeFinalSchema(),
    tracing: true,
    formatError: (error: GraphQLError): GraphQLFormattedError => {
      blogger.error(`Apollo server error: ${error.message} + ${error.locations} + ${error.stack}`);
      return error;
    },
    dataSources: (): any => ({
	  myDataSource: new MyDataSource(),
    }),
	// ...
  });

Resolver:

export const resolvers: Resolvers = {
  Query: {
    things: async (objs: null, args: null, context: GraphQLCustomResolversContext): Promise<Thing[]> => {
      try {
        const appointments = await context.dataSources.myDataSource.things();
        return appointments;
      } catch (error) {
        throw new ApolloError(error.message);
      }
    },
  },


So we do get the type safety, but in a roundabout way.

@JakeDawkins
Copy link
Contributor

@damianesteban Is this still an issue? Have you been able to make a reproduction for it?

@JakeDawkins JakeDawkins added 🚧👷‍♀️👷‍♂️🚧 in triage Issue currently being triaged 🧬 data-sources 🧬 typings Relates to TypeScript changes or improvements. labels Jul 8, 2019
@damianesteban
Copy link
Author

All set now actually. Sorry, should have updated sooner.

@JakeDawkins
Copy link
Contributor

Good to know! Thanks for the quick response @damianesteban!

@abernix abernix removed 🚧👷‍♀️👷‍♂️🚧 in triage Issue currently being triaged labels Jul 9, 2019
@mikecfisher
Copy link

How did you guys solve this? Would like to know!

@Nopzen
Copy link

Nopzen commented Feb 28, 2020

@damianesteban how did you end up doing this? Just using the roundabout way?

@EthanSK
Copy link

EthanSK commented Apr 16, 2020

A config option in the codegen.yml file for specifying the datasource type would be very helpful. There is currently one for context type:
contextType: ./context#MyContext
but not for datasource type!!

@mnpenner
Copy link

mnpenner commented Dec 29, 2020

I'm still figuring this out, but I managed to get dataSources typed by writing my resolvers like this:

import type {IResolvers} from 'apollo-server'
import {ResolverContext} from './context'
import {sql} from 'mysql3'

const resolvers: IResolvers<unknown,  ResolverContext> = {
    Query: {
        async users(_source, _args, {dataSources}, _info) {
            const users = await dataSources.db.query(sql`select * from users`)
            console.log(users)
            return users
        }
    },
}

export default resolvers;

Where ResolverContext is my own type, defined like:

import type Database from './datasources/database'

export interface DataSources {
    db: Database
}

export interface ServerContext {

}

export type ResolverContext = ServerContext & {
    dataSources: DataSources
}

I'm assuming here that dataSources is some magic thing that Apollo is merging into the context.

Edit: Ahah, it's documented as such:

Apollo Server will put the data sources on the context for every request, so you can access them from your resolvers. It will also give your data sources access to the context.

https://www.apollographql.com/docs/apollo-server/data/data-sources/#accessing-data-sources-from-resolvers

@the-ult
Copy link

the-ult commented Feb 19, 2021

I know this issue has been closed for a while. But I would still like to know how/if we could use the DataSources<TContext> type. Is it exported somewhere?

Screenshot 2021-02-19 at 20 58 56

Like this:

export interface MyDataSources {
  ...
  testPersonAPI: TestPersonAPI;
}

const apolloServer = new ApolloServer({
  ...
  // (property) dataSources?: () => DataSources<object> 
  dataSources: ( ): DataSources<MyDataSources> => ({
    ...
    testPersonAPI: new TestPersonAPI(),
  })
);

/// We are using it in combination with `graphql-modules`
/// https://graphql-modules.com/docs/essentials/type-safety#shaping-context-type
declare global {
  namespace GraphQLModules {
    interface GlobalContext {
      request: any;
      dataSources: MyDataSources;
    }
  }
}
// resolver
import { TestPersonModule } from './__generated__.module-types';

export const TestPersonResolvers: TestPersonModule.Resolvers = {
  Query: {
    testPerson: async (_parent, { uuid }, { dataSources }: GraphQLModules.GlobalContext) =>
      dataSources.testPersonAPI.getPerson(uuid),
  },
};

Or isn't this necessary to get proper typesafety and the check in the new ApolloServer({ dataSources })

@glasser
Copy link
Member

glasser commented Feb 23, 2021

This isn't necessarily super compelling, but ReturnType<Exclude<GraphQLServerOptions<{}>["dataSources"], undefined>> does the trick.

(I'm not sure I want to export DataSources directly from apollo-server-core because I'm considering moving towards making the data source functionality a little more separate from AS core; see #4950.)

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Apr 20, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
🧬 typings Relates to TypeScript changes or improvements.
Projects
None yet
Development

No branches or pull requests

9 participants