Code Monkey home page Code Monkey logo

vesper's Introduction

Vesper is a NodeJS framework that helps you to create scalable, maintainable, extensible, declarative and fast GraphQL-based server applications. It perfectly fits any architecture and scale you choose - from monoliths to microservices, from small to enterprise apps.

Using Vesper your app's core components are:

  • Controllers (root queries)
  • Args (e.g. GraphQL resolver args or user input)
  • Models
  • Resolvers
  • Services
  • GraphQL schemas defined in .graphql format

Vesper provides you following features:

  • Controllers framework for your root queries
  • Maintainable GraphQL resolvers framework with data loader out of the box
  • User input validation framework with ability to use any validation library
  • User authorization and access control framework
  • Modules framework for large and scalable applications
  • Integration with TypeORM and ability to use any other ORM or database source
  • Automatic database relations resolver
  • Automatic transaction wrapper for your mutations
  • Powerful service container for code organization and seamless testing
  • Ability to use model interfaces across backend and frontend
  • Uses Express and Apollo Server and you have all power of these tools

And more...

Quick Start

To create a new JavaScript project using Vesper install it globally and use init command with --javascript flag:

npm i vesper -g
vesper init --name my-project --javascript

To create a new TypeScript project using Vesper install it globally and use init command with --typescript flag:

npm i vesper -g
vesper init --name my-project --typescript

Documentation

Links

Contributing

Want to contribute? Vesper is opened for any contributions, just create a new github issue!

vesper's People

Contributors

josephktcheung avatar pleerock avatar shimarulin avatar singingwolfboy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

vesper's Issues

A few questions / ideas

  • Why do controllers and resolvers need to be defined in separate files? I feel like with Graphql, the resolver pattern is all that is needed, and I'm not sure why the distinction needs to be made.
  • I started my own Express setup before seeing Vesper, and I'm autoloading my entities, typeDefs and resolvers based on file naming conventions. Is this something that you'd be interested in doing with Vesper? (I know Typeorm already allows this for entities.)
  • How risky would you say it'd be to use Vesper at this stage of the project?
  • Have you looked into Prisma? It auto generates CRUD operations based on graphql models. Is this something you'd be interested in doing with Vesper?

Project outdated?

Project is awesome. I used it several times.
But I'm a little confused by what the project looks abandoned. Is have any plans for future versions?

I don't found roadmap or project board.
No contribution guide, not clearly how contribute start.

About setupContainer

First, Great work. I really believe node/typescript need this kind of framework to provide best practice and help speed up development.

Question about setupContainer.

In your sample: authorization & subscription. Both use setupContainer. Does it make sense to make setupContainer as an array instead?

Samples not working at all

I wanted to test how subscriptions work but I'm not able to run the samples, e.g. sample11-subscribers:

E:\#Programowanie\#GitHub\vesper\sample\typescript\sample11-subscribers> npx ts-node index.ts
TypeError: this.connection.createQueryRunner is not a function

I've commented the lines with entityManager but subscriptions still doesn't work.
I also don't see an option to set the topic list of subscriptions - only the method name is used as trigger.

@pleerock
I appreciate your work but it looks like the samples are just a bunch of lines of code that show the API draft, not the real use cases that users can run and experiment with it.
I think that you would catch this bugs if you have tests - I'm shocked that you can create a big framework without single line of tests ๐Ÿ˜จ

Self referencing / tree entities don't work

Self referencing entities don't work at the moment. Here's reproduction branch https://github.com/josephktcheung/typescript-advanced-example/tree/feature/self_referencing.

Here's my category with tree structure (adjacency list):

import { Column, Entity, ManyToMany, PrimaryGeneratedColumn, ManyToOne, OneToMany } from "typeorm";
import { Post } from "./Post";

@Entity()
export class Category {

    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    name: string;

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

    @ManyToOne(() => Category, category => category.children)
    parent: Category;

    @OneToMany(() => Category, category => category.parent)
    children: Category[];
}

Here's how I add child category to parent category in controller:

import { Controller, Mutation, Query } from "vesper";
import { EntityManager } from "typeorm";
import { Category } from "../entity/Category";
import { CategorySaveArgs } from "../args/CategorySaveArgs";
import { AddCategoryChildArgs } from '../args/AddCategoryChildArgs';

@Controller()
export class CategoryController {

    constructor(private entityManager: EntityManager) {
    }

    @Query()
    categories(): Promise<Category[]> {
        return this.entityManager.find(Category);
    }

    @Query()
    category({ id }: { id: number }): Promise<Category> {
        return this.entityManager.findOne(Category, id);
    }

    @Mutation()
    async categorySave(args: CategorySaveArgs): Promise<Category> {
        const category = args.id ? await this.entityManager.findOneOrFail(Category, args.id) : new Category();
        category.name = args.name;
        return this.entityManager.save(category);
    }

    @Mutation()
    async addCategoryChild(args: AddCategoryChildArgs): Promise<Category> {
        const parent = await this.entityManager.findOneOrFail(Category, args.parentId);
        const child = await this.entityManager.findOneOrFail(Category, args.childId);
        child.parent = parent;

        return this.entityManager.save(child);
    }
}

Here's my category graphql:

type Category {
    id: Int
    name: String
    posts: [Post]
    parent: Category
    children: [Category]
}

Create 2 categories, assign child to parent using the following mutation:

mutation {
  addCategoryChild(parentId: 1, childId: 2) {
    id
    name
    parent {
      id
      name
    }
    children {
      id
      name
    }
  }
}

If I query categories like this:

{
  categories {
    id
    name
    parent {
      id
      name
    }
    children {
      id
      name
    }
  }
}

The result is:

{
  "data": {
    "categories": [
      {
        "id": 1,
        "name": "category #1",
        "parent": {
          "id": 1,
          "name": "category #1"
        },
        "children": []
      },
      {
        "id": 2,
        "name": "category #2",
        "parent": null,
        "children": []
      }
    ]
  }
}

You can see that category id 1's parent is itself and it has no children, while category id 2 has no parent. But if you look at the category table in sqlite the relationship is set up correctly:
screen shot 2018-04-27 at 5 22 29 pm

I believe there's something wrong with typeorm's RelationIdLoader that it cannot handle self referencing entities correctly. For example, this is one of the sql queries typeorm generates when the categories query is run:

query: SELECT "Category"."id" AS "Category_id", "Category"."parentId" AS "Category_id" FROM "category" "Category" WHERE "Category"."parentId" IN (?, ?) -- PARAMETERS: [1,2]

You can see that when selecting primary column and join column ids, the join column id is selected as "Category_id" which is the same as the primary column alias.

GraphQL schema subset query is selecting all fields in the model from the database

Running any query using just a subset of schema fields is running queries on all fields in the model which ultimately kills performance.

{
  solution_all {
    solutionid
  }
}

is running this query.

SELECT "Solution"."configurationpageid" AS "Solution_configurationpageid", "Solution"."createdby" AS "Solution_createdby", "Solution"."createdon" AS "Solution_createdon", "Solution"."createdonbehalfby" AS "Solution_createdonbehalfby", "Solution"."description" AS "Solution_description", "Solution"."friendlyname" AS "Solution_friendlyname", "Solution"."installedon" AS "Solution_installedon", "Solution"."isinternal" AS "Solution_isinternal", "Solution"."ismanaged" AS "Solution_ismanaged", "Solution"."isvisible" AS "Solution_isvisible", "Solution"."modifiedby" AS "Solution_modifiedby", "Solution"."modifiedon" AS "Solution_modifiedon", "Solution"."modifiedonbehalfby" AS "Solution_modifiedonbehalfby", "Solution"."organizationid" AS "Solution_organizationid", "Solution"."parentsolutionid" AS "Solution_parentsolutionid", "Solution"."pinpointassetid" AS "Solution_pinpointassetid", "Solution"."pinpointsolutiondefaultlocale" AS "Solution_pinpointsolutiondefaultlocale", "Solution"."pinpointsolutionid" AS "Solution_pinpointsolutionid", "Solution"."publisherid" AS "Solution_publisherid", "Solution"."solutionid" AS "Solution_solutionid", "Solution"."solutionpackageversion" AS "Solution_solutionpackageversion", "Solution"."solutiontype" AS "Solution_solutiontype", "Solution"."uniquename" AS "Solution_uniquename", "Solution"."version" AS "Solution_version", "Solution"."versionnumber" AS "Solution_versionnumber" FROM "solutionbase" "Solution"

