things-factory / shell

things app starter kit
MIT License
3 stars 0 forks source link

type-graphql #49

Closed lotstar87 closed 4 years ago

lotstar87 commented 4 years ago

graphql의 type을 typescript의 type으로 사용할 수 있게 하기위해, type-graphql를 적용

lotstar87 commented 4 years ago

적용결과 type이 중복해서 적용된다는 오류가 발생 (Schema must contain uniquely named types but contains multiple types named XXX)

lotstar87 commented 4 years ago

AS-IS

  1. write graphql types

    import gql from 'graphql-tag'
    
    export const Domain = gql`
      type Domain {
        id: String
        name: String
        description: String
        timezone: String
        systemFlag: Boolean
        subdomain: String
        brandName: String
        brandImage: String
        contentImage: String
        theme: String
        createdAt: String
        updatedAt: String
      }
    `
  2. write resolvers in separate file.

    // domain.ts
    import { getRepository } from 'typeorm'
    import { Domain } from '../../../entities'
    
    export const domainResolver = {
      async domain(_, { name }: Record<string, string>, context, info): Promise<Domain> {
        const repository = getRepository(Domain)
    
        return await repository.findOne({ name })
      }
    }
    
    // domains.ts
    import { Context } from 'koa'
    import { getRepository } from 'typeorm'
    import { Domain } from '../../../entities'
    import { buildQuery } from '../../list-query-builder'
    import { DomainList } from '../../types/domain/domain-list'
    import { ListParam } from '../../types/list-param'
    
    export const domainsResolver = {
      async domains(_: any, params: ListParam, context: Context): Promise<DomainList> {
        const queryBuilder = getRepository(Domain).createQueryBuilder()
        buildQuery(queryBuilder, params, context, false)
        const [items, total] = await queryBuilder.getManyAndCount()
    
        return { items, total }
      }
    }
    
    // create-domain.ts
    import { getRepository } from 'typeorm'
    import { Domain } from '../../../entities'
    
    export const createDomain = {
      async createDomain(_: any, { domain }: any): Promise<Domain> {
        return await getRepository(Domain).save(domain)
      }
    }
    
    // update-domain.ts
    import { getRepository } from 'typeorm'
    import { Domain } from '../../../entities/domain'
    import { DomainPatch } from '../../types/domain/domain-patch'
    
    type UpdateDomainInput = {
      name: string
      patch: DomainPatch
    }
    
    export const updateDomain = {
      async updateDomain(_: any, { name, patch }: UpdateDomainInput) {
        const repository = getRepository(Domain)
        const domain = await repository.findOne({ name })
    
        return await repository.save({
          ...domain,
          ...patch
        })
      }
    }
    
    // delete-domain.ts
    import { DeleteResult, getRepository } from 'typeorm'
    import { Domain } from '../../../entities/domain'
    
    export const deleteDomain = {
      async deleteDomain(_: any, { name }: Record<string, string>): Promise<DeleteResult> {
        return await getRepository(Domain).delete({ name })
      }
    }
  3. write the graphql SDL for queries, mutations, subscriptions and export it

    import { Domain } from './domain'
    import { NewDomain } from './new-domain'
    import { DomainPatch } from './domain-patch'
    import { DomainList } from './domain-list'
    
    export const Mutation = /* GraphQL */ `
      createDomain (
        domain: NewDomain!
      ): Domain
    
      updateDomain (
        name: String!
        patch: DomainPatch!
      ): Domain
    
      deleteDomain (
        name: String!
      ): Domain
    `
    
    export const Query = /* GraphQL */ `
      domains(filters: [Filter], pagination: Pagination, sortings: [Sorting]): DomainList
      domain(name: String!): Domain
    `
    
    export const Types = [Domain, NewDomain, DomainPatch, DomainList]
  4. export resolvers

    import * as Domain from './domain'
    
    export const queries = [Domain.Query]
    export const mutations = [Domain.Mutation]
    export const subscriptions = []

TO-BE

  1. add decorator for type-graphql in entity

    // domain.ts
    import { Field, ID, ObjectType } from 'type-graphql'
    import { Column, CreateDateColumn, Entity, Index, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm'
    
    @ObjectType('Domain')
    @Entity('domains')
    @Index('ix_domain_0', (domain: Domain) => [domain.name], { unique: true })
    @Index('ix_domain_1', (domain: Domain) => [domain.subdomain])
    @Index('ix_domain_2', (domain: Domain) => [domain.systemFlag])
    export class Domain {
      @Field(type => ID)
      @PrimaryGeneratedColumn('uuid')
      id: string
    
      @Field()
      @Column({ unique: true })
      name: string
    
      @Field({ nullable: true })
      @Column({ nullable: true })
      description?: string
    
      @Field({ nullable: true })
      @Column({ nullable: true })
      timezone?: string
    
      @Field({ defaultValue: false })
      @Column({ default: false })
      systemFlag: boolean
    
      @Field({ nullable: true })
      @Column({ nullable: true })
      subdomain?: string
    
      @Field({ nullable: true })
      @Column({ nullable: true })
      brandName?: string
    
      @Field({ nullable: true })
      @Column({ nullable: true })
      brandImage?: string
    
      @Column({ nullable: true })
      contentImage?: string
    
      @Field({ nullable: true })
      @Column({ nullable: true })
      theme?: string
    
      @Field()
      @CreateDateColumn()
      createdAt: Date
    
      @Field()
      @UpdateDateColumn()
      updatedAt: Date
    }
  2. write resolver

    import { Context } from 'koa'
    import { Arg, Args, Ctx, Mutation, Query, Resolver } from 'type-graphql'
    import { Repository } from 'typeorm'
    import { InjectRepository } from 'typeorm-typedi-extensions'
    import { Domain } from '../../entities'
    import { buildQuery } from '../list-query-builder'
    import { CreateDomainInput } from '../types/domain/create-domain-input'
    import { DomainList } from '../types/domain/domain-list'
    import { UpdateDomainInput } from '../types/domain/domain-patch'
    import { ListParam } from '../types/list-param'
    
    @Resolver(Domain)
    export class DomainResolver {
      constructor(@InjectRepository(Domain) private readonly domainRepository: Repository<Domain>) {}
    
      @Query(returns => Domain)
      async domain(@Arg('name') name: string) {
        return this.domainRepository.findOne({ name })
      }
    
      @Query(returns => DomainList)
      async domains(@Args() params: ListParam, @Ctx() context: Context & Record<string, any>) {
        const queryBuilder = this.domainRepository.createQueryBuilder()
        buildQuery(queryBuilder, params, context, false)
        const [items, total] = await queryBuilder.getManyAndCount()
    
        return { items, total }
      }
    
      @Mutation(returns => Domain)
      async createDomain(@Arg('domain') domain: CreateDomainInput) {
        return this.domainRepository.save(domain)
      }
    
      @Mutation(returns => Boolean)
      async deleteDomain(@Arg('name') name: string) {
        return this.domainRepository.delete({ name })
      }
    
      @Mutation(returns => Domain)
      async updateDomain(@Args() { name, patch }: UpdateDomainInput) {
        const repository = this.domainRepository
        const domain = await repository.findOne({ name })
    
        return await repository.save({
          ...domain,
          ...patch
        })
      }
    }
  3. export resolvers
    import { DomainResolver } from './domain.resolver'
    export default [DomainResolver]