Trying to build NestJS + GraphQL + TypeORM backend and I am stuck on trying to create mutations and queries. Tutorials people solve the problem in exactly this way, but still I can't understand what the error is.
Error: Cannot determine a GraphQL output type for the "createUser". Make sure your class is decorated with an appropriate decorator.
All decorators seem to be okay. Anyone have same problem?
user.module.ts
import { Module } from '#nestjs/common';
import { UserService } from './user.service';
import { UsersResolver } from './user.resolver';
import { TypeOrmModule } from '#nestjs/typeorm';
import { User } from './user.entity';
#Module({
imports: [TypeOrmModule.forFeature([User])],
exports:[TypeOrmModule],
providers: [UserService,UsersResolver ],
})
export class UserModule {}
user.entity.ts
import {
Column,
Entity,
OneToMany,
PrimaryGeneratedColumn,
} from 'typeorm';
import { Post } from '../post/post.entity';
import { Comment } from '../comment/comment.entity';
import { Field, ObjectType } from '#nestjs/graphql';
enum RoleEnum {
Admin = 'Admin',
Author = 'Author',
}
#Entity()
#ObjectType()
export class User {
#PrimaryGeneratedColumn('uuid')
#Field()
id: string;
#Column()
#Field()
name: string;
#Column()
#Field()
email: string;
#Column()
#Field()
password: string;
#Column({
type: 'enum',
enum: RoleEnum,
default: RoleEnum.Author,
})
#Field()
role: RoleEnum;
#OneToMany((type) => Post, post => post.user)
#Field(type=>Post)
posts: Post[];
#OneToMany((type) => Comment, comment => comment.author)
#Field(type=>Comment)
comments: Comment[];
}
user.resolver.ts
import { Args, Mutation, Query, Resolver } from '#nestjs/graphql';
import { UserService } from './user.service';
import { CreateUserDto } from './dto/createuser.dto';
import { userInput } from './input/user.input';
import { User } from './user.entity';
#Resolver((of=>User))
export class UsersResolver {
constructor(private userService: UserService) {
}
#Mutation(()=> [CreateUserDto])
async createUser (#Args('data') data: userInput){
return this.userService.createUser(data)
}
}
user.service.ts
import { Injectable } from '#nestjs/common';
import { InjectRepository } from '#nestjs/typeorm';
import { User } from './user.entity';
import { Repository } from 'typeorm';
import { CreateUserDto } from './dto/createuser.dto';
#Injectable()
export class UserService {
constructor (#InjectRepository(User) private readonly userRepository: Repository<User>) {}
async createUser (data: CreateUserDto): Promise<User> {
const user = new User()
user.name=data.name
user.email=data.email
user.role=data.role
user.password=data.password
await this.userRepository.save(user)
return user
}
}
createUser.dto.ts
import { Field, ObjectType } from 'type-graphql';
import { registerEnumType } from '#nestjs/graphql';
enum RoleEnum {
Admin = 'Admin',
Author = 'Author',
}
registerEnumType(RoleEnum, {
name: 'RoleEnum',
});
#ObjectType()
export class CreateUserDto {
#Field() name: string;
#Field() email: string;
#Field() password: string;
#Field(type => RoleEnum) role: RoleEnum;
}
user.input.ts
import { Field, InputType } from 'type-graphql';
import { registerEnumType } from '#nestjs/graphql';
enum RoleEnum {
Admin = 'Admin',
Author = 'Author',
}
registerEnumType(RoleEnum, {
name: 'RoleEnum',
});
#InputType()
export class userInput {
#Field() name: string;
#Field() email: string;
#Field() password: string;
#Field(type => RoleEnum) role: RoleEnum;
}
I noticed few possible causes for the error,
1. user.resolver.ts
#Mutation() decorator should return the type [User], not the type [CreateUserInput]
#Mutation(()=> [User]) // change here
async createUser (#Args('data') data: userInput){
return this.userService.createUser(data)
}
2. user.input.ts
If you encounter error : "Input" cannot be found in the registry,
add #ArgsType() decorator to the class userInput
#InputType()
#ArgsType() //add decorator here
export class userInput {
#Field() name: string;
#Field() email: string;
#Field() password: string;
#Field(type => RoleEnum) role: RoleEnum;
}
note: I used "#nestjs/graphql": "^7.10.3",
Related
I am using TypeORM in nestJS project which has users and each user has a cart.
I have One-To-One relation between the users table and the carts table.
If I create a user I want the Cart to be automatically created and inserted into carts table and get its id and insert it into users table with the created user's info.
My question is, is there any way I can do that from the UserService
I have this User entity
import { Cart } from 'src/cart/cart.entity';
import { Entity, Column, PrimaryGeneratedColumn, OneToOne } from 'typeorm';
#Entity()
export class User {
#PrimaryGeneratedColumn('increment')
id: number;
#OneToOne(() => Cart, cart => cart.id)
cart_id: number;
#Column()
username: string;
#Column()
email: string;
#Column({ default: true })
password: string;
}
Also this Cart entity
import { CartItem } from 'src/cart_item/cart_item.entity';
import { Entity, Column, PrimaryGeneratedColumn, OneToMany } from 'typeorm';
#Entity()
export class Cart {
#PrimaryGeneratedColumn('increment')
id: number;
#Column({ default: 0.00 })
total_price: number;
#OneToMany(
type => CartItem,
cartItem => cartItem.cart,
)
cartItem: CartItem[];
}
This is the user service:
import { Injectable } from '#nestjs/common';
import { InjectRepository } from '#nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './user.entity';
#Injectable()
export class UserService {
constructor(
#InjectRepository(User)
private usersRepository: Repository<User>,
) {}
findAll(): Promise<User[]> {
return this.usersRepository.find();
}
findOne(id: number): Promise<User> {
return this.usersRepository.findOne(id);
}
async remove(id: number): Promise<void> {
await this.usersRepository.delete(id);
}
async add(user: User){
// write code here to create and insert a cart
// then get the cart id
// then add the id to "user" to be inserted
await this.usersRepository.insert(user);
}
}
this is the cart service:
import { Injectable } from '#nestjs/common';
import { InjectRepository } from '#nestjs/typeorm';
import { Repository } from 'typeorm';
import { Cart } from './cart.entity'
#Injectable()
export class CartService {
constructor(
#InjectRepository(Cart)
private cartsRepository: Repository<Cart>
) { }
findOne(id: number): Promise<Cart> {
return this.cartsRepository.findOne(id)
}
findAll(): Promise<Cart[]> {
return this.cartsRepository.find()
}
async remove(id: number): Promise<void> {
await this.cartsRepository.delete(id);
}
public async create(cart: Cart) {// I want to call this function in UserService
await this.cartsRepository.insert(cart);
}
}
Pretty new to GraphQL, I am facing an issue with the latest version of NestJS where I am currently trying to add a mutation to a resolver that doesn't show in the playground when the server is running.
It looks like the GraphQL schema is not updated on server launch.
The createUser mutation is showing in the GraphQL playground and working but the getUsers one (created for test purposes) is not showing.
I would appreciate any hint on how to tackle this issue.
Importation of the GraphQLMOdule in app.module
import { Module } from '#nestjs/common';
// Libraries
import { TypeOrmModule } from '#nestjs/typeorm';
import { GraphQLModule } from '#nestjs/graphql';
// App modules
import { MealModule } from './meal/meal.module';
import { AuthModule } from './auth/auth.module';
// Entities
import { MealEntity } from './meal/meal.entity';
import { UserEntity } from './auth/user.entity';
#Module({
imports: [
TypeOrmModule.forRoot({
type: 'mongodb',
url: 'mongodb://localhost/sideproject',
synchronize: true,
useUnifiedTopology: true,
entities: [MealEntity, UserEntity],
}),
GraphQLModule.forRoot({
autoSchemaFile: true,
debug: true,
playground: true
}),
MealModule,
AuthModule,
],
})
export class AppModule {}
Here are the types for the user module I am experiencing difficulties with :
import { ObjectType, Field, ID } from '#nestjs/graphql';
#ObjectType('User')
export class UserType {
#Field(() => ID)
id: string;
#Field()
username: string;
#Field()
email: string;
#Field()
password: string;
}
The associated resolver :
import { Resolver, Mutation, Args, Query } from '#nestjs/graphql';
import { UserType } from './types/user.types';
import { CreateUserInputType } from './inputs/create-user.input';
import { UserEntity } from './user.entity';
import { AuthService } from './auth.service';
#Resolver(of => UserType)
export class AuthResolver {
constructor(private authService: AuthService) {}
#Mutation(returns => UserType)
signUp(
#Args('createUserInput') createUserInput: CreateUserInputType,
): Promise<UserEntity> {
return this.authService.signUp(createUserInput);
}
#Query(returns => [UserType])
getUsers(): Promise<UserEntity[]> {
return this.authService.getUsers()
}
}
The service :
import { Injectable } from '#nestjs/common';
import { InjectRepository } from '#nestjs/typeorm';
import { CreateUserInputType } from './inputs/create-user.input';
import { UserRepository } from './user.repository';
import { UserEntity } from './user.entity';
#Injectable()
export class AuthService {
constructor(
#InjectRepository(UserRepository)
private userRepository: UserRepository,
) {}
signUp(createUserInput: CreateUserInputType): Promise<UserEntity> {
return this.userRepository.signUp(createUserInput);
}
async getUsers(): Promise<UserEntity[]> {
return await this.userRepository.find();
}
}
And finally the repository for the user module :
import { Repository, EntityRepository } from 'typeorm';
import { UserEntity } from './user.entity';
import { InternalServerErrorException } from '#nestjs/common';
import { CreateUserInputType } from './inputs/create-user.input';
import { v4 as uuid } from 'uuid';
import * as bcrypt from 'bcryptjs';
#EntityRepository(UserEntity)
export class UserRepository extends Repository<UserEntity> {
async signUp(createUserInput: CreateUserInputType): Promise<UserEntity> {
const { username, email, password } = createUserInput;
const user = this.create();
user.id = uuid();
user.username = username;
user.email = email;
user.password = bcrypt.hashSync(password, bcrypt.genSaltSync(12));
try {
return await this.save(user);
} catch (err) {
console.log(err);
throw new InternalServerErrorException();
}
}
}
Thank you very much !
I think you need to add the resolver to the providers collection for the resolvers to be available.
Had the same issue and these steps solved the problem:
stop the server
remove the dist folder
restart the server
The answer to this is : declaring the objectype only will not update the schema. including the objectype as a return type within a resolver is the key.
In my case (using Docker), I had to clear cached volumes with:
docker system prune -a
After rebuild, schema.gql was updated.
I am creating a graphql api using nestjs, type-graphql and typeorm. I am having trouble finding out how to do a certain query. Let's say i have the following entities:
#Entity()
#ObjectType({ description: 'The user model' })
export class User extends BaseEntity {
#Field(type => ID)
#PrimaryGeneratedColumn('uuid')
readonly id: string;
#Field()
#Column()
#IsNotEmpty()
#IsEmail()
email: string;
#Field()
#IsNotEmpty()
#Column()
password: string;
#Field(type => [Project], { nullable: true })
#ManyToMany(type => Project)
#JoinTable()
projects?: Project[];
}
#Entity()
#ObjectType({ description: 'The project model' })
export class Project extends BaseEntity {
#Field(type => ID)
#PrimaryGeneratedColumn('uuid')
readonly id: string;
#Field()
#Column()
#IsNotEmpty()
description: string;
#Field(type => [User], { nullable: true })
#ManyToMany(type => User)
#JoinTable()
participants?: User[];
}
I have the following service:
import { includes } from 'lodash';
#Injectable()
export class ProjectService {
constructor(
#InjectRepository(Project)
private projectRepository: Repository<Project>,
#InjectRepository(User)
private userRepository: Repository<User>,
) {}
async loadAllForUser(
userId: string
): Promise<Project[]> {
const projects = await this.projectRepository.find(
{
relations: ['participants'],
},
);
return projects.filter(project =>
includes(
project.participants.map(participant => participant.id),
userId,
),
);
}
}
And this will be my resolver:
#Resolver( of => Project)
export class ProjectResolver {
constructor(
private projectService: ProjectService,
private userService: UserService,
) {}
#Query(returns => [Project])
#UseGuards(GqlAuthGuard)
async myProjects(
#GqlUser() user: User,
) {
return await this.projectService.loadAllForUser(
user.id,
);
}
}
As you can see in my project service i am just fetching all the projects with the find method and then afterwards i am filtering this list using includes from lodash to filter out only the projects that contains the userid. Isn't there a way to add some sort of operation inside the find method of the project repository like a 'where' or 'in' so that i can filter it directly from there instead of manually filtering and using the includes from lodash? I even have no idea if this is even correct the way i am manually filtering?
Kind regards,
Gerry
Try to use TypeORM QueryBuilder and make some inner joins to filter projects that does not belong to selected user.
As of v0.3, you can use ArrayContains() to return a FindOptionsWhere<> type that we can use to filter the where clause on a relation.
where: {
project: ArrayContains[participant.id]
}
I'm trying to create a singleton class so I could avoid opening the same database again.
I've read about creating a class provider with a static variable so it could stay the same, but I've seen that each time I navigate to another component, the static variable content is lost.
Here's my code so far
couchbase.service.ts
import { Injectable } from "#angular/core";
import { Couchbase } from "nativescript-couchbase";
import * as connection from "tns-core-modules/connectivity";
import { isAndroid } from "platform";
#Injectable()
export class CouchbaseService {
private static database: any;
constructor() {
console.log("enter constructor")
}
public static init(){
if(!CouchbaseService.database) {
console.log("enter init")
CouchbaseService.database = new Couchbase("data");
}
return CouchbaseService.database;
}
public getDatabase() {
return CouchbaseService.database;
}
...
}
app.component.ts: I've read that calling init from this parent class would make the app keep it for the children (note that I also tried passing the CouchbaseService as a constructor parameter, changing the method init to non-static)
import { Component } from "#angular/core";
import * as Platform from "platform";
import { CouchbaseService } from './services/couchbase.service';
#Component({
selector: "ns-app",
templateUrl: "app.component.html",
})
export class AppComponent {
constructor() {
CouchbaseService.init();
}
}
In my app.module.ts file, I added CouchbaseService to the providers list.
import { NgModule, ErrorHandler, NO_ERRORS_SCHEMA } from "#angular/core";
import { NativeScriptModule } from "nativescript-angular/nativescript.module";
import { NativeScriptUIListViewModule } from "nativescript-ui-listview/angular";
import { NativeScriptUISideDrawerModule } from "nativescript-ui-sidedrawer/angular";
import { NativeScriptHttpModule } from "nativescript-angular/http";
import { Http } from "#angular/http";
import { NativeScriptUIDataFormModule } from "nativescript-ui-dataform/angular";
import { NativeScriptFormsModule } from "nativescript-angular/forms";
import { registerElement } from 'nativescript-angular/element-registry';
import { CardView } from 'nativescript-cardview';
import { AppRoutingModule } from "./app.routing";
import { AppComponent } from "./app.component";
import { CouchbaseService } from "./services/couchbase.service";
import { HomeComponent } from "./components/home/home.component";
registerElement('CardView', () => CardView);
registerElement("MapboxView", () => require("nativescript-mapbox").MapboxView);
registerElement("Fab", () => require("nativescript-floatingactionbutton").Fab);
#NgModule({
bootstrap: [
AppComponent
],
imports: [
NativeScriptModule,
NativeScriptUIListViewModule,
NativeScriptUISideDrawerModule,
AppRoutingModule,
NativeScriptUIDataFormModule,
NativeScriptFormsModule,
NativeScriptHttpModule
],
declarations: [
AppComponent,
HomeComponent
],
providers: [
CouchbaseService,
],
schemas: [
NO_ERRORS_SCHEMA
],
entryComponents: [
],
})
/*
Pass your application module to the bootstrapModule function located in main.ts to start your app
*/
export class AppModule { }
When I look at the logs in the terminal, seems like each time I navigate to another component the service is restarted, so it looses the value of database.
I'm using NativeScript 4 with Angular 5 and TypeScript 2.7.2.
This is not how should make use of a singleton service. Let's do this way:
Change your service like this:
import { Injectable } from "#angular/core";
import { Couchbase } from "nativescript-couchbase";
import * as connection from "tns-core-modules/connectivity";
import { isAndroid } from "platform";
#Injectable()
export class CouchbaseService {
private database: any;
constructor() {
console.log("enter constructor")
this.init();
}
private init(){
if(!this.database) {
console.log("enter init")
this.database = new Couchbase("data");
}
}
public getDatabase() {
return this.database;
}
...
}
In your AppModule [or module where you have your service] specify the service in #NgModule's provider array like this:
#NgModule({
declarations: [
AppComponent,
YourComponent
],
imports: [
BrowserModule
],
providers: [CouchbaseService],
bootstrap: [AppComponent]
})
export class AppModule { }
Now in your component use constructor dependency injection like this:
import { Component } from "#angular/core";
import * as Platform from "platform";
import { CouchbaseService } from './services/couchbase.service';
#Component({
selector: "ns-app",
templateUrl: "app.component.html",
})
export class AppComponent {
constructor(private service: CouchbaseService) {
console.log(this.service.getDatabase());
}
}
AIM: I send a http request from angular 2 ui to java server. While it is executing, server generates messages with progress status so I can update progress bar on ui.
I have 3 entities: AppComponent, AppService and WebsocketService.
Here is a simplified example of WebsocketService. It can subscribe to message topic and perform some actions on incoming each message.
export class WebsocketService {
private client: Client;
constructor() {
var service = this;
service.client = Stomp.client('ws://localhost:8080/stomp/websocket');
service.client.connect("user", "pass", function () {
service.client.subscribe("/progress", function (message) {
// some actions here
})
});
}
}
So, my question is: how to update AppComponent's property (field) value, which is binded to template, from AppService or even WebsocketService? Use a setter? Is it allright from the terms of MVC?
There is more than one way to do this but I would use a "Subject" stream.
Here is an example:
import {Injectable} from '#angular/core';
import {Http, Headers, RequestOptions, Response} from '#angular/http';
import {Observable} from 'rxjs/Rx';
import {Subject} from 'rxjs/Subject';
import {NextObserver} from 'rxjs/Observer';
export class WebsocketService {
public ProcessingMessages$: Observable<string>;
private client: Client;
private processingMessages: Subject<string>;
constructor() {
this.processingMessages = new Subject<string>();
this.ProcessingMessages$ = this.processingMessages.asObservable();
var service = this;
service.client = Stomp.client('ws://localhost:8080/stomp/websocket');
service.client.connect("user", "pass", function () {
service.client.subscribe("/progress", function (message) {
this.processingMessages.next(message);
})
});
}
}
// Sample Component
#Component({
selector: 'my-component',
template: 'WebsocketService Message: {{Message}}',
})
export class Sample Component implements OnInit {
public Message: string;
constructor(
private service: WebsocketService
) {
}
ngOnInit() {
this.service.ProcessingMessages$.subscribe((message: string) => {
this.Message = message;
});
}
}