instead of.

SELECT "Solution"."solutionid" AS "Solution_solutionid" FROM "solutionbase" "Solution"

Is there a way to make this happen? I haven't even touched on relationships yet but I am assuming that it will be the same outcome.

Is it possible to resolve multiple fields in one function?

Is it possible to resolve multiple fields at once in a single custom resolver function? I realize this is an unusual request, so let me explain my use-case.

I want to create an application that can track changes over time, and respond to date-based queries. For example, lets say I want to track how people move over time. I want to allow queries that look like this:

query {
  people {
    name
    homeAddress
  }
}

Then, I could specify a date in a HTTP header, include that value in the context object, and use that date when making queries. The entities look like this:

@Entity()
export class Person extends BaseEntity {
  @PrimaryGeneratedColumn() id: number;

  // these are all saved in the PersonKVString model
  name: string;
  homeAddress: string;

  @OneToMany(type => PersonKVString, kvs => kvs.person)
  personKVStrings: PersonKVString[];
}
@Entity()
export class PersonKVString extends BaseEntity {
  @PrimaryGeneratedColumn() id: number;

  @ManyToOne(type => Person, person => person.personKVStrings)
  person: Person;

  @Column() key: string;
  @Column() value: string;

  @Column({ name: "start_on", nullable: true })
  startOn: Date;
  @Column({ name: "end_on", nullable: true })
  endOn: Date;
}

As you can see, the PersonKVString entity holds a key-value pairing, along with a time duration that the key-value pairing is valid. I currently have a custom resolver that looks like this:

const resolverQuery = (
  key: string,
  people: Person[],
  date: string,
  entityManager: EntityManager
): Promise<string[]> => {
  const personIds = people.map(person => person.id);
  return entityManager
    .createQueryBuilder(PersonKVString, "kvs")
    .innerJoinAndSelect(
      "kvs.person",
      "person",
      "person.id IN (:...personIds) AND COALESCE(kvs.start_on, '-infinity') <= :date AND COALESCE(kvs.end_on, 'infinity') >= :date",
      { personIds, date }
    )
    .where(`kvs.key = '${key}'`)
    .orderBy("kvs.start_on", "DESC", "NULLS LAST")
    .getMany()
    .then(ary_kvs => {
      return people.map(person => {
        const kvs = ary_kvs.find(kvs => kvs.person.id === person.id);
        return kvs ? kvs.value : null;
      });
    });
};

@Resolver(Person)
export class PersonResolver implements ResolverInterface<Person> {
  constructor(private entityManager: EntityManager) {}

  @Resolve()
  name(people: Person[], args: any, context: any) {
    const date = context.date || "today";
    return resolverQuery("name", people, date, this.entityManager);
  }

  @Resolve()
  homeAddress(people: Person[], args: any, context: any) {
    const date = context.date || "today";
    return resolverQuery("homeAddress", people, date, this.entityManager);
  }
}

This works, but as you can see, it generates one complex resolver query for every field that the client requests. Ideally, I'd like to generate only one query, which fetches the PersonKVString entities for every field requested. I figured that I might be able to do that, if I could write one resolver that handles every requested field at once.

Any thoughts? Am I solving this problem the wrong way?

Implementing Relay-compatible connections

Hi there, Vesper looks like a useful framework, and I'm trying to use it for my app. However, I can't figure out how to implement Relay-compatible connections in my resolvers; the type signature seems to expect that I will return either a list of database entities, or a promise that returns that. Instead, I need to return a plain JavaScript object with at least the following information:

{
  edges: [
    {
      node: entity,
      cursor: encodeCursor(entity)
    },
    // ...repeated as necessary
  ],
  pageInfo: {
    hasPreviousPage,
    hasNextPage,
    startCursor,
    endCursor,
    totalCount
  }
}

where entity is the database entity, and encodeCursor is a function that returns a cursor string so that a subsequent API call can paginate starting from that entity.

Is it possible to do this? And if so, how? Can you provide a code example? I'm happy to provide my code, if you need.

Query runner already released. Cannot run queries anymore.

I'm using postgres in vesper's typescript-advanced-example. I got this Query runner already released. Cannot run queries anymore. error if I run a mutation and retrieve categoryNames like this:

mutation PostSave {
  postSave(title: "lol", text: "lol2", categoryIds: [1]) {
    id
    categories {
      id
      name
    }
    categoryNames
  }
}

It works fine if I use sqlite. No idea why this happens. Perhaps it's a typeorm's bug?

Apollo context

Is it still possible to use Apollo context option for authentication like DataLoader is still avalable through context IIRC seeing the code.

If so how do I do it as the following does not seem to be being called at all.

    app.use("/graphql", bodyParser.json(), vesper(schema, {
        setupContainer: async (container, action) => {
            console.log("setupContainer()");
            const request = action.request;
            const token:string = request.headers["token"] as string || "";

            if (token !== "") {
                const entityManager = getManager();
                const payload = jwt.verify(token, "secret");
                const currentUser = await entityManager.findOneOrFail(User, { id: payload["id"] });

                container.set(User, currentUser);
                console.log("this.currentUser = ", currentUser);
            }
        }
    }));

as the above does not seem to be doing anything a

https and integration

Hello,

I can't find a way to run https from bootstrap options or to quickly develop using https://localhost:3000.
Is it possible to integrate with custom https server or express to serve static files for example, or vesper is only recommended as a microservice ?

Thank you in advance for your help.

Batch resolve method should be invoked N times for N aliases with different arguments.

vesper: 0.1.9

Consider the following query

{
  queryA {
    entitiesA {
      aRelations: relations(filter: A)
      bRelations: relations(filter: B)
    }
  }
}

In the resolver for EntityA, we have a @resolve method async relations(entities: EntityA[]).

Current behavior

  1. Resolver method is only being invoked once with { filter: A }
  2. the result for bRelations are the same as result for aRelations

Expected behavior

  1. Resolver method should be invoked twice with both { filter: A } and { filter: B }
  2. the result for bRelations should be corresponding the { filter: B }

Any Example for Basic Authentication

Hello,

I search a Example how we can setup the request authorization.
I think that is in authorizationChecker, but if I thow HttpQueryError that generate a error in output that doesn't send the error code and header.
I have extend the class HttpQueryError to setup default value.

class AuthenticationError extends HttpQueryError {

   constructor(
      statusCode?: number,
      message?: string,
      isGraphQLError: boolean = false,
      headers?: { [key: string]: string }
   ) {
      super(statusCode||401, message||"Access denied", isGraphQLError, headers || {
         'WWW-Authenticate': 'Basic realm="Test Eri"'
      });
   }
}

Regards.

Config (like api keys, password salts, analytic keys, etc)

I see in the project structure there is a config.json mentioned but the sample projects don't implement them and I can't see docs about it. Where is the best place to store keys that shouldn't be in a repo and/or should change per-environment?

Documentation on Authorized decorator and authorizationChecker?

Hi,

I found out there's a @Authorized decorator in sample10-current-user but we need to set authorizationChecker in the SchemaBuilderOptions in order to make it work. It'd be great if there's a simple example of setting authorizationChecker in the documentation and the sample.

Thanks,
Joseph

Resolver doesn't work with subscription

Subscription fails if a field is resolved by a resolver.

Reproduction Steps

demo code available at https://github.com/josephktcheung/vesper/tree/feature/resolver_subscription, run sample 11 with npx ts-node sample/typescript/sample11-subscribers/index.ts

  1. Add a new test field for Message
import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";

@Entity()
export class Message {

    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    text: string;

    @Column()
    receiver: number;

    test: string;
}
  1. Add a resolver for the new field.
import { Resolve, Resolver, ResolverInterface } from "../../../../src";
import { EntityManager } from "typeorm";
import { Message } from "../entity/Message";

@Resolver(Message)
export class MessageResolver implements ResolverInterface<Message> {
    constructor(private entityManager: EntityManager) {
    }

    @Resolve()
    async test(messages: Message[]) {
        return messages.map(m => 'test');
    }
}
  1. Include resolver in bootstrap:
