npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

nexus-typeorm-plugin

v0.0.1-alpha.12

Published

Create schemas from TypeORM models easily with this nexus plugin. Comes with dataloader!

Downloads

18

Readme

CircleCI

nexus-typeorm-plugin

Create a GraphQL.js schema from your TypeORM Entities.

Usage

Here's example Apollo Server with a posts resolver to paginate over the Post entity.

import 'reflect-metadata'

import * as path from 'path'
import dotenv from 'dotenv'
import { ApolloServer } from 'apollo-server'
import {
  Column,
  ManyToOne,
  OneToMany,
  PrimaryGeneratedColumn,
  createConnection,
  ManyToMany,
} from 'typeorm'
import {
  NexusEntity,
  nexusTypeORMPlugin,
  entityType,
  FindManyResolveFnContext,
} from 'nexus-typeorm-plugin'
import { queryType, makeSchema } from 'nexus'
import { propertyPathToAlias } from 'nexus-typeorm-plugin/dist/query-builder'

dotenv.config()

@NexusEntity()
export class User {
  @PrimaryGeneratedColumn()
  public id!: number

  @Column()
  public name: string

  @Column()
  public age: number

  @OneToMany(() => Post, post => post.author)
  public posts: Post[]

  @ManyToMany(() => Category, category => category.posts)
  public categories?: Category[]

  constructor(name: string, age: number, posts: Post[], categories?: Category[]) {
    this.name = name
    this.age = age
    this.posts = posts
    this.categories = categories
  }
}

@NexusEntity()
class Category {
  @PrimaryGeneratedColumn()
  public id!: number

  @Column()
  public name: string

  @ManyToMany(() => Post, post => post.categories)
  public posts?: Post[]

  constructor(name: string, posts?: Post[]) {
    this.name = name
    this.posts = posts
  }
}

@NexusEntity()
class Post {
  @PrimaryGeneratedColumn()
  public id!: number

  @Column()
  public title: string

  @ManyToOne(() => User, user => user.posts)
  public author: User

  @ManyToMany(() => Category, category => category.posts)
  public categories?: Category[]

  constructor(title: string, author: User, categories?: Category[]) {
    this.title = title
    this.author = author
    this.categories = categories
  }
}

const { DB_HOST, DB_TYPE, DB_NAME, DB_USERNAME, DB_PASSWORD, DB_PORT } = process.env

async function main() {
  await createConnection({
    entities: [User, Post, Category],
    host: DB_HOST,
    type: DB_TYPE as 'postgres',
    database: DB_NAME,
    username: DB_USERNAME,
    password: DB_PASSWORD,
    port: DB_PORT ? parseInt(DB_PORT as any, 10) : undefined,
    synchronize: true,
  })

  const query = queryType({
    definition: t => {
      t.crud.posts()
      t.crud.users('listUsers')
      t.crud.users('listUsersWithNameJohn', {
        resolve: (ctx: FindManyResolveFnContext<User, User, any, any>) => {
          ctx.args.where = {
            ...ctx.args.where,
            name: 'John',
          }

          return ctx.next(ctx)
        },
      })
      t.crudField('listPostsWithCategoryFoo', {
        entity: 'Post',
        type: 'Post',
        method: 'findMany',
        resolve: ctx => {
          return ctx.next({
            ...ctx,
            queryBuilderConfig: config => ({
              ...config,
              joins: [
                ...(config.joins || []),
                {
                  type: 'inner',
                  select: false,
                  propertyPath: 'categories',
                  where: {
                    expression: `${propertyPathToAlias('categories')}.id = :id`,
                    params: { id: 1 },
                  },
                },
              ],
            }),
          })
        },
      })
    },
  })

  const schema = makeSchema({
    types: [nexusTypeORMPlugin(), query, entityType(User), entityType(Post), entityType(Category)],
    outputs: {
      schema: path.resolve('generated', 'schema.graphql'),
      typegen: path.resolve('generated', 'nexus-typegen.ts'),
    },
  })
  new ApolloServer({ schema }).listen(3000)
  console.log('Server running at http://localhost:3000')
}

main().catch(error => {
  // eslint-disable-next-line no-console
  console.error(error)
  process.exit(1)
})

Features

Entity field definition

