Giter Site home page Giter Site logo

graphql-nexus / nexus Goto Github PK

View Code? Open in Web Editor NEW
3.4K 33.0 277.0 18.64 MB

Code-First, Type-Safe, GraphQL Schema Construction

Home Page: https://nexusjs.org

License: MIT License

JavaScript 0.19% TypeScript 99.80% HTML 0.01%
graphql typescript nexus graphql-nexus graphql-schema

nexus's People

Contributors

ahmedosama5200 avatar chenkie avatar davekiss avatar dependabot[bot] avatar estrada9166 avatar ggriffin avatar hiphapis avatar huv1k avatar izumin5210 avatar janpio avatar jasonkuhrt avatar johnnyoshika avatar kandros avatar leonardodino avatar nayni avatar nikolasburk avatar nilubava avatar niyabits avatar redrebel avatar riccardoscalco avatar ruheni avatar santialbo avatar schickling avatar shreyas44 avatar sytten avatar tgriesser avatar tom-sherman avatar valentinpalkovic avatar vinassefranche avatar weakky 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

nexus's Issues

NexusGenFieldTypes uses NexusGenRootTypes for properties

As far as I understand we need NexusGenRootTypes for resolvers and NexusGenFieldTypes for actual results.
This how my generated file looks like:

export interface NexusGenRootTypes {
  BasketModel: {
    id: number; // Int!
    userId: number; // Int!
  };
  BasketProductModel: {
    id: number; // Int!
    price?: number | null; // Float
    productId: number; // Int!
    quantity: number; // Int!
  };
  UserModel: {
    email?: string | null; // String
    id: number; // Int!
    phone?: string | null; // String
  };
}