import { bootstrap } from "../../../src";
import { MessageController } from "./controller/MessageController";
import { Message } from "./entity/Message";
import { PubSub } from "graphql-subscriptions";
import { MessageResolver } from "./resolver/MessageResolver";

const pubSub = new PubSub();

bootstrap({
    port: 3000,
    controllers: [MessageController],
    resolvers: [MessageResolver],
    entities: [Message],
    schemas: [__dirname + "/schema/**/*.graphql"],
    setupContainer: container => container.set(PubSub, pubSub),
    subscriptionAsyncIterator: triggers => pubSub.asyncIterator(triggers)
});
  1. Add test field to Message.graphql
type Message {
    id: Int
    text: String
    receiver: Int
    test: String
}
  1. Subscribe to messageSent with new test field:
subscription {
  messageSent(me: 1) {
    id
    text
    test
  }
}
  1. Create a new message
mutation {
  messageSave(id: 1, receiver: 1, text: "Hi") {
    id
    text
    test
  }
}
  1. Following error appears in console:
TypeError: Cannot read property 'Message' of undefined
    at resolvers.(anonymous function).(anonymous function) (/Users/josephcheung/workspace/zeep/repos/vesper/src/SchemaBuilder.ts:374:45)
    at /Users/josephcheung/workspace/zeep/repos/vesper/node_modules/graphql-tools/src/schemaGenerator.ts:683:22
    at resolveFieldValueOrError (/Users/josephcheung/workspace/zeep/repos/vesper/node_modules/graphql/execution/execute.js:531:18)
    at resolveField (/Users/josephcheung/workspace/zeep/repos/vesper/node_modules/graphql/execution/execute.js:495:16)
    at /Users/josephcheung/workspace/zeep/repos/vesper/node_modules/graphql/execution/execute.js:364:18
    at Array.reduce (<anonymous>)
    at executeFields (/Users/josephcheung/workspace/zeep/repos/vesper/node_modules/graphql/execution/execute.js:361:42)
    at collectAndExecuteSubfields (/Users/josephcheung/workspace/zeep/repos/vesper/node_modules/graphql/execution/execute.js:772:10)
    at completeObjectValue (/Users/josephcheung/workspace/zeep/repos/vesper/node_modules/graphql/execution/execute.js:762:10)
    at completeValue (/Users/josephcheung/workspace/zeep/repos/vesper/node_modules/graphql/execution/execute.js:660:12)

Multi connexion DB

How use multi connexion typeOrm in controller ?

It's possible to use one connexion and set name of db dynamically in index.ts for exemple ?

Need a way to set typeorm connection charset

Hi Vesper team,

First of all, thanks for this great framework! It works quite well together with TypeORM.

One thing that I couldn't find a solution myself is a way to set the connection charset for MySQL. With Typeorm, I can do the following

  // read connection options from ormconfig file (or ENV variables)
  const connectionOptions = await getConnectionOptions();

  // create a connection using modified connection options
  const connection = await createConnection({
    ...connectionOptions,
    charset: 'UTF8MB4_GENERAL_CI',
  } as MysqlConnectionOptions);

It's not been loaded from option reader so this seems the only option.
I'm thinking we can either allow passing a connection to Vesper or Vesper reuse the existing default connection automatically (instead of die with AlreadyHasActiveConnectionError).

Question: Is SQL generation correct?

I have two entities, Solution and SolutionComponent. Solution has a OneToMany relationship with SolutionComponent and SolutionComponent has ManyToOne relationship to Solution as required by TypeORM.

Solution

  @OneToMany(() => SolutionComponent, solutionComponent => solutionComponent.solutionid_ref)
  @JoinColumn({ name: "solutionid" })
  solutioncomponent_refs: [SolutionComponent];

SolutionComponent

  @ManyToOne(() => Solution, solution => solution.solutioncomponent_refs)
  @JoinColumn({ name: "solutionid" })
  solutionid_ref: Solution;

In the SolutionController I have a standard entityManager find statement.

    @Query()
    solution_all(): Promise<Solution[]> {
        return this.entityManager.find(Solution);
    }

And run this query using the following GraphQL query on mssql.

{
  solution_all {
    solutionid
    solutioncomponent_refs {
      solutioncomponentid
    }
  }
}

Sorry for the long output but necessary. This generates the following 3 T-SQL queries and takes approx 1 minute to run.

query: SELECT "Solution"."configurationpageid" AS "Solution_configurationpageid", "Solution"."createdby" AS "Solution_createdby", "Solution"."createdon" AS "Solution_createdon", "Solution"."createdonbehalfby" AS "Solution_createdonbehalfby", "Solution"."description" AS "Solution_description", "Solution"."friendlyname" AS "Solution_friendlyname", "Solution"."installedon" AS "Solution_installedon", "Solution"."isinternal" AS "Solution_isinternal", "Solution"."ismanaged" AS "Solution_ismanaged", "Solution"."isvisible" AS "Solution_isvisible", "Solution"."modifiedby" AS "Solution_modifiedby", "Solution"."modifiedon" AS "Solution_modifiedon", "Solution"."modifiedonbehalfby" AS "Solution_modifiedonbehalfby", "Solution"."organizationid" AS "Solution_organizationid", "Solution"."parentsolutionid" AS "Solution_parentsolutionid", "Solution"."pinpointassetid" AS "Solution_pinpointassetid", "Solution"."pinpointsolutiondefaultlocale" AS "Solution_pinpointsolutiondefaultlocale", "Solution"."pinpointsolutionid" AS "Solution_pinpointsolutionid", "Solution"."publisherid" AS "Solution_publisherid", "Solution"."solutionid" AS "Solution_solutionid", "Solution"."solutionpackageversion" AS "Solution_solutionpackageversion", "Solution"."solutiontype" AS "Solution_solutiontype", "Solution"."uniquename" AS "Solution_uniquename", "Solution"."version" AS "Solution_version", "Solution"."versionnumber" AS "Solution_versionnumber" FROM "solutionbase" "Solution"

