Search code examples
typescriptgraphqltypeormtypegraphql

How to use type-graphql to call typeorm's BaseEntity when do a creation?


Versions:

  • typeorm: 0.3.6
  • type-graphql: 1.1.1

The project directory likes:

- src/
    inputs/
      CreateBookInput.ts
    models/
      Book.ts
    resolvers/
      BookResolver.ts
    index.ts

It just creates a sample book feature.

src/inputs/CreateBookInput.ts

import { InputType, Field } from "type-graphql";

@InputType()
export class CreateBookInput {
  @Field()
  title: string;

  @Field()
  author: string;
  @Field({ nullable: true })
  isPublished?: boolean;
}

src/models/Book.ts

import { Entity, BaseEntity, PrimaryGeneratedColumn, Column } from "typeorm";
import { ObjectType, Field, ID } from "type-graphql";

@Entity()
@ObjectType()
export class Book extends BaseEntity {
  @Field(() => ID)
  @PrimaryGeneratedColumn()
  id: string;

  @Field(() => String)
  @Column()
  title: string;

  @Field(() => String)
  @Column()
  author: string;

  @Field(() => Boolean)
  @Column({ default: false })
  isPublished: boolean;
}

src/models/BookResolver.ts

import { Resolver, Query, Mutation, Arg } from "type-graphql";
import { Book } from "../models/Book";
import { CreateBookInput } from "../inputs/CreateBookInput";

@Resolver()
export class BookResolver {
  @Query(() => [Book])
  books() {
    return Book.find();
  }

  @Mutation(() => Book)
  async createBook(@Arg("data") data: CreateBookInput) {
    const book = Book.create(data);
    await book.save();
    return book;
  }
}

src/index.ts

import "reflect-metadata";
import { createConnection } from "typeorm";
import { ApolloServer } from "apollo-server";
import { buildSchema } from "type-graphql";

import { BookResolver } from "./resolvers/BookResolver";

async function main() {
  await createConnection();
  const schema = await buildSchema({ resolvers: [BookResolver] });
  const server = new ApolloServer({ schema });
  await server.listen(4000);
  console.log("Server has started!");
}

main();

When run the application, there is an error happened:

TSError: ⨯ Unable to compile TypeScript:
src/resolvers/BookResolver.ts:20:30 - error TS2769: No overload matches this call.
  Overload 1 of 3, '(this: (new () => Book) & typeof BaseEntity, entityLikeArray: DeepPartial<Book>[]): Book[]', gave the following error.
    Argument of type 'CreateBookInput' is not assignable to parameter of type 'DeepPartial<Book>[]'.
      Type 'CreateBookInput' is missing the following properties from type 'DeepPartial<Book>[]': length, pop, push, concat, and 29 more.
  Overload 2 of 3, '(this: (new () => BaseEntity) & typeof BaseEntity, entityLike: DeepPartial<BaseEntity>): BaseEntity', gave the following error.
    Argument of type 'CreateBookInput' is not assignable to parameter of type 'DeepPartial<BaseEntity>'.

20     const book = Book.create(data);
                                ~~~~

src/resolvers/BookResolver.ts:21:16 - error TS2339: Property 'save' does not exist on type 'Book[]'.

21     await book.save();

The Book.create(data); seems use the built-in method.

Why this error happened?


Solution

  • Try with the following approach. Create an instance of Book and assign the values from the input type to it.

    @Mutation(() => Book)
    async createBook(@Arg("data") data: CreateBookInput) {
      const book = Book.create(Book, data);
      await book.save();
      return book;
    }
    

    or

    @Mutation(() => Book)
    async createBook(@Arg("data") data: CreateBookInput) {
      const book = new Book();
      Object.assign(book, data);
      await book.save();
      return book;
    }