export interface NexusGenFieldTypes {
  BasketModel: {
    id: number; // Int!
    products: NexusGenRootTypes["BasketProductModel"][]; // [BasketProductModel!]!
    `: NexusGenRootTypes["UserModel"]; // UserModel!
    userId: number; // Int!
  };
  BasketProductModel: {
    id: number; // Int!
    price?: number | null; // Float
    productId: number; // Int!
    quantity: number; // Int!
  };
  OrderModel: {
    basket: NexusGenRootTypes["BasketModel"]; // BasketModel!
    basketId: number; // Int!
    code: string; // String!
    createdAt: any; // Date!
    customerComment: string | null; // String
    id: number; // Int!
    managerComment: string | null; // String
  };
  UserModel: {
    email?: string | null; // String
    id: number; // Int!
    phone?: string | null; // String
  };
}

NexusGenFieldTypes["OrderModel"] uses NexusGenRootTypes["BasketModel"] type for basket property and typescript does not let to access order.basket.user or order.basket.products because of that.

Is it intended to be like that? And we have to create our own types for loaded fragments?

Discussion about developer experience

Nexus uses a particular approach to deal with typechecking, that involve generation of typings types files from "runtime defined objects" that extend a global "object type" that nexus itself uses to typecheck other types.

This is meant as a discussion to find problems related to developer experience and a possible solution to address them.

Use an inputObjectType as type for another inputObjectType

If I try to use a inputTypeObjectType as a field for another inputObjectType like this:

export const ObjWithName = inputObjectType({
  name: 'ObjWithName',
  definition(t) {
    t.string('name');
  },
});

export const RootType = inputObjectType({
  name: 'RootType',
  definition(t) {
    t.field('obj', {type: ObjWithName});
  },
});

I get this error:

Error:(39, 21) TS2322: Type 'NexusInputObjectTypeDef<"ObjWithName">' is not assignable to type "Boolean" | "Float" | "ID" | "Int" | "JSON" | "String"'.
  Type 'NexusInputObjectTypeDef<"ObjWithName">' is not assignable to type '"String"'.

But I don't het this error if I try the same thing with objectType:

export const ObjWithName = objectType({
  name: 'ObjWithName',
  definition(t) {
    t.string('name');
  },
});

export const RootType = objectType({
  name: 'RootType',
  definition(t) {
    t.field('obj', {type: ObjWithName});
  },
});

I tried playing around with the type here to look more like this. But I couldn't make it work to make a PR x.x

Thanks!

Argument of type 'NexusObjectTypeDef<"Node">' is not assignable to parameter of type 'NexusInterfaceTypeDef<string>'.

Following the getting started example from the docs almost exactly:

image

I am unable to reference the Node type in my account type.

image

Gives me the tslint error of

Argument of type 'NexusObjectTypeDef<"Node">' is not assignable to parameter of type 'NexusInterfaceTypeDef'.
Property 'config' is protected but type 'NexusObjectTypeDef' is not a class derived from 'NexusInterfaceTypeDef'.ts(2345)

And a console error of

Using ts-node version 8.0.2, typescript version 3.3.3
Error: Expected [object Object] to be an interfaceType, saw GraphQLObjectType
at SchemaBuilder.getInterface (/Users/jakelowen/code/tmp_code/nexus_test/node_modules/nexus/dist/builder.js:445:19)
at /Users/jakelowen/code/tmp_code/nexus_test/node_modules/nexus/dist/builder.js:169:89
at Array.map ()
at interfaces (/Users/jakelowen/code/tmp_code/nexus_test/node_modules/nexus/dist/builder.js:169:57)
at resolveThunk (/Users/jakelowen/code/tmp_code/nexus_test/node_modules/graphql/type/definition.js:374:40)
at defineInterfaces (/Users/jakelowen/code/tmp_code/nexus_test/node_modules/graphql/type/definition.js:522:20)
at GraphQLObjectType.getInterfaces (/Users/jakelowen/code/tmp_code/nexus_test/node_modules/graphql/type/definition.js:503:31)
at typeMapReducer (/Users/jakelowen/code/tmp_code/nexus_test/node_modules/graphql/type/schema.js:259:23)
at Array.reduce ()
at new GraphQLSchema (/Users/jakelowen/code/tmp_code/nexus_test/node_modules/graphql/type/schema.js:108:28)
[ERROR] 21:47:25 Error: Expected [object Object] to be an interfaceType, saw GraphQLObjectType

I can get everything to work just fine if I do not attempt to implement the Node type and instead just specify the 'id' field manually.

Any idea why I am not able to implement Node as illustrated in the docs?

Here are my package versions:

"graphql": "^14.1.1",
    "graphql-yoga": "^1.17.4",
    "knex": "^0.16.3",
    "lodash": "^4.17.11",
    "nexus": "^0.9.9",
    "pg": "^7.8.0"

Thank you. Seems like a very promising project!

Usage of generated typescript types / general typescript questions

I'm using nexus in a typescript-based graphql server and have a few questions in regard to typing this correctly, sorted in order of (subjective) importance:

import { arg, queryType } from 'nexus'
import { Context } from '../Context'

export const Query = queryType({
  definition: t => {
    t.field('getProject', {
      nullable: true,
      type: 'Project',
      args: {
        id: arg({
          type: 'String',
          description: 'Project id',
          nullable: false,
        }),
      },
      async resolve(root: any, args, ctx: Context, info: any): Promise<any> {
        return await ctx.getProject(args.id)
      },
    })
  },
})
  1. how can i type the return value of my resolver, e.g. Promise<Project> instead of Promise<any>? Typescript types are auto-generated, but i'm confused about the way to use them (i'd want to use them on both server (resolver return value) and client). Also, based on the docs, i don't understand the typegenConfig and typegenAutoConfig parameters of makeSchema at all - the ghost example doesn't help either. I assume these configuration options have something to do with the usage of the generated typescript types.
  2. why does the info parameter of the resolver have no type information?
  3. is there a way to get type information on the root parameter?

Errors from SDL Converted Schema in Nexus

I have a repository I have been using to learn nexus at https://github.com/jblevins1991/Nexus-Example/tree/converter. On the converter branch I took my schema from the generated folder that is created by the prisma generate command and used the SDL Converter on it. Here is the link for that converted schema https://github.com/jblevins1991/Nexus-Example/blob/converter/src/schema/schema.js.

In my index file in the schema folder I import my schema and use it in the makeSchema function. When I run the application I get the following error from nexus's index.js file.

TypeError: schema.getTypeMap is not a function
    at forEachField (/home/developer/Documents/nexus-example/src/node_modules/graphql-extensions/lib/index.js:157:26)
    at Object.enableGraphQLExtensions (/home/developer/Documents/nexus-example/src/node_modules/graphql-extensions/lib/index.js:106:5)
    at doRunQuery (/home/developer/Documents/nexus-example/src/node_modules/apollo-server-core/dist/runQuery.js:67:30)
    at /home/developer/Documents/nexus-example/src/node_modules/apollo-server-core/dist/runQuery.js:21:56
    at process._tickCallback (internal/process/next_tick.js:68:7)
Error: Expected { _extensionsEnabled: true } to be a GraphQL schema.
    at invariant (/home/developer/Documents/nexus-example/src/node_modules/graphql/jsutils/invariant.js:19:11)
    at assertSchema (/home/developer/Documents/nexus-example/src/node_modules/graphql/type/schema.js:38:46)
    at validateSchema (/home/developer/Documents/nexus-example/src/node_modules/graphql/type/validate.js:51:28)
    at assertValidSchema (/home/developer/Documents/nexus-example/src/node_modules/graphql/type/validate.js:75:16)
    at Object.validate (/home/developer/Documents/nexus-example/src/node_modules/graphql/validation/validate.js:55:35)
    at doRunQuery (/home/developer/Documents/nexus-example/src/node_modules/apollo-server-core/dist/runQuery.js:111:42)
    at /home/developer/Documents/nexus-example/src/node_modules/apollo-server-core/dist/runQuery.js:21:56
    at process._tickCallback (internal/process/next_tick.js:68:7)

Add Used Imports on SDL Converter

Maybe there can be a flag or checkbox --enable-used-imports like on https://svgr.now.sh/ to add used imports to SDL Converter so I can convert my existing markup.

An example -

type Mutation {
  register(userInfo: UserInfo): String!
  login(userInfo: UserInfo): Boolean!
}

type Query {
  hello(name: String = "World"): String!
  user: User
}

type Subscription {
  newUser: User!
}

type User {
  id: ID!
  username: String!
}

input UserInfo {
  email: String!
  password: String!
}

might output in

const { objectType, inputObjectType, makeSchema } = require("nexus");

const Mutation = objectType({
  name: "Mutation",
  definition(t) {
    t.string("register")
    t.boolean("login")
  }
})
const Query = objectType({
  name: "Query",
  definition(t) {
    t.string("hello")
    t.field("user", {nullable:true,type:'User'})
  }
})
const Subscription = objectType({
  name: "Subscription",
  definition(t) {
    t.field("newUser", {type:'User'})
  }
})
const User = objectType({
  name: "User",
  definition(t) {
    t.id("id")
    t.string("username")
  }
})

const UserInfo = inputObjectType({
  name: "UserInfo",
  definition(t) {
    t.string("email", {required:true})
    t.string("password", {required:true})
  }
});

Todo List

Just adding a list of things I come across as I work on this with a real-world api

Todo:

  • Multi-dimensional arrays (and nullability?)
  • Option to strip unused (inaccessible) types from schema gen / type gen
  • Check for undefined on args, for accidentally mixing .field w/ .fieldArg edit: removed fieldArg
  • required: true on input arg members
  • Example of wrapping type for connections (gqliteral-contrib)
  • Configuring nullability on type level
  • Option to use existing typegen for arg types?
  • Sensible defaults for return types without root
  • Check if lexicographicSortSchema is a function before calling (older graphql)
  • Check for empty types (no fields, members, etc.) before typegen
  • Toggle for pre-TS3.0 features?
  • Type completion/validation for backingTypes
  • Re-export types from gqliteral
  • Validate name
  • Explicit toggle for whether to run fs things
  • Tests for all of the features

Ideas / Maybe Todo:

  • Validation on field level / type level? Or at least ability to wrap for plugins
  • Plugin API, e.g. adding custom fields for type building: t.date / t.dateArg (encouraging wrapping)

Other things:

  • Subscriptions

Support middleware

Description

As nexus recently implemented some kind of authorization system (which is a kind of middleware), I think we should also allow for more generic middlewares.

Proposal

Middlewares should be definable at every level of the API:

Globally

import { makeSchema } from 'nexus'
import * as allTypes from './graphql'
import { logging, auth } from './middlewares'

makeSchema({
  types: allTypes,
  middlewares: [logging, auth]
})

On an object type (not sure whether it makes sense to have it elsewhere)

import { objectType } from 'nexus'
import { someMiddleware } from './middlewares'

export const Query = objectType({
  name: 'Query',
  middlewares: [someMiddleware],
  definition(t) { ... }
})

On a field

import { objectType } from 'nexus'
import { fieldMiddleware } from './middlewares'

export const Mutation = objectType({
  name: 'Mutation',
  definition(t) {
    t.field('login', {
      middlewares: [fieldMiddleware],
      resolve(root, args, ctx) { ... }
    })
  }
})

Concerns

The current proposal might makes it hard to build abstractions upon it.

A use-case might be to provide an ACL style authorization system, in which you probably want to handle everything in one file.

With the current proposal, it doesn't seem trivial (or impossible) to inject middlewares with enough granularity (eg: on a field), because fields are evaluated later during the schema construction.

graphql-middleware use the following data-structure to define middlewares at every level of the API:

const beepMiddleware = {
  Query: {
    hello: async (resolve, parent, args, context, info) => {
      // Implement middleware on `Query.hello` field
    },
  },
}

It might be necessary to have something alike on the global middlewares option (and eventually on types)

import { makeSchema } from 'nexus'
import * as allTypes from './graphql'
import { middleware1, middleware2 } from './middlewares'

makeSchema({
  types: allTypes,
  middlewares: {
    Query: {
      hello: [middleware1, middleware2],
      world: [middleware1]
    }
  }
})

Open questions

  • Should we keep authorize as a separate use-case ?
  • Or should we make authorize a simple use-case of middlewares and provide docs?

Thanks 🙌

Type definitions from runtime objects

Had some new ideas for being able to specify the root type definitions with runtime objects, for those using the library for JS without robust TS objects to map to, or for simple cases.

typesFromRuntime({
  Node: { id: String },
  User: { id: String, name: String, birthday: Date },
  Date: Date
})

Relay style connections example

Very interesting library, thanks to everyone involved!

I'd be interested in a relay-style connection example! How'd you do these?

Resolve does not allow return Promise

Resolve does not allow Promise.
At this example the function context.getDocumentItem is a Promise.

t.field('document', {
  nullable: true,
  args: { id: idArg({ required: true }) },
  type: 'Document',
  resolve: (root, { id }, context) => {
    return context.getDocumentItem({ id });
  },
});

Typescript failed with this message:

Error:(10, 3) TS2322: Type '(root: {}, { id }: { id: string; }, context: Context) => Promise<Document>' is not assignable to type 'FieldResolver<"Query", "document">'.

typeGenAutoConfig wrong relative path in Windows

These lines generate the imports of the typegenAutoConfig. https://github.com/prisma/nexus/blob/18a306ee2903d98fc110c1067e1903ea0ee5f256/src/typegenAutoConfig.ts#L212-L215 And use this function https://github.com/prisma/nexus/blob/18a306ee2903d98fc110c1067e1903ea0ee5f256/src/typegenAutoConfig.ts#L340-L350

The problem is with this line:

    return `.${path.sep}${path.join(relative, filename)}`;

In Windows it generates an import statement with backslashes \, and when node tries to compile it, it can't.

Maybe change it to:

    return `./${path.join(relative, filename)}`;

Problems making updates to the schema

I have a simple schema like this

const Query = queryType({
  definition(t) {
    t.field("user", {
      type: User,
      nullable: false,
      resolve() {
        return null;
      }
    });
  }
});

And this give the following error because of the nullable

$ ts-node-dev src/index.ts
Using ts-node version 8.0.2, typescript version 3.3.3
[ERROR] 12:02:47 ⨯ Unable to compile TypeScript:
src/index.ts(26,7): error TS2322: Type '() => null' is not assignable to type 'FieldResolver<"Query", "user">'.
  Type 'null' is not assignable to type 'MaybePromise<{ id: MaybePromise<string>; name: MaybePromise<string>; }>'.

The problem is that when I update to nullable: true I still get the same error.
It looks like it is getting the old generated types because it cannot run the server to generate the new types.

makePrismaSchema does not generate code due to an exception from mkdir

I'm trying to run the prisma-examples/typescript/graphql/ example.

I think the problem is in the following code:
https://github.com/prisma/nexus/blob/7dc2ac7aedc7992fb709ed580a13435993aeffe7/src/typegenMetadata.ts#L93

which causes:

{ Error: EEXIST: file already exists, mkdir '/home/test/prisma-examples/typescript/graphql/src/generated'
  errno: -17,
  code: 'EEXIST',
  syscall: 'mkdir',
  path: '/home/test/prisma-examples/typescript/graphql/src/generated' }

allow an objectType as second parameter to field()

we were just on a call with @schickling discussing nexus vs decorator based libs and someone mentioned that it might be hard to navigate between the files using the current tooling.

I think this is easily solvable by allowing this:

const CommentType = objectType("Comment", (t) => {
  commonFields(t);
  t.description("A comment about an entry, submitted by a user");
  t.directive("cacheControl", { maxAge: 240 });
  t.float("createdAt", {
    property: "created_at",
    description: "A timestamp of when the comment was posted",
  });
  t.string("content", {
    description: "The text of the comment",
  });
  t.string("repoName", {
    description: "The repository which this comment is about",
  });
});

exports.Mutation = objectType("Mutation", (t) => {
  t.field("submitComment", CommentType, { // use the type directly here instead of just it's name
    args: {
      repoFullName: RepoNameArg,
      commentContent: stringArg({
        required: true,
        description: "The text content for the new comment",
      }),
    },
  });
});

sorry for such a long example showing a simple function overload.
It would be beneficial to keep the string as an option, because it might be needed when running into circular dependencies.

What do you think @tgriesser? Would this be reasonable? It would certainly make an IDE navigation a breeze in VSCode.

local development and testing for contributing

looking at the scripts of the root package.json looks like running yarn dev is enough to make everything work locally but unfortunately

"scripts": {
    "dev": "yarn link-examples && tsc -w",
    "test": "jest",
    "build": "tsc && prettier --write 'dist/**/*.ts'",
    "lint": "tslint -p tsconfig.json",
    "clean": "rm -rf dist",
    "format": "prettier --write 'src/**/*.ts'",
    "prepublish": "yarn clean && yarn lint && yarn build",
    "postpublish": "yarn upgrade-deps || echo 'Oops...'",
    "website": "cd website && yarn && yarn start",
    "examples": "yarn link-examples && cd website && yarn gulp run-examples",
    "deploy-site": "cd website && yarn && yarn gulp link-website && yarn build",
    "link-examples": "cd website && yarn && yarn gulp link-examples",
    "unlink-examples": "cd website && yarn && yarn gulp unlink-examples",
    "upgrade-deps": "cd website && yarn && yarn gulp upgrade-deps",
    "ts-ast-reader": "cd examples/ts-ast-reader && yarn start"
  }

➜  nexus git:(develop) yarn dev         
yarn run v1.13.0
$ yarn link-examples && tsc -w
$ cd website && yarn && yarn gulp link-examples
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 🔨  Building fresh packages...
$ /Users/jaga/coding/nexus/website/node_modules/.bin/gulp link-examples
[16:26:52] Requiring external module ts-node/register
[16:26:53] Using gulpfile ~/coding/nexus/website/gulpfile.ts
[16:26:53] Starting 'link-examples'...

Linking ts-ast-reader
Linking star-wars
Linking kitchen-sink
Linking githunt-api
Linking apollo-fullstack
Linking ghost
Linking website
Finished linking website
[16:27:06] 'link-examples' errored after 13 s
[16:27:06] Error: Command failed: yarn
warning package.json: No license field
warning @graphql-nexus/ghost-example-api: No license field
error [email protected]: The engine "node" is incompatible with this module. Expected version "^6.9.0 || ^8.9.0 || ^10.13.0". Got "11.6.0"
warning [email protected]: The engine "cli" appears to be invalid.
error Found incompatible module

    at ChildProcess.exithandler (child_process.js:294:12)
    at ChildProcess.emit (events.js:188:13)
    at maybeClose (internal/child_process.js:978:16)
    at Process.ChildProcess._handle.onexit (internal/child_process.js:265:5)

running yarn v1.13.0 on macOS

Error: Command failed: yarn looks strange

TypeScript error for async resolvers that can return null

I am getting a TypeScript error when using async resolvers that can also return null. In the following example I could just remove the async but I would like to use await inside the resolver. Is there anything I am missing?

const Query = objectType({
  name: 'Query',
  definition(t) {
    t.int('getNumberOrNull', {
      nullable: true,
      args: { a: intArg({ required: true }) },
      async resolve(_, { a }) {
        if (a > 0) {
          return a;
        }
        return null;
      },
    });
  },
});

The error is:

Type '(_: {}, { a }: { a: number; }) => Promise<number | null>' is not assignable to type 'FieldResolver<"Query", "getNumberOrNull">'.
  Type 'Promise<number | null>' is not assignable to type 'number | PromiseLike<null> | PromiseLike<number> | null'.
    Type 'Promise<number | null>' is not assignable to type 'PromiseLike<number>'.
      Types of property 'then' are incompatible.
        Type '<TResult1 = number | null, TResult2 = never>(onfulfilled?: ((value: number | null) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined) => Promise<...>' is not assignable to type '<TResult1 = number, TResult2 = never>(onfulfilled?: ((value: number) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined) => PromiseLike<...>'.
          Types of parameters 'onfulfilled' and 'onfulfilled' are incompatible.
            Types of parameters 'value' and 'value' are incompatible.
              Type 'number | null' is not assignable to type 'number'.
                Type 'null' is not assignable to type 'number'.

However, returning promises by themselves inside the resolver without an async does work:

resolve(_, { a }) {
  if (a > 0) {
    return Promise.resolve(a);
  }
  return Promise.resolve(null);
}

The async keyword changes the return type from Promise<null> | Promise<number> to Promise<null | number> which seems to be the problem?

Is this related to #28? The author also used a nullable async function and was wondering why he has to set the return type of the resolver to Promise<any> (which does the trick by ignoring the problem).

Split queries and mutations to multiple files

Hey,
I really like nexus, but I would like to see a better solution how to split queries and mutations to multiple files. Because with more complex applications its hard to follow one big file with everything inside. So what is the best solution for it right now? Are there any plans on how to address this kind of issue?

Use Prisma generated input type as arg

I am trying to add my own custom mutation on top of the mutations generated by Prisma. As one of the args, I would like to use a prisma generated input type. Currently it seems that unless I create my own input type, I can only use scalars as args for my mutation? Is that correct? Still trying to get a hang of how nexus and prisma interact.

Currently, in our custom resolver definitions, we have:
createNavigationItems(data: [NavigationItemCreateInput!]!): [NavigationItem]!

How can I do the same thing with nexus?

const Mutation = prismaObjectType({ 
  name: 'Mutation',
  definition(t) { 
    t.prismaFields(['*'])

    t.list.field('createManyNavigationItems', {
      type: 'NavigationItem',
      args: {
        data: NavigationItemCreateInput[] // THIS IS WHERE I WANT TO USE THE PRISMA GENERATED INPUT TYPE BUT IT THROWS AN ERROR
      },
      resolve(root, args, ctx, info) {
        let createdNavigationItems: NavigationItem[] = [];

        for (let navItemInput of args.data) {
          let createdNavigationItem: NavigationItem = ctx.prisma.createNavigationItem(navItemInput);
          createdNavigationItems.push(createdNavigationItem);
        }

        return createdNavigationItems;
      }
    })
  }
});

Generated Typescript definitions for Unions are null or undefined

const AuthSuccess = objectType({
  name: "AuthSuccess",
  definition(t) {
    t.string("token");
    t.field("user", { type: CurrentUser });
  }
});
const NoUser = objectType({
  name: "NoUser",
  definition(t) {
    t.string("message");
  }
});
const WrongPassword = objectType({
  name: "WrongPassword",
  definition(t) {
    t.string("message");
  }
});
const AuthResult = unionType({
  name: "AuthResult",
  definition(t) {
    t.members(AuthSuccess, NoUser, WrongPassword);
  }
});
const AuthPayload = objectType({
  name: "AuthPayload",
  definition(t) {
    t.field("result", { type: AuthResult });
  }
});

generates the following typescript definition for AuthPayload in nexus.ts:

  AuthPayload: { // root type
    result: undefined | null; // AuthResult!
  }

This is the same for any other union I define. Am I doing something wrong?

And while I'm here, is there a better way to resolve types than looking at what properties a root field has? In the example above, some of the types that are part of the AuthResult union have exactly the same fields, and what I'm really interested in is their type identity.

SubscribeFieldConfig resolve shouldn't be required

Thanks for implementing subscriptions -- these are the missing piece for an app I'm working on.

However, I'm a little confused by why the resolve property on SubscribeFieldConfig is required. Resolve in subscription resolvers is mostly useless, everything is handled through subscribe. Is there something I'm missing about how this is supposed to be used?

Inline resolving doesn't work

I've found this code snippet in the docs, that allows to pass resolvers inline:

const User = objectType("User", (t) => {
  t.id("id", (o) => o.user_id);
  t.id("name", (o) => o.user_name);
  t.id("description", (o) => o.user_description);
});

However, when I'm trying to achieve this I am getting an error:

~/D/nexus-example $ yarn start
yarn run v1.13.0
warning package.json: No license field
$ ts-node-dev ./src
Using ts-node version 8.0.1, typescript version 3.2.4
[ERROR] 18:48:55 ⨯ Unable to compile TypeScript:
src/index.ts(8,20): error TS2559: Type '(root: any, args: any) => string' has no properties in common with type 'OutputFieldOpts<GraphQLNexusGen, "Query", "test">'.
src/index.ts(8,21): error TS7006: Parameter 'root' implicitly has an 'any' type.
src/index.ts(8,27): error TS7006: Parameter 'args' implicitly has an 'any' type.

image

NexusGenFieldTypes with resolved fields

I am not sure how best to ask this question, so I hope this simplest reproduction makes sense.

This code

// SDL
// type User {
//     name: String!
//     post: Post!
// }
// type Post {
//     body: String!
//     author: User!
// }
// type Query {
//     user: User!
// }
const User = objectType({
    name: 'User',
    definition: t => {
        t.string('name');
        t.field('post', {
            type: Post,
            resolve: (): any => ({ body: 'hello' }),
        });
    },
});

const Post = objectType({
    name: 'Post',
    definition: t => {
        t.string('body');
        t.field('author', {
            type: User,
            resolve: (): any => ({ name: 'Johannes' }),
        });
    },
});

const Query = objectType({
    name: 'Query',
    definition: t => {
        t.field('user', {
            type: User,
            resolve: () => ({ name: 'Johannes' }),
        });
    },
});

produces these ts type definitions

declare global {
  interface NexusGen extends NexusGenTypes {}
}

export interface NexusGenInputs {
}

export interface NexusGenEnums {
}

export interface NexusGenRootTypes {
  Post: { // root type
    body: string; // String!
  }
  Query: {};
  User: { // root type
    name: string; // String!
  }
  String: string;
  Int: number;
  Float: number;
  Boolean: boolean;
  ID: string;
}

export interface NexusGenAllTypes extends NexusGenRootTypes {
}

export interface NexusGenFieldTypes {
  Post: { // field return type
    author: NexusGenRootTypes['User']; // User!
    body: string; // String!
  }
  Query: { // field return type
    user: NexusGenRootTypes['User']; // User!
  }
  User: { // field return type
    name: string; // String!
    post: NexusGenRootTypes['Post']; // Post!
  }
}

export interface NexusGenArgTypes {
}

export interface NexusGenAbstractResolveReturnTypes {
}

export interface NexusGenInheritedFields {}

export type NexusGenObjectNames = "Post" | "Query" | "User";

export type NexusGenInputNames = never;

export type NexusGenEnumNames = never;

export type NexusGenInterfaceNames = never;

export type NexusGenScalarNames = "Boolean" | "Float" | "ID" | "Int" | "String";

export type NexusGenUnionNames = never;

export interface NexusGenTypes {
  context: any;
  inputTypes: NexusGenInputs;
  rootTypes: NexusGenRootTypes;
  argTypes: NexusGenArgTypes;
  fieldTypes: NexusGenFieldTypes;
  allTypes: NexusGenAllTypes;
  inheritedFields: NexusGenInheritedFields;
  objectNames: NexusGenObjectNames;
  inputNames: NexusGenInputNames;
  enumNames: NexusGenEnumNames;
  interfaceNames: NexusGenInterfaceNames;
  scalarNames: NexusGenScalarNames;
  unionNames: NexusGenUnionNames;
  allInputTypes: NexusGenTypes['inputNames'] | NexusGenTypes['enumNames'] | NexusGenTypes['scalarNames'];
  allOutputTypes: NexusGenTypes['objectNames'] | NexusGenTypes['enumNames'] | NexusGenTypes['unionNames'] | NexusGenTypes['interfaceNames'] | NexusGenTypes['scalarNames'];
  allNamedTypes: NexusGenTypes['allInputTypes'] | NexusGenTypes['allOutputTypes']
  abstractTypes: NexusGenTypes['interfaceNames'] | NexusGenTypes['unionNames'];
  abstractResolveReturn: NexusGenAbstractResolveReturnTypes;
}

In my client I am trying to give types to the following query

{
  user {
    name
    post {
        body
    }
  }
}

the closest I can get is NexusGenFieldTypes['Query']['user'] but that returns NexusGenRootTypes['User'] which does not have a body. Is there a reason that NexusGenFieldTypes is not the following? (linking to field types instead of root types)

export interface NexusGenFieldTypes {
  Post: { // field return type
    author: NexusGenFieldTypes['User']; // User!
    body: string; // String!
  }
  Query: { // field return type
    user: NexusGenFieldTypes['User']; // User!
  }
  User: { // field return type
    name: string; // String!
    post: NexusGenFieldTypes['Post']; // Post!
  }
}

Circular Type Dependency

I'm trying to set up a simple Query resolver:

export const Query = prismaObjectType('Query', t => {
    t.field('listUsers', 'User', {
        list: true,
        resolve: (parent, args, ctx) => {
            return ctx.prisma.users()
        }
    })
})

The 'User' type referenced in the second argument is only being typechecked against 'BaseScalars', not against GenTypes. GenTypes = GraphQLNexusGen, which as far as I can gather is populated by the nexus.ts artifact. Is there a way to generate the nexus.ts artifact manually? The only way mentioned in the docs is by starting the server, which I'm unable to do because the code does not pass the typechecker (User is not of type 'BaseScalars').

Improve typing on field return value / `MaybePromiseDeep`

Currently the return value uses MaybePromiseDeep which attempts to recursively allow for Promises at any level of the expected value.

It also doesn't account for typings that evaluate function properties on a resolved type object.

Two things that could improve this typing:

  1. If the field is not a user defined type, maybe that's when we do the MaybePromiseDeep, otherwise we likely don't need to worry about doing this. Cleaning this up would really help with the errors on return type mismatch
  2. Accounting for return values being allowed to be objects w/ function properties and for user defined types to have methods that return the correct type, etc.

Type GraphQL vs Nexus

Difference between Type GraphQL & Nexus might be helpful since I am guessing both solve the same problem❓

`WrappedType` typings should allow `(schema) => WrappedType[]`

Description

Currently, the typings for WrappedType are the following:

export declare class WrappedType {
    readonly type: Types.NamedTypeDef | DirectiveTypeDef | GraphQLScalarType | ((schema: SchemaBuilder) => WrappedType);
    constructor(type: Types.NamedTypeDef | DirectiveTypeDef | GraphQLScalarType | ((schema: SchemaBuilder) => WrappedType));
}

I believe they should be updated to:

export declare class WrappedType {
    readonly type: Types.NamedTypeDef | DirectiveTypeDef | GraphQLScalarType | ((schema: SchemaBuilder) => WrappedType | WrappedType[]); // <-- Allow to return WrappedType[]
    constructor(type: Types.NamedTypeDef | DirectiveTypeDef | GraphQLScalarType | ((schema: SchemaBuilder) => WrappedType | WrappedType[])); // <-- Allow to return WrappedType[]
}

This is already supported by nexus anyway

Override `printSchema`

Description

We'd like to be able to re-order how the schema is printed, mostly to put the Query and Mutation type on top of the file vs. the alphabetical order that is made at the moment.

As we're importing types from prisma with nexus-prisma, it creates a lot of noise especially with input types being huge. For that reason, it'd be useful for us to be able to decide how the schema is printed

Code/SDL generation of list/array field generates single, not array

I have a code-first inputObjectType which contains a list.field as one of the fields

export const AddToBasketInput = inputObjectType({
  name: "AddToBasketInput",
  definition(t) {
    t.field("activity", { type: "ActivityBasketInput" });
    t.list.field("extras", { type: "ExtraBasketInput" });
  }
});

However, when I run makeSchema() against this, the following is created:

schema.graphql

input AddToBasketInput {
  activity: ActivityBasketInput
  extras: ExtraBasketInput // should be [ExtraBasketInput]
}

types.d.ts

export interface NexusGenInputs {
  AddToBasketInput: { // input type
    activity?: NexusGenInputs['ActivityBasketInput'] | null;
    extras?: NexusGenInputs['ExtraBasketInput'] | null; // should be NexusGenInputs['ExtraBasketInput'][] | null
  }
}

This doesn't happen for type generation, only for input generation. Would you be able to have a look please?

We're really excited by code-first (so much less copy/paste!) so thank you!

release: Automate the release process.

I would like to propose you to add 2 dev dependencies to automate the release process and ease respository changes communication.

These tools are:

Commitizen will ensure we are using convention whilre writing commit so that we can auto generate changelog later. more info https://www.conventionalcommits.org . It would be trivial to install it because you are already using husky hooks mechanism

semantic release helps deploying npm packages by automating multiples things.
By using it, we would be able to automate the release process if in addition we use Github Actions.

@semantice-release/changelog is a semantic-release plugin and can generate a changelog based on the commit history of a repository. This auto generated changelog would eases the changes communication

What do you think?

Possible Feature: Add mutationField abstraction

Mutations are usually best split up, and are likely the most common use-case for extendType. Consider adding a helper for this common case:

export const createUser = mutationField('createUser', {
  type: SomeType,
  resolve() {
    // ...
  }
})

as shorthand for:

export const createUser = extendType({
  type: "Mutation",
  definition(t) {
    t.field('createUser', {
      type: SomeType
      resolve() {
        // ...
      }
    })
  }
})

Open question: Can/should we extend this general concept to more types than Mutation / Query?

Subscription type checking error with Prisma $subscribe

Hi everyone! Yesterday I’ve implemented the subscription in my project using nexus/nexus-prisma. By default, function call prisma.$subscribe.type() returns asyncIterator which resolves as {node: Type, mutation: MutationType, updatedFields: [String], previousValue: TypePreviousValue} and this structure is convenient for my case. But if I use this asyncIterator in my subscription - nexus throws an error for the subscription in Playground, which says: Unknown prisma-client function for field TypeSubscriptionPayload.mutation, but in resolve function it's a normal object with normal values. Any ideas? Apollo server works fine it this case and I think that this problem related to the implementation of subscription on Nexus.

Packages versions:
nexus-prisma: 0.3.5
nexus: 0.11.3

Authorization

A common way to implement authorization rules in JS-based GraphQL servers right now is to use graphql-shield. While it's possible to use it together with Nexus, I was hoping there was a more "idiomatic" (i.e. type-safe) way to accomplish the same?

Can the Context be strongly typed yet?

Hi there,

I was refactoring some code in my code first schema and I was trying to get my Context as strongly typed on my resolver function. Is that possible?

I have something like:

const customerInOrgById: FieldResolver<"Query", "customerInOrgById"> = async (root, { organisationId, customerId }, context , info) => {
...

The args are strongly typed, but the context is of type any.
Is it possible to pass in the interface in the code first?

Thank you!

extend Input

It's possible to extend any Type that comes from prisma with extendType. That's a great feature of nexus! But it's not possible to do something like extendInputType.

If I use extendType on a InputType like this:

const createAccountInput = extendType({
  type: 'AccountCreateInput',
  definition(t): void {
    t.string('password', {nullable: false});
  },
});

I get this error:

Expected AccountCreateInput to be a possible input type, saw GraphQLObjectType

My Question/Feature request

It there a way to extend Input Type?

After upgrading to 0.11.1, typegen output is being written to schema.graphql

I’m on a Mac

Upgraded to 0.11.1 and after this upgrade my typegen output is not being generated due to error “Error: EEXIST: file already exists, mkdir ‘(graphql output schema directory path)’, errno: -17, syscall: mkdir

The graphql schema output is fine.

I suspect it’s related to the commit ae5141e adding recursive mkdir but I can’t fully make sense of it

Reason: 'username' Field 'username' is not defined in the input type 'UserWhereUniqueInput'.

I have an input (see below) built using nexus.

const UserUniqueInput = inputObjectType({
    name: 'UserWhereUniqueInput',
    definition(t) {
        t.id('id');
        t.string('username');
    }
});

And I ran this query

query ($where: UserWhereUniqueInput) {
  user(where:$where) {
    id
    username
    password
    posts {
      id
      title
      content
    }
  }
}

With the variables:

{
  "where": {
    "username": "jblevins"
  }
}

If I use id instead of username it works just fine. Did define my input incorrectly somwhere?

Here is the full error message.

Error: Variable '$where' expected value of type 'UserWhereUniqueInput!' but got: {"username":"jblevins"}. Reason: 'username' Field 'username' is not defined in the input type 'UserWhereUniqueInput'. (line 1, column 8):query ($where: UserWhereUniqueInput!) {

Promise error in Ghost example

Using code copied straight from ghost example. UserSource byIdLoader throws a tslint error:

Code:

dataSources/UserLoader

import DataLoader from "dataloader";
import { byColumnLoader } from "../utils/loaderUtils";
import { Context } from "./Context";
import { dbt } from "../generated";

export class UserSource {
  constructor(protected ctx: Context) {}

  byIdLoader = new DataLoader<string, dbt.Users>(ids => {
    return byColumnLoader(this.ctx, "users", "id", ids);
  });

  byId(id: string) {
    return this.byIdLoader.load(id);
  }
}

utils/loaderUtils

import _ from "lodash";
import { DBTables } from "../generated/db-tables";
import { Context } from "../dataSources/Context";

export function byColumnLoader<
  Tbl extends keyof DBTables,
  Key extends Extract<keyof DBTables[Tbl], string>,
  KeyType extends Extract<DBTables[Tbl][Key], string | number>
>(
  ctx: Context,
  table: Tbl,
  key: Key,
  keys: KeyType[]
): PromiseLike<DBTables[Tbl][]> {
  return ctx
    .knex(table)
    .select(`${table}.*`)
    .whereIn(`${table}.${key}`, keys)
    .then((rows: DBTables[Tbl][]) => {
      const keyed: Record<KeyType, DBTables[Tbl]> = _.keyBy(rows, key);
      return keys.map(k => {
        return keyed[k] || new Error(`Missing row for ${table}:${key} ${k}`);
      });
    });
}

Gives tslint error:

Argument of type '(ids: string[]) => PromiseLike<Users[]>' is not assignable to parameter of type 'BatchLoadFn<string, Users>'.
  Type 'PromiseLike<Users[]>' is missing the following properties from type 'Promise<(Users | Error)[]>': catch, [Symbol.toStringTag], finallyts(2345)

image

How to write a mutation arg as a list of ints

First of all, thank you for nexus! It's awesome!

I have this Mutation:

type Mutation {
    changeSupplier(productIds: [Int], supplierId: Int): Boolean
}

How would one represent an argument as a list of int with nexus?
The converter doesn't give the args.

const Mutation = objectType({
  name: "Mutation",
  definition(t) {
    t.boolean("changeSupplier", {nullable:true})
  }
})

Thanks!

allow custom directives

if i understand correctly there is only @deprecated possible so far? it there way to add custom ones?
tools out there usually depend on custom directives then i could integrate nexus into toolchain

How to do schema stitching with Nexus?

Hi there!

This is a question not an issue or a bug 😄

What is it the best way to do schema stitching with nexus?
The only thing I can think about is using the converter to convert the schemas to code first, but it doesn’t sound ideal and I am not sure if it is possible to use fragment with it.

Thanks for the amazing work!

Typescript decorators

Seems like there's an opportunity here to simplify the DX using Typescript decorators. I'd be interested in contributing if this is a direction you want to go.

generateSelections is passed incorrect name, error: Could not find id for type User.

I was testing my SDL Converted schema and the create inputs are working just fine, but when I use an input on any kind of query it does not work.

After doing some console.logging in prisma-client-lib in Client.js's generateSelections, I found the cause.

I ran the following mutation with a console log in the following line of code:

mutation ($data:UserCreateInput!) {
  createUser(data:$data) {
    id
    username
    password
  }
}

var inputArg = instruction.field.args.find(function (arg) { return arg.name === name; });

Here is the output of args.name and arg:

arg.name: data and name: data

I ran this query:

query ($where: UserWhereInput!) {
  users(where:$where) {
    id
    username
    password
  }
}

Here is the output for my query.

arg.name: where and name: id
arg.name: orderBy and name: id
arg.name: skip and name: id
arg.name: after and name: id
arg.name: before and name: id
arg.name: first and name: id
arg.name: last and name: id

Here are the variables for my query:

{
  "where": {
    "id": "cjt99apz6000g0920yl6xs3x6"
  }
}

It looks as if id is getting passed into the name argument of generateSelections.

I did some more testing after finding this, the only thing that works on a query currently is:

where:{}

because Object.keys(instructions.args).length is 0 and it bypasses most of instructions.reduceRight's logic completely.

image

Type 'Promise<boolean>' is not assignable to type 'boolean | PromiseLike<false> | PromiseLike<true>'

I have a mutation where I want to return a boolean value of true but getting this error:
Type 'Promise<boolean>' is not assignable to type 'boolean | PromiseLike<false> | PromiseLike<true>'

Mutation field:

export const Mutation = mutationType({
  definition(t) {
    t.field('forgotPassword', {
      type: 'Boolean',
      args: {
        email: stringArg(),
      },
      resolve: async (parent, { email }, ctx) => {
        const user = await ctx.prisma.user({ email })

        if (!user) {
          return true
        }

        return true
      },
    })
  },
})

full error message:

Type '(parent: {}, { email }: { email: string; }, ctx: Context) => Promise<boolean>' is not assignable to type 'FieldResolver<"Mutation", "forgotPassword">'.
  Type 'Promise<boolean>' is not assignable to type 'boolean | PromiseLike<false> | PromiseLike<true>'.
    Type 'Promise<boolean>' is not assignable to type 'PromiseLike<true>'.
      Types of property 'then' are incompatible.
        Type '<TResult1 = boolean, TResult2 = never>(onfulfilled?: ((value: boolean) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined) => Promise<...>' is not assignable to type '<TResult1 = true, TResult2 = never>(onfulfilled?: ((value: true) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined) => PromiseLike<...>'.
          Types of parameters 'onfulfilled' and 'onfulfilled' are incompatible.
            Types of parameters 'value' and 'value' are incompatible.
              Type 'boolean' is not assignable to type 'true'.ts(2322)
definitionBlocks.d.ts(52, 3): The expected type comes from property 'resolve' which is declared here on type 'NexusOutputFieldConfig<"Mutation", "forgotPassword"> & { resolve: FieldResolver<"Mutation", "forgotPassword">; }'
(property) resolve: FieldResolver<"Mutation", "forgotPassword">

Interop with graphql-js

I'm investigating using this to progressively replace graphql to generate the schema of a large application. Using types defined with graphql works well by referencing them using their name but I haven't found I way to use types defined with nexus inside a type defined with graphql.

I did try using extendType to extend my types defined using graphql but that doesn't seem to work currently. I'm getting a duplicate type error: Error: Schema must contain unique named types but contains multiple types named "Viewer"..

Here's a simplified example

const Viewer = new GraphQLObjectType({
  name: 'Viewer',
  fields: {
    name: { type: GraphQLString },
  },
});

// Using extendType to extend types defined with `graphql`
const NexusViewer = extendType({
  type: 'Viewer',
  definition(t) {
    t.int('age');
  },
});

const Query = new GraphQLObjectType({
  name: 'Query',
  fields: () => ({
    viewer: { type: Viewer },
  }),
});

const schema = makeSchema({
  types: [
    Query,
    NexusViewer,
  ],
});

Or alternatively provide a way to convert to a graphql type.

const NexusObjectType = objectType({ name: 'NexusObject' definition() { /* ... */ } });

const GraphQLObjectType = new GraphQLObject({
  name: 'GraphQLObject',
  fields: { nexus: { type: toGraphQLType(NexusObjectType) } }
});

VSCode extension to convert SDL

Like Markdown Preview Enhanced shows Markdown Previews on the side. This would output SDL conversion to Nexus :)

I'm talking about this icon which comes built-in with VSCode for Markdown support. Something like that for SDL would be fun 😜

Markdown Preview Icon

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.