query: SELECT "solutioncomponent_refs"."componenttype" AS "solutioncomponent_refs_componenttype", "solutioncomponent_refs"."createdby" AS "solutioncomponent_refs_createdby", "solutioncomponent_refs"."createdon" AS "solutioncomponent_refs_createdon", "solutioncomponent_refs"."createdonbehalfby" AS "solutioncomponent_refs_createdonbehalfby", "solutioncomponent_refs"."ismetadata" AS "solutioncomponent_refs_ismetadata", "solutioncomponent_refs"."modifiedby" AS "solutioncomponent_refs_modifiedby", "solutioncomponent_refs"."modifiedon" AS "solutioncomponent_refs_modifiedon", "solutioncomponent_refs"."modifiedonbehalfby" AS "solutioncomponent_refs_modifiedonbehalfby", "solutioncomponent_refs"."objectid" AS "solutioncomponent_refs_objectid", "solutioncomponent_refs"."rootcomponentbehavior" AS "solutioncomponent_refs_rootcomponentbehavior", "solutioncomponent_refs"."rootsolutioncomponentid" AS "solutioncomponent_refs_rootsolutioncomponentid", "solutioncomponent_refs"."solutioncomponentid" AS "solutioncomponent_refs_solutioncomponentid", "solutioncomponent_refs"."solutionid" AS "solutioncomponent_refs_solutionid", "solutioncomponent_refs"."versionnumber" AS "solutioncomponent_refs_versionnumber" FROM "solutioncomponentbase" "solutioncomponent_refs" WHERE "solutioncomponent_refs"."solutionid" IN (@0, @1, @2, @3, @4, @5, @6, @7, @8, @9, @10, @11, @12, @13, @14, @15, @16, @17, @18, @19, @20, @21, @22, @23, @24, @25, @26, @27, @28, @29, @30, @31, @32, @33, @34, @35, @36, @37, @38, @39, @40, @41, @42, @43, @44, @45, @46, @47, @48, @49, @50, @51, @52, @53, @54, @55, @56, @57, @58, @59, @60, @61, @62, @63, @64, @65, @66, @67, @68, @69, @70, @71, @72, @73, @74, @75, @76, @77, @78, @79, @80, @81, @82, @83, @84, @85, @86, @87, @88, @89, @90, @91, @92, @93, @94, @95, @96, @97, @98, @99, @100, @101, @102, @103, @104, @105, @106, @107, @108, @109, @110, @111, @112, @113, @114, @115, @116, @117, @118, @119, @120, @121, @122, @123, @124, @125, @126, @127, @128, @129, @130) -- PARAMETERS: ["FD140AAD-4DF4-11DD-BD17-0019B9312238","FD140AAE-4DF4-11DD-BD17-0019B9312238","FD140AAF-4DF4-11DD-BD17-0019B9312238","D83099BC-67BF-E511-80C1-005056930C8E","83CB08CD-28CF-E511-80C3-005056930C8E","2ABCA093-3DCF-E511-80C3-005056930C8E","055FD054-9CD4-E511-80C5-005056930C8E","91D975F4-A0D4-E511-80C5-005056930C8E","FBCE987E-CFDF-E511-80CA-005056930C8E","B98664BC-5EE4-E511-80CA-005056930C8E","419E178B-6AE4-E511-80CA-005056930C8E","BC8869E6-DCE5-E511-80CA-005056930C8E","A28480F9-6BE7-E511-80CA-005056930C8E","203136CB-86EF-E511-80CA-005056930C8E","51D3BFCE-DF15-E611-80CB-005056930C8E","1A95E991-DE1C-E611-80CB-005056930C8E","85775DC9-8D3F-E611-80CF-005056930C8E","A2C7A145-2F44-E611-80D2-005056930C8E","C8B047D0-C849-E611-80D3-005056930C8E","A3E03268-0D5F-E611-80D3-005056930C8E","634C20BC-5C6C-E611-80D3-005056930C8E","5BF449CD-12A0-E611-80D3-005056930C8E","15D88CBE-D1A1-E611-80D3-005056930C8E","2F3D5987-20E3-E611-80D9-005056930C8E","8720EF42-7AEC-E611-80D9-005056930C8E","2F1AFE57-54F8-E611-80DA-005056930C8E","0BF71A63-DAFC-E611-80DA-005056930C8E","227742A4-69FF-E611-80DA-005056930C8E","4742F634-2B0F-E711-80DB-005056930C8E","9F88B2AD-9110-E711-80DB-005056930C8E","14F22083-0A1A-E711-80DC-005056930C8E","D0832DF6-A41A-E711-80DC-005056930C8E","2005CAC8-DB3A-E711-80E0-005056930C8E","99434124-4D41-E711-80E0-005056930C8E","7A5EB4B1-1566-E711-80E1-005056930C8E","DF649B0E-7C7B-E711-80E1-005056930C8E","A53AB1EF-E296-E711-80EB-005056930C8E","3FAA8010-309F-E711-80EC-005056930C8E","51F51093-EAA4-E711-80EC-005056930C8E","6AA1C216-60BD-E711-80ED-005056930C8E","6BDF76BA-E4CD-E711-80F1-005056930C8E","24A6E64E-F5CD-E711-80F1-005056930C8E","2058FE3D-D4D9-E711-80F1-005056930C8E","889BA297-0CE7-E711-80F3-005056930C8E","D27731D1-E9F5-E711-80F3-005056930C8E","56C5ACB1-D9FA-E711-80F3-005056930C8E","4D90ED2C-3200-E811-80F4-005056930C8E","039BCC7D-FC04-E811-80F4-005056930C8E","FE7D70C9-1E0B-E811-80F5-005056930C8E","593904A1-610B-E811-80F5-005056930C8E","B409B63A-E60F-E811-80F5-005056930C8E","C619CF31-D117-E811-80F5-005056930C8E","957DFFCE-F21A-E811-80F6-005056930C8E","EECC742F-B71B-E811-80F6-005056930C8E","02111F1A-F521-E811-80F6-005056930C8E","7A7FD78C-8E23-E811-80F6-005056930C8E","F6F429C2-6F39-E811-80F7-005056930C8E","57078624-893D-E811-80FA-005056930C8E","AA8FB495-8641-E811-80FD-005056930C8E","8632EA82-1D43-E811-80FD-005056930C8E","6D1A5102-6048-E811-80FD-005056930C8E","F43554BE-864C-E811-80FD-005056930C8E","777D748C-8F4C-E811-80FD-005056930C8E","7117AEE4-D14E-E811-80FD-005056930C8E","D46248E4-8707-E411-BB07-005056C00008","A8BEB096-6B0E-E411-BB07-005056C00008","03109BA6-F541-4194-A230-0392ABAEA641","25A01723-9F63-4449-A3E0-046CC23A2902","0D265B52-DC31-4960-9925-074279A0B6D5","94BAF356-DB62-4EC9-BF28-0A1D2F1DBDD8","1CEEF079-F870-42AD-94CC-13A530CA5C88","8C8CBD10-2E5B-4401-9921-1582B6DE60E4","C6411348-41D0-408F-8C3D-1DF24B5A83F6","62DED6B9-4B76-4EB8-AF4C-255EA9773FB7","6A999E1E-8CE6-4475-81B2-25DEDF21A9C1","4CF405C0-F1BA-4914-85D3-2936237328CE","23BA9D0E-E3F6-4A81-92FB-2F80EFC108A5","7A649F8B-C74C-4CA3-BAC6-356DFB9F17AE","7749B0ED-CDF1-4819-ACB6-3DA9FB7B8ECF","5FC67456-D4EB-4FDD-93D3-3DAB820ED9A8","C514D95B-637B-40E6-9C11-435DA249757A","2465535D-3F77-4330-B76B-473CBE2A1AE9","32E0E5F3-3C84-4849-98A5-558B19874583","57A85212-B3B4-4C39-BCB0-5D5EF1F5FFC4","5A94B2B3-CE0C-4B81-9EE8-62BD3A010FF8","7EF6BDF7-2F37-4A1B-896B-62C33FAE1B55","750E621B-D4B8-410B-A003-7500EE65569F","DA800266-C402-4244-96C7-76F1B3235BF5","3D1BC26E-42D6-4D38-AB36-77462E08C696","557DAE4D-AB50-E111-98D8-782BCB776B8B","CA8EDDA0-6851-E111-98D8-782BCB776B8B","83869108-D438-E411-81E7-782BCB776B8C","26B47A2F-D94E-E411-81E7-782BCB776B8C","8F07D3EF-F758-E411-81E7-782BCB776B8C","EA5977EC-66D3-E211-8B26-782BCB776B8C","0FA23C60-EF82-E111-8FA3-782BCB776B8C","A863C122-4F1F-E211-9680-782BCB776B8C","BE4BD90F-1FB1-E411-9837-782BCB776B8C","177CD169-8EB2-E411-9837-782BCB776B8C","F2BA4CFC-25B8-E411-9837-782BCB776B8C","DAA8C853-39D9-E411-9B4F-782BCB776B8C","93646E7F-02DD-E411-9B4F-782BCB776B8C","31EA2187-32E4-E411-9B4F-782BCB776B8C","1918C834-9AF8-E411-9B4F-782BCB776B8C","D867CA6A-4804-E511-9B4F-782BCB776B8C","52A1C91E-2314-E511-9B4F-782BCB776B8C","B71D9424-E740-E511-9D3B-782BCB776B8C","8CA7CAE0-9D4C-E511-9D3B-782BCB776B8C","32BEC5B3-E84C-E511-9D3B-782BCB776B8C","42EAB3FD-9AA3-E411-B32F-782BCB776B8C","CBB9C768-C892-466E-8E0F-798B820DB8B1","026EBEDE-228E-473E-81FB-7CF5A56ABB5D","92B512F8-CAAE-450E-9A10-884D7545FE95","2B485265-350F-4706-BC94-8A22DDE75203","1A1CFB89-91E9-4BC3-B750-950263B723FE","3080E288-2E67-4BA7-B9E9-979D89E99039","D0BC3831-8F1B-4EC0-A380-9E1795CBD2E0","BCEBC82D-4C1D-48ED-9389-AA7937977654","5A43A936-2324-4469-9FAC-B3FAB5F117BF","B5BD1472-1F86-4B3A-ACC2-B510F5BD0D67","62D5948B-F118-4658-A6B2-CFF0AED89C97","03E346C6-17CC-4A17-B567-D2318D0803F6","4D3BBA9E-94A1-4BB6-B772-DBC4ADECBD49","92696DB3-642D-4382-BD8C-DC08CCD9946C","80A44453-3286-4DEA-AF6C-DDDA949DF15D","DD569DED-790E-4C0B-A31A-DFC786C80F02","1D31335C-0F8F-4B7D-9252-E3062103C8C0","7D76EC81-68D3-4BB5-B668-E4DA52E6922A","93E09BEC-AFAF-4F41-A74A-EF853172A75A","764D5B4E-77BE-4CB7-81A0-F51724816061","B1516D26-A644-4DCC-BA30-FF2339D957C5"]

