Type |聽Version
---|---
Question | 4.33.0
any plan for integrating graphql or type-graphql stuff?
I want to use graphql with tsed but not work as i expected.
would you recommend me any example?
Hello @bodguy
Great idea :)
It's possible to create a new package like tsed/mongoose or tsed/typeorm to support any third library. In the first time, you can try with your project locally by creating a new Service. Then if it works, we can create the new package inside the tsed project (very easy ;)).
For graphql, the idea is to configure the Apollo server with express (Doc: https://www.apollographql.com/docs/apollo-server/servers/express.html)
According to the example we can do that with a new Service as module. Create a new file GraphQLModule.ts:
import {Constant, OnInit, Service, ExpressApplication} from "@tsed/common";
import { ApolloServer, gql } from 'apollo-server-express';
@Service()
export class GraphQLModule implements OnInit {
@Constant("graphql", {})
private settings: {[key: string]: any}; // options
constructor(@ExpressApplication private expressApp: ExpressApplication) {}
async $onInit(): Promise<any> | void {
const {typeDefs, resolvers} = this.settings;
const server = new ApolloServer({ typeDefs, resolvers });
server.applyMiddleware({ app: this.expressApp });
}
}
ServerLoader:
@ServerSettings({
graphql: {
typeDefs, // give the typeDefs like is described in the apollo server example
resolvers // and same things for resolvers
}
})
It's a basic approach but if it works, you can create a new package based on the typeorm package example here: https://github.com/Romakita/ts-express-decorators/tree/master/packages/typeorm
If you have any suggestion to improve the code or other things don't hesitate, you are welcome,
See you,
Romain
Thanks so much, I did try but I'm quite confusing. I don't really understand when the service is actually injected in to the graphql resolver. (using typeorm, type-graphql)
this is my graphql resolver class
@Service()
@Resolver(User)
export class UserResolver {
constructor(private userService: UserService) {}
@Query(returns => [User])
async getAll(): Promise<User[]> {
return await this.userService.find(); // this is UserResolver.ts:27
}
}
UserService is just simple class accessing to database.
@Service()
export class UserService implements AfterRoutesInit {
private connection: Connection;
constructor(private typeORMService: TypeORMService) {
}
$afterRoutesInit(): Promise<any> | void {
this.connection = this.typeORMService.get("development");
}
async find(): Promise<User[]> {
const users = await this.connection.manager.find(User);
return users;
}
...
then graphql complain like below
"TypeError: Cannot read property 'find' of undefined",
" at UserResolver.<anonymous> (/Users/bodguy/Desktop/awesome-ts-server/src/resolvers/UserResolver.ts:27:35)",
I guess the problem caused by the order of service's lifecycle.
but I don't have any idea. can you help me?
Hum ok. I see what you want. The UserResolver class is instanciated two times. One, probably by the library of graphql and the second, by Ts.ED.
Here a kind of solution:
import {classOf, Metadata} from "@tsed/core";
import {Constant, ExpressApplication} from "@tsed/common";
import {Service, OnInit, InjectorService} from "@tsed/di";
import { ApolloServer, gql } from 'apollo-server-express';
@Service()
export class GraphQLModule implements OnInit {
@Constant("graphql", {})
private settings: {[key: string]: any}; // options
constructor(@ExpressApplication private expressApp: ExpressApplication, private injector: InjectorService) {}
async $onInit(): Promise<any> | void {
const schema = this.createSchema()
const server = new ApolloServer(schema);
server.applyMiddleware({ app: this.expressApp });
}
async createSchema() {
const {typeDefs, resolvers} = this.settings;
const schema = await buildSchema({
resolvers,
typeDefs
});
// we inject services on the current instance created by the typedGraphQL
schema.resolvers.forEach((instance) => {
(this.injector as any).bindInjectableProperties(instance); // I'll publish a new version to expose this method as public instead of private (if the code works ;) )
});
return schema;
}
}
Then, the server can be configured like that:
@ServerSettings({
graphql: {
resolvers: [
__dirname + "/**/*.resolver.ts"
]
}
})
And finally create your resolver like that:
import {Inject} from "@tsed/common"; // or from "@tsed/di"
@Resolver(User)
export class UserResolver {
@Inject()
private userService: UserService
@Query(returns => [User])
async getAll(): Promise<User[]> {
return await this.userService.find();
}
}
I hope this code will work for you and we will produce a new package for graphQL ;)
See you,
Romain
For type-graphql follow instructions from @Romakita above but replace GraphQLModule.createSchema method with this code:
async createSchema() {
const {resolvers} = this.settings;
useContainer(this.injector);
return await buildSchema({
resolvers,
});
}
In my case complete GraphQLModule looks like this:
import { Constant, ExpressApplication } from "@tsed/common";
import { InjectorService, OnInit, Service } from "@tsed/di";
import { ApolloServer } from 'apollo-server-express';
import { buildSchema, useContainer } from 'type-graphql';
@Service()
export class GraphQLModule implements OnInit {
@Constant("graphql", {})
private settings: {[key: string]: any}; // options
constructor(@ExpressApplication private expressApp: ExpressApplication, private injector: InjectorService) {}
async $onInit() {
const schema = await this.createSchema();
const srv = new ApolloServer({ schema });
srv.applyMiddleware({ app: this.expressApp, path: '/api/graphql' });
}
async createSchema() {
const {resolvers} = this.settings;
useContainer(this.injector);
return buildSchema({
resolvers,
});
}
}
works perfect
useContainer ? It works with the injector ? XD
Yep, It works with everything that has .get(target: T): T method and uses it to inject dependencies itself :)
nice :)
Somebody want to write the package and/or new documentation page ?
I can try a bit later when i'm sure there's no pitfalls.
Most helpful comment
For
type-graphqlfollow instructions from @Romakita above but replace GraphQLModule.createSchema method with this code:In my case complete GraphQLModule looks like this:
works perfect