Helps you create an objectType for an entity faster and simpler.

export const User = entityType<User>(User, {
  definition: t => {
    t.entity.id()
    // Either t.entityField('name') or
    t.entity.name()

    // Either t.crudField('followers', { entity: 'UserFollow', ... or
    t.crud.userFollows('followers', {
      type: 'User',
      resolve: async (source: User, args, ctx, info, next) => {
        const follows = await next(source, args, ctx, info)

        return getConnection()
          .getRepository(User)
          .createQueryBuilder()
          .where('id IN (:...ids)', {
            ids: follows.map((follow: UserFollows) => follow.followerId),
          })
          .getMany()
      },
    })
  },
})

CRUD

Find Many

Creates a field that resolves into a list of instances of the choosen entity. It includes the first, last, after, before, skip, where, and the orderBy arguments.

export const Query = queryType({
  definition(t) {
    t.crud.posts()
    t.crud.users('listUsers')
    t.crud.users('listUsersWithNameJohn', {
      resolve: ctx => {
        ctx.args.where = {
          ...ctx.args.where,
          name: 'John',
        }

        return ctx.next(ctx)
      },
    })
    t.crudField('listPostsWithCategoryFoo', {
      entity: 'Post',
      method: 'findMany',
      resolve: ctx => {
        return ctx.next({
          ...ctx,
          queryBuilderConfig: config => ({
            ...config,
            joins: [
              ...config.joins,
              {
                type: 'inner',
                select: false,
                propertyPath: 'categories',
                where: {
                  expression: `${propertyPathToAlias('categories')}.id = :id`,
                  params: { id: 1 },
                },
              },
            ],
          }),
        })
      },
    })
  },
})

Find One

Creates a field that resolves into one entity instance. It includes the where and the orderBy arguments.

export const Query = queryType({
  definition(t) {
    t.crud.user()
    t.crud.post()
    t.crud.post('postsByUserId', {
      args: args => ({ ...args, userId: stringArg({ nullable: false }) }),
      resolve: ctx =>
        ctx.next({
          ...ctx,
          args: { ...ctx.args, where: { ...ctx.args.where, userId: ctx.args.userId } },
        }),
    })
  },
})

Create One

Creates a field that resolves into one entity instance. It includes the where and the orderBy arguments.

export const Mutation = mutationType({
  definition(t) {
    t.crud.createOneUser()
    t.crud.createOnePost()
  },
})

Example

mutation {
  createOneUser(
    data: {
      name: "John with posts"
      age: 42
      type: NORMAL
      profile: { create: { slug: "john-with-posts", displayName: "displayName" } }
      posts: {
        create: [
          {
            title: "created post"
            isPublic: false
            categories: { create: [{ name: "create category 1" }, { name: "create category 2" }] }
          }
          { title: "created post 2", isPublic: true }
        ]
      }
    }
  ) {
    id
    name
    age
    profile {
      slug
      displayName
    }
    posts {
      title
      isPublic
      categories {
        name
      }
    }
  }
}

Add business logic

export const Mutation = mutationType({
  definition(t) {
    t.crud.createOneUser('addUser', {
      args: {
        name: stringArg({ nullable: false }),
        postsIds: stringArg({ list: true, nullable: false }),
      },
      resolve: ctx => {
        const { name, postsIds } = ctx.args
        return ctx.next({
          ...ctx,
          args: {
            data: {
              name,
              posts: {
                connect: { id_in: postsIds },
              },
            },
          },
        })
      },
    })
  },
})

Auto join

In order to speed up requests and decrease the number of queries made to the database, this plugin analyzes each graphql query and makes the necessary joins automatically.

{
  user {
    id
    posts {
      id
    }
  }
}

Generates a SQL query that left joins Post.

SELECT * from User ... LEFT JOIN Post

Checkout the tests/ directory to see examples.

Contributing

To run tests create .env file at the project root and fill it with database information.

TEST_DB_HOST=localhost
TEST_DB_TYPE=mysql
TEST_DB_NAME=test
TEST_DB_USERNAME=root
TEST_DB_PASSWORD=mypassword
TEST_DB_PORT=3316

If you want, you can run a Docker container of MySQL for test based on .env file.

docker-compose up -d

Now you can run tests.

yarn test