query: SELECT "SolutionComponent"."solutioncomponentid" AS "SolutionComponent_solutioncomponent_refs_solutioncomponentid", "SolutionComponent"."solutionid" AS "Solution_solutionid" FROM "solutioncomponentbase" "SolutionComponent" WHERE "SolutionComponent"."solutionid" IN (@0, @1, @2, @3, @4, @5, @6, @7, @8, @9, @10, @11, @12, @13, @14, @15, @16, @17, @18, @19, @20, @21, @22, @23, @24, @25, @26, @27, @28, @29, @30, @31, @32, @33, @34, @35, @36, @37, @38, @39, @40, @41, @42, @43, @44, @45, @46, @47, @48, @49, @50, @51, @52, @53, @54, @55, @56, @57, @58, @59, @60, @61, @62, @63, @64, @65, @66, @67, @68, @69, @70, @71, @72, @73, @74, @75, @76, @77, @78, @79, @80, @81, @82, @83, @84, @85, @86, @87, @88, @89, @90, @91, @92, @93, @94, @95, @96, @97, @98, @99, @100, @101, @102, @103, @104, @105, @106, @107, @108, @109, @110, @111, @112, @113, @114, @115, @116, @117, @118, @119, @120, @121, @122, @123, @124, @125, @126, @127, @128, @129, @130) -- PARAMETERS: ["FD140AAD-4DF4-11DD-BD17-0019B9312238","FD140AAE-4DF4-11DD-BD17-0019B9312238","FD140AAF-4DF4-11DD-BD17-0019B9312238","D83099BC-67BF-E511-80C1-005056930C8E","83CB08CD-28CF-E511-80C3-005056930C8E","2ABCA093-3DCF-E511-80C3-005056930C8E","055FD054-9CD4-E511-80C5-005056930C8E","91D975F4-A0D4-E511-80C5-005056930C8E","FBCE987E-CFDF-E511-80CA-005056930C8E","B98664BC-5EE4-E511-80CA-005056930C8E","419E178B-6AE4-E511-80CA-005056930C8E","BC8869E6-DCE5-E511-80CA-005056930C8E","A28480F9-6BE7-E511-80CA-005056930C8E","203136CB-86EF-E511-80CA-005056930C8E","51D3BFCE-DF15-E611-80CB-005056930C8E","1A95E991-DE1C-E611-80CB-005056930C8E","85775DC9-8D3F-E611-80CF-005056930C8E","A2C7A145-2F44-E611-80D2-005056930C8E","C8B047D0-C849-E611-80D3-005056930C8E","A3E03268-0D5F-E611-80D3-005056930C8E","634C20BC-5C6C-E611-80D3-005056930C8E","5BF449CD-12A0-E611-80D3-005056930C8E","15D88CBE-D1A1-E611-80D3-005056930C8E","2F3D5987-20E3-E611-80D9-005056930C8E","8720EF42-7AEC-E611-80D9-005056930C8E","2F1AFE57-54F8-E611-80DA-005056930C8E","0BF71A63-DAFC-E611-80DA-005056930C8E","227742A4-69FF-E611-80DA-005056930C8E","4742F634-2B0F-E711-80DB-005056930C8E","9F88B2AD-9110-E711-80DB-005056930C8E","14F22083-0A1A-E711-80DC-005056930C8E","D0832DF6-A41A-E711-80DC-005056930C8E","2005CAC8-DB3A-E711-80E0-005056930C8E","99434124-4D41-E711-80E0-005056930C8E","7A5EB4B1-1566-E711-80E1-005056930C8E","DF649B0E-7C7B-E711-80E1-005056930C8E","A53AB1EF-E296-E711-80EB-005056930C8E","3FAA8010-309F-E711-80EC-005056930C8E","51F51093-EAA4-E711-80EC-005056930C8E","6AA1C216-60BD-E711-80ED-005056930C8E","6BDF76BA-E4CD-E711-80F1-005056930C8E","24A6E64E-F5CD-E711-80F1-005056930C8E","2058FE3D-D4D9-E711-80F1-005056930C8E","889BA297-0CE7-E711-80F3-005056930C8E","D27731D1-E9F5-E711-80F3-005056930C8E","56C5ACB1-D9FA-E711-80F3-005056930C8E","4D90ED2C-3200-E811-80F4-005056930C8E","039BCC7D-FC04-E811-80F4-005056930C8E","FE7D70C9-1E0B-E811-80F5-005056930C8E","593904A1-610B-E811-80F5-005056930C8E","B409B63A-E60F-E811-80F5-005056930C8E","C619CF31-D117-E811-80F5-005056930C8E","957DFFCE-F21A-E811-80F6-005056930C8E","EECC742F-B71B-E811-80F6-005056930C8E","02111F1A-F521-E811-80F6-005056930C8E","7A7FD78C-8E23-E811-80F6-005056930C8E","F6F429C2-6F39-E811-80F7-005056930C8E","57078624-893D-E811-80FA-005056930C8E","AA8FB495-8641-E811-80FD-005056930C8E","8632EA82-1D43-E811-80FD-005056930C8E","6D1A5102-6048-E811-80FD-005056930C8E","F43554BE-864C-E811-80FD-005056930C8E","777D748C-8F4C-E811-80FD-005056930C8E","7117AEE4-D14E-E811-80FD-005056930C8E","D46248E4-8707-E411-BB07-005056C00008","A8BEB096-6B0E-E411-BB07-005056C00008","03109BA6-F541-4194-A230-0392ABAEA641","25A01723-9F63-4449-A3E0-046CC23A2902","0D265B52-DC31-4960-9925-074279A0B6D5","94BAF356-DB62-4EC9-BF28-0A1D2F1DBDD8","1CEEF079-F870-42AD-94CC-13A530CA5C88","8C8CBD10-2E5B-4401-9921-1582B6DE60E4","C6411348-41D0-408F-8C3D-1DF24B5A83F6","62DED6B9-4B76-4EB8-AF4C-255EA9773FB7","6A999E1E-8CE6-4475-81B2-25DEDF21A9C1","4CF405C0-F1BA-4914-85D3-2936237328CE","23BA9D0E-E3F6-4A81-92FB-2F80EFC108A5","7A649F8B-C74C-4CA3-BAC6-356DFB9F17AE","7749B0ED-CDF1-4819-ACB6-3DA9FB7B8ECF","5FC67456-D4EB-4FDD-93D3-3DAB820ED9A8","C514D95B-637B-40E6-9C11-435DA249757A","2465535D-3F77-4330-B76B-473CBE2A1AE9","32E0E5F3-3C84-4849-98A5-558B19874583","57A85212-B3B4-4C39-BCB0-5D5EF1F5FFC4","5A94B2B3-CE0C-4B81-9EE8-62BD3A010FF8","7EF6BDF7-2F37-4A1B-896B-62C33FAE1B55","750E621B-D4B8-410B-A003-7500EE65569F","DA800266-C402-4244-96C7-76F1B3235BF5","3D1BC26E-42D6-4D38-AB36-77462E08C696","557DAE4D-AB50-E111-98D8-782BCB776B8B","CA8EDDA0-6851-E111-98D8-782BCB776B8B","83869108-D438-E411-81E7-782BCB776B8C","26B47A2F-D94E-E411-81E7-782BCB776B8C","8F07D3EF-F758-E411-81E7-782BCB776B8C","EA5977EC-66D3-E211-8B26-782BCB776B8C","0FA23C60-EF82-E111-8FA3-782BCB776B8C","A863C122-4F1F-E211-9680-782BCB776B8C","BE4BD90F-1FB1-E411-9837-782BCB776B8C","177CD169-8EB2-E411-9837-782BCB776B8C","F2BA4CFC-25B8-E411-9837-782BCB776B8C","DAA8C853-39D9-E411-9B4F-782BCB776B8C","93646E7F-02DD-E411-9B4F-782BCB776B8C","31EA2187-32E4-E411-9B4F-782BCB776B8C","1918C834-9AF8-E411-9B4F-782BCB776B8C","D867CA6A-4804-E511-9B4F-782BCB776B8C","52A1C91E-2314-E511-9B4F-782BCB776B8C","B71D9424-E740-E511-9D3B-782BCB776B8C","8CA7CAE0-9D4C-E511-9D3B-782BCB776B8C","32BEC5B3-E84C-E511-9D3B-782BCB776B8C","42EAB3FD-9AA3-E411-B32F-782BCB776B8C","CBB9C768-C892-466E-8E0F-798B820DB8B1","026EBEDE-228E-473E-81FB-7CF5A56ABB5D","92B512F8-CAAE-450E-9A10-884D7545FE95","2B485265-350F-4706-BC94-8A22DDE75203","1A1CFB89-91E9-4BC3-B750-950263B723FE","3080E288-2E67-4BA7-B9E9-979D89E99039","D0BC3831-8F1B-4EC0-A380-9E1795CBD2E0","BCEBC82D-4C1D-48ED-9389-AA7937977654","5A43A936-2324-4469-9FAC-B3FAB5F117BF","B5BD1472-1F86-4B3A-ACC2-B510F5BD0D67","62D5948B-F118-4658-A6B2-CFF0AED89C97","03E346C6-17CC-4A17-B567-D2318D0803F6","4D3BBA9E-94A1-4BB6-B772-DBC4ADECBD49","92696DB3-642D-4382-BD8C-DC08CCD9946C","80A44453-3286-4DEA-AF6C-DDDA949DF15D","DD569DED-790E-4C0B-A31A-DFC786C80F02","1D31335C-0F8F-4B7D-9252-E3062103C8C0","7D76EC81-68D3-4BB5-B668-E4DA52E6922A","93E09BEC-AFAF-4F41-A74A-EF853172A75A","764D5B4E-77BE-4CB7-81A0-F51724816061","B1516D26-A644-4DCC-BA30-FF2339D957C5"]

On a GraphQL project without vesper but using typeORM I run the same query using the following syntax.

      const records = await connection.manager
        .find(Solution, {
          relations: [
            "solutioncomponent_refs"
          ]
        })
        .catch(error => {
          connection.close();
          throw error as Error;
        });
      await connection.close();

This generates the following single query which runs in approx 1.5 seconds so why does vesper create 3 queries when typeORM generates 1 on it's native entityManager ?

query: SELECT "Solution"."configurationpageid" AS "Solution_configurationpageid", "Solution"."createdby" AS "Solution_createdby", "Solution"."createdon" AS "Solution_createdon", "Solution"."createdonbehalfby" AS "Solution_createdonbehalfby", "Solution"."description" AS "Solution_description", "Solution"."friendlyname" AS "Solution_friendlyname", "Solution"."installedon" AS "Solution_installedon", "Solution"."isinternal" AS "Solution_isinternal", "Solution"."ismanaged" AS "Solution_ismanaged", "Solution"."isvisible" AS "Solution_isvisible", "Solution"."modifiedby" AS "Solution_modifiedby", "Solution"."modifiedon" AS "Solution_modifiedon", "Solution"."modifiedonbehalfby" AS "Solution_modifiedonbehalfby", "Solution"."organizationid" AS "Solution_organizationid", "Solution"."parentsolutionid" AS "Solution_parentsolutionid", "Solution"."pinpointassetid" AS "Solution_pinpointassetid", "Solution"."pinpointsolutiondefaultlocale" AS "Solution_pinpointsolutiondefaultlocale", "Solution"."pinpointsolutionid" AS "Solution_pinpointsolutionid", "Solution"."publisherid" AS "Solution_publisherid", "Solution"."solutionid" AS "Solution_solutionid", "Solution"."solutionpackageversion" AS "Solution_solutionpackageversion", "Solution"."solutiontype" AS "Solution_solutiontype", "Solution"."uniquename" AS "Solution_uniquename", "Solution"."version" AS "Solution_version", "Solution"."versionnumber" AS "Solution_versionnumber", "Solution_solutioncomponent_refs"."componenttype" AS "Solution_solutioncomponent_refs_componenttype", "Solution_solutioncomponent_refs"."createdby" AS "Solution_solutioncomponent_refs_createdby", "Solution_solutioncomponent_refs"."createdon" AS "Solution_solutioncomponent_refs_createdon", "Solution_solutioncomponent_refs"."createdonbehalfby" AS "Solution_solutioncomponent_refs_createdonbehalfby", "Solution_solutioncomponent_refs"."ismetadata" AS "Solution_solutioncomponent_refs_ismetadata", "Solution_solutioncomponent_refs"."modifiedby" AS "Solution_solutioncomponent_refs_modifiedby", "Solution_solutioncomponent_refs"."modifiedon" AS "Solution_solutioncomponent_refs_modifiedon", "Solution_solutioncomponent_refs"."modifiedonbehalfby" AS "Solution_solutioncomponent_refs_modifiedonbehalfby", "Solution_solutioncomponent_refs"."objectid" AS "Solution_solutioncomponent_refs_objectid", "Solution_solutioncomponent_refs"."rootcomponentbehavior" AS "Solution_solutioncomponent_refs_rootcomponentbehavior", "Solution_solutioncomponent_refs"."rootsolutioncomponentid" AS "Solution_solutioncomponent_refs_rootsolutioncomponentid", "Solution_solutioncomponent_refs"."solutioncomponentid" AS "Solution_solutioncomponent_refs_solutioncomponentid", "Solution_solutioncomponent_refs"."solutionid" AS "Solution_solutioncomponent_refs_solutionid", "Solution_solutioncomponent_refs"."versionnumber" AS "Solution_solutioncomponent_refs_versionnumber" FROM "solutionbase" "Solution" LEFT JOIN "solutioncomponentbase" "Solution_solutioncomponent_refs" ON "Solution_solutioncomponent_refs"."solutionid"="Solution"."solutionid

TypeError: Cannot read property 'isTransactionActive' of undefined

Hello
At first, thanks for your great framework!

With version 0.1.5 was noticed this issue:
TypeError: Cannot read property 'isTransactionActive' of undefined
at apollo_server_core_1.runHttpQuery.then.then.catch (...\node_modules\vesper\index.js:111:54)
at
at process._tickCallback (internal/process/next_tick.js:188:7)

Driver: 'sqljs'

I suppose, 'transactionEntityManager.queryRunner !== undefined &&' is missed.

And, could you update package typeorm in 'dependencies'?

Question: Ongoing development

Hi, I like the idea behind this project and was going to give it a PoC for something I'm working on at work, but I noticed there hasn't been a commit to master in a little over a month. Are you planning on actively developing this moving forward?

Authentication in subscription

In https://www.apollographql.com/docs/graphql-subscriptions/authentication.html, apollo suggests to use onConnect param to pass context specific data e.g. authenticating user over websocket. However, currently Vesper doesn't have a way to do that as the SubscriptionServer created by Vesper doesn't take any params to customise onConnect param.

@pleerock what do you think is the best way to handle this in Vesper not only for authentication but also other possible cases when we need to pass something into context in subscription?

EntityManager doesn't work with existing TypeORM project

We are working with an existing TypeORM project and have been working on getting things moved over to Vesper recently. The samples work just fine but when we use our existing Database and TypeORM models the injected EntityManger in Controllers does not contain the needed connection data.

It does connect properly to the database, if we change the password it fails to start due to MySQL issues.

Here is a screenshot inside the startup of Vesper where the connection looks fine:
screen shot 2018-11-02 at 1 36 08 pm

After startup in the controller:
screen shot 2018-11-02 at 12 38 49 pm

Error in playground:
screen shot 2018-11-02 at 1 43 47 pm

Custom errors format

Hello guys,

I've following custom error class

class TypedError extends Error {
    constructor(message: string, public code: number) {
        super(message)

        // Set the prototype explicitly.
        Object.setPrototypeOf(this, TypedError.prototype)
    }
}

which I use in controller like

@Mutation()
raiseAnError() {
    throw new TypedError('Not found', 404)
}

however, using the playground I only get following with no Error.code

"errors": [
    {
      "message": "Not found",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "raiseAnError"
      ]
    }
  ]

How can I achieve something like (https://codeburst.io/custom-errors-and-error-reporting-in-graphql-bbd398272aeb)

formatError(err) {
    errors.report(err, req);   // <-- log the error
    return {
      message: err.message,
      code: err.originalError && err.originalError.code,   // <--
      locations: err.locations,
      path: err.path
    };
  }```

Do I have to install typeorm separately?

When I install vesper with yarn, importing from typeorm is ok.

But installing with npm, importing from typeorm produces error saying failed to find module.

Installing typeorm separately seems dangerous about versioning. Is there something I'm missing?

Vesper Build failure

vesper fails to build either with tsc or gulp

:\Users\aaron\GitHub\AaronNGray\vesper>gulp
[03:28:31] Requiring external module ts-node/register
ReferenceError: primordials is not defined
    at fs.js:47:5
    at req_ (C:\Users\aaron\GitHub\AaronNGray\vesper\node_modules\natives\index.js:137:5)
    at Object.req [as require] (C:\Users\aaron\GitHub\AaronNGray\vesper\node_modules\natives\index.js:54:10)
    at Object.<anonymous> (C:\Users\aaron\GitHub\AaronNGray\vesper\node_modules\graceful-fs\fs.js:1:37)
    at Module._compile (node:internal/modules/cjs/loader:1095:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1124:10)
    at Module.load (node:internal/modules/cjs/loader:975:32)
    at Function.Module._load (node:internal/modules/cjs/loader:816:12)
    at Module.require (node:internal/modules/cjs/loader:999:19)
    at require (node:internal/modules/cjs/helpers:93:18)
C:\Users\aaron\GitHub\AaronNGray\vesper>tsc
../../../AppData/Roaming/npm/node_modules/typescript/lib/lib.es2015.iterable.d.ts:41:6 - error TS2300: Duplicate identifier 'IteratorResult'.

41 type IteratorResult<T, TReturn = any> = IteratorYieldResult<T> | IteratorReturnResult<TReturn>;
        ~~~~~~~~~~~~~~

  node_modules/@types/node/index.d.ts:78:11
    78 interface IteratorResult<T> { }
                 ~~~~~~~~~~~~~~
    'IteratorResult' was also declared here.

node_modules/@types/node/index.d.ts:78:11 - error TS2300: Duplicate identifier 'IteratorResult'.

78 interface IteratorResult<T> { }
             ~~~~~~~~~~~~~~

  ../../../AppData/Roaming/npm/node_modules/typescript/lib/lib.es2015.iterable.d.ts:41:6
    41 type IteratorResult<T, TReturn = any> = IteratorYieldResult<T> | IteratorReturnResult<TReturn>;
            ~~~~~~~~~~~~~~
    'IteratorResult' was also declared here.

src/commands/CommandUtils.ts:15:92 - error TS2794: Expected 1 arguments, but got 0. Did you forget to include 'void' in your type argument to 'Promise'?

15         return new Promise((ok, fail) => mkdirp(directory, (err: any) => err ? fail(err) : ok()));
                                                                                              ~~~~

  ../../../AppData/Roaming/npm/node_modules/typescript/lib/lib.es2015.promise.d.ts:33:34
    33     new <T>(executor: (resolve: (value: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void): Promise<T>;
                                        ~~~~~~~~~~~~~~~~~~~~~~~~~
    An argument for 'value' was not provided.

src/metadata/ActionMetadataBuilder.ts:96:9 - error TS2322: Type 'string | number | symbol' is not assignable to type 'string'.
  Type 'number' is not assignable to type 'string'.

96         metadata.methodName = action.action;
           ~~~~~~~~~~~~~~~~~~~

src/metadata/ActionMetadataBuilder.ts:97:9 - error TS2322: Type 'string | number | symbol' is not assignable to type 'string'.
  Type 'number' is not assignable to type 'string'.

97         metadata.name = action.name || action.action;
           ~~~~~~~~~~~~~
Found 5 errors.

I have tried latest tsc.

Authorization example?

I am quite fond of Vesper but I can't figure out how I should do authorization in this framework because i am quite inexperienced.

Could you extend the advanced example with a simple JWT implementation?

Select only limited query params

I'm trying to write the query

query {
  posts(limit: 0, offset: 1) {
    title
  }
}

But in the sql it is executing like

SELECT "Post"."id" AS "Post_id", "Post"."title" AS "Post_title", "Post"."text" AS "Post_text" FROM "post";

I want to select the title but it also select the other query too.
How can I get the title in the controller.

@Query()
posts() {
    // I want to select only title in this. 
    return this.entityManager.find();
}

Not working with composite primary key

I am trying to use vesper with my existing project, but it does not return entities that has composite primary key.

https://github.com/icepeng/oriental-vesper

Here is my repo, the card-response, card-stats entity has a problem.

This is example query

query {
  expansions {
    code
    cards {
      id
      stats {
        survey {
          id
        }
        data {
          power
          generality
          responseCount
        }
      }
    }
  }
}

And this is response

{
  "data": {
    "expansions": [
      {
        "code": "the-witchwood",
        "cards": [
          {
            "id": "black-cat",
            "stats": []
          },
          {
            "id": "vex-crow",
            "stats": []
          },
          {
            "id": "cinderstorm",
            "stats": []
          },
        ]
      }
    ]
  }
}

And this is logged typeorm generated query

query: SELECT "Expansion"."code" AS "Expansion_code" FROM "expansion" "Expansion"
query: SELECT "cards"."id" AS "cards_id", "cards"."cost" AS "cards_cost", "cards"."class" AS "cards_class", "cards"."rarity" AS "cards_rarity", "cards"."expansionCode" AS "cards_expansionCode" FROM "card" "cards" WHERE "cards"."expansionCode" IN ($1) -- PARAMETERS: ["the-witchwood"]
query: SELECT "Card"."id" AS "Card_id", "Card"."expansionCode" AS "Expansion_code" FROM "card" "Card" WHERE "Card"."expansionCode" IN ($1) -- PARAMETERS: ["the-witchwood"]
query: SELECT "stats"."cardId" AS "stats_cardId", "stats"."data" AS "stats_data", "stats"."surveyId" AS "stats_surveyId" FROM "card_stat" "stats" WHERE "stats"."cardId" IN ($1, $2, $3) -- PARAMETERS: ["black-cat","vex-crow","cinderstorm"]
query: SELECT "CardStat"."cardId" AS "CardStat_cardId", "CardStat"."surveyId" AS "CardStat_survey_id" FROM "card_stat" "CardStat" WHERE "CardStat"."cardId" IN ($1, $2, $3) -- PARAMETERS: ["black-cat","vex-crow","cinderstorm"]

Query batching fails

I'm using apollo-link-batch-http to batch our graphql request but Vesper fails to run batched mutations.

Reproduction commit: https://github.com/josephktcheung/typescript-advanced-example/tree/a773ce11bc45890720b24fe432403dbee6463e7c

Steps:

  1. Reuse the typescript-advanced-example and upgraded vesper to latest
  2. Here's my index.ts:
import { bootstrap } from "vesper";
import { PostController } from "./controller/PostController";
import { Post } from "./entity/Post";
import { CategoryController } from "./controller/CategoryController";
import { Category } from "./entity/Category";
import { PostResolver } from "./resolver/PostResolver";
import { createApolloFetch } from 'apollo-fetch';

const apolloFetch = createApolloFetch({ uri: 'http://localhost:3000/graphql' });
const request = {
    "operationName": "CreateCategory",
    "query": "mutation CreateCategory {\n  categorySave(name: \"a\") {name}}\n"
};

const run = async () => {
    const single = await apolloFetch(request);
    console.log('signle request reesult: ', JSON.stringify(single, null, 2));
    const multiple = await apolloFetch([request, request]);
    console.log('multiple requests result: ', JSON.stringify(multiple, null, 2));
}

bootstrap({
    port: 3000,
    controllers: [
        PostController,
        CategoryController
    ],
    resolvers: [
        PostResolver
    ],
    entities: [
        Post,
        Category
    ],
    schemas: [__dirname + "/schema/**/*.graphql"]
}).then(() => {
    console.log("Your app is up and running on http://localhost:3000 . " +
        "You can use Playground in development mode on http://localhost:3000/playground");
    return run();
}).catch(error => {
    console.error(error.stack ? error.stack : error);
});
  1. Console logs:
Your app is up and running on http://localhost:3000 . You can use Playground in development mode on http://localhost:3000/playground
signle request reesult:  {
  "data": {
    "categorySave": {
      "name": "a"
    }
  }
}
TypeError: this.connection.createQueryRunner is not a function
    at EntityPersistExecutor.<anonymous> (/Users/josephcheung/workspace/zeep/repos/typescript-advanced-example/node_modules/typeorm/persistence/EntityPersistExecutor.js:82:75)
    at step (/Users/josephcheung/workspace/zeep/repos/typescript-advanced-example/node_modules/typeorm/persistence/EntityPersistExecutor.js:32:23)
    at Object.next (/Users/josephcheung/workspace/zeep/repos/typescript-advanced-example/node_modules/typeorm/persistence/EntityPersistExecutor.js:13:53)
    at /Users/josephcheung/workspace/zeep/repos/typescript-advanced-example/node_modules/typeorm/persistence/EntityPersistExecutor.js:7:71
    at new Promise (<anonymous>)
    at __awaiter (/Users/josephcheung/workspace/zeep/repos/typescript-advanced-example/node_modules/typeorm/persistence/EntityPersistExecutor.js:3:12)
    at /Users/josephcheung/workspace/zeep/repos/typescript-advanced-example/node_modules/typeorm/persistence/EntityPersistExecutor.js:76:60
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:188:7)
TypeError: this.connection.createQueryRunner is not a function
    at EntityPersistExecutor.<anonymous> (/Users/josephcheung/workspace/zeep/repos/typescript-advanced-example/node_modules/typeorm/persistence/EntityPersistExecutor.js:82:75)
    at step (/Users/josephcheung/workspace/zeep/repos/typescript-advanced-example/node_modules/typeorm/persistence/EntityPersistExecutor.js:32:23)
    at Object.next (/Users/josephcheung/workspace/zeep/repos/typescript-advanced-example/node_modules/typeorm/persistence/EntityPersistExecutor.js:13:53)
    at /Users/josephcheung/workspace/zeep/repos/typescript-advanced-example/node_modules/typeorm/persistence/EntityPersistExecutor.js:7:71
    at new Promise (<anonymous>)
    at __awaiter (/Users/josephcheung/workspace/zeep/repos/typescript-advanced-example/node_modules/typeorm/persistence/EntityPersistExecutor.js:3:12)
    at /Users/josephcheung/workspace/zeep/repos/typescript-advanced-example/node_modules/typeorm/persistence/EntityPersistExecutor.js:76:60
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:188:7)
TypeError: this.connection.createQueryRunner is not a function
    at EntityPersistExecutor.<anonymous> (/Users/josephcheung/workspace/zeep/repos/typescript-advanced-example/node_modules/typeorm/persistence/EntityPersistExecutor.js:82:75)
    at step (/Users/josephcheung/workspace/zeep/repos/typescript-advanced-example/node_modules/typeorm/persistence/EntityPersistExecutor.js:32:23)
    at Object.next (/Users/josephcheung/workspace/zeep/repos/typescript-advanced-example/node_modules/typeorm/persistence/EntityPersistExecutor.js:13:53)
    at /Users/josephcheung/workspace/zeep/repos/typescript-advanced-example/node_modules/typeorm/persistence/EntityPersistExecutor.js:7:71
    at new Promise (<anonymous>)
    at __awaiter (/Users/josephcheung/workspace/zeep/repos/typescript-advanced-example/node_modules/typeorm/persistence/EntityPersistExecutor.js:3:12)
    at /Users/josephcheung/workspace/zeep/repos/typescript-advanced-example/node_modules/typeorm/persistence/EntityPersistExecutor.js:76:60
    at <anonymous>
TypeError: this.connection.createQueryRunner is not a function
    at EntityPersistExecutor.<anonymous> (/Users/josephcheung/workspace/zeep/repos/typescript-advanced-example/node_modules/typeorm/persistence/EntityPersistExecutor.js:82:75)
    at step (/Users/josephcheung/workspace/zeep/repos/typescript-advanced-example/node_modules/typeorm/persistence/EntityPersistExecutor.js:32:23)
    at Object.next (/Users/josephcheung/workspace/zeep/repos/typescript-advanced-example/node_modules/typeorm/persistence/EntityPersistExecutor.js:13:53)
    at /Users/josephcheung/workspace/zeep/repos/typescript-advanced-example/node_modules/typeorm/persistence/EntityPersistExecutor.js:7:71
    at new Promise (<anonymous>)
    at __awaiter (/Users/josephcheung/workspace/zeep/repos/typescript-advanced-example/node_modules/typeorm/persistence/EntityPersistExecutor.js:3:12)
    at /Users/josephcheung/workspace/zeep/repos/typescript-advanced-example/node_modules/typeorm/persistence/EntityPersistExecutor.js:76:60
    at <anonymous>
multiple requests result:  [
  {
    "data": {
      "categorySave": null
    },
    "errors": [
      {
        "message": "this.connection.createQueryRunner is not a function",
        "locations": [
          {
            "line": 2,
            "column": 3
          }
        ],
        "path": [
          "categorySave"
        ]
      }
    ]
  },
  {
    "data": {
      "categorySave": null
    },
    "errors": [
      {
        "message": "this.connection.createQueryRunner is not a function",
        "locations": [
          {
            "line": 2,
            "column": 3
          }
        ],
        "path": [
          "categorySave"
        ]
      }
    ]
  }
]

Cannot read property 'isTransactionActive' of undefined

Hi, I'm trying to get a new project up and running by doing the following (on version 0.1.7):

npm i vesper -g
vesper init --name my-project --typescript
cd my-project
yarn && yarn start
open http://localhost:3000/playground

I try the following query:

query {
  users {
    id
    firstName
  }
}

...and I get:

TypeError: Cannot read property 'isTransactionActive' of undefined
    at /Users/dancaddigan/Code/tmp/my-project/src/index.ts:140:53

Any thoughts? I get the same thing if I change the DB engine to Postgres and create a new pg DB.

Ability to customize topic/trigger sbscribed to at subscription initialization time?

Currently, subscriptions topics seem to be determined by the GraphQL API. Controllers filter messages published to the GraphQL API-determined topic. This does not scale well in certain situations.

Consider a chatroom server, where users may create rooms and subscribe to those rooms, passing the room id as a parameter. Then by the example, the publisher must publish to ALL chatrooms, and the subscription controller filters by chatroom. This is not ideal in, say, redis implementations, since publishing is O(n) in the number of topic subscribers. The ideal way is to be able to determine which topic to subscribe to as controller. (As well as implement a filtering function)

[Feature Request] GraphQL interfaces

First of all, thanks for this great framework!

I was wondering if I could use GraphQL interfaces with Vesper. I did some source digging, but I couldn't find anything about it.
Also, when I run my server with an interface, a warning gets printed to the console:

Type "..." is missing a "resolveType" resolver. Pass false into "resolverValidationOptions.requireResolversForResolveType" to disable this warning.

Your website is broken (blank screen)

Didn't know how to reach out. So I'm creating a new issue. Your website is broken. Looking at the console you have dependencies on vue and vue router that broke. Says it's related to unpkg.

Add custom playground url

Could you create some configuration so that you can set the default playground URL? I like to use my projects as a default url '/ @' as a playground because besides making it difficult to track common urls, it also makes the query url smaller ...

Custom Scalar

How can I add some custom Scalar like Date, Time, JSON and Enum?

[Feature Request] Multi-tenancy support

I was wondering if multi-tenancy support is relevant as a new feature ? I am interested about it, precisely using a shared database design with multiple schema (PostgreSQL). The support should be implemented in queries but also for runtime migration (creation of new schema).

Let me know you are also interested, maybe i could contribute.

Thank you.

Custom connection options

Hi,

This is a nice framework, thank you for creating this!

I have 2 questions on customising the TypeORM connection options:

  1. Can we have multiple database connections in 1 project? Currently it only connects to 1 database.
  2. Would we be able to connect TypeORM by ourselves instead of being handled by the framework?

Best,
Joseph

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.