Skip to content

Commit

Permalink
feat: add support for BigInt scalar (graphql-nexus#56)
Browse files Browse the repository at this point in the history
Co-authored-by: Jason Kuhrt <jasonkuhrt@me.com>
  • Loading branch information
iddan and jasonkuhrt authored Jul 7, 2021
1 parent d30ac3c commit 67ce824
Show file tree
Hide file tree
Showing 13 changed files with 413 additions and 32 deletions.
17 changes: 10 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ export const schema = makeSchema({

##### Shortterm

- [ ] ([#59](https://github.com/prisma/nexus-prisma/issues/59)) Support for Prisma Model field type `BigInt`
- [x] ([#59](https://github.com/prisma/nexus-prisma/issues/59)) Support for Prisma Model field type `BigInt`
- [ ] ([#94](https://github.com/prisma/nexus-prisma/issues/94)) Support for Prisma Model field type `Decimal`
- [ ] Improved JSDoc for relation 1:1 & 1:n fields

Expand Down Expand Up @@ -221,13 +221,16 @@ However some of the Prisma scalars do not have a natural standard representation

**Prisma Standard-Scalar to GraphQL Custom-Scalar Mapping**

| Prisma | GraphQL | Nexus `t` Helper | GraphQL Scalar Implementation |
| ---------- | ---------- | ---------------- | ----------------------------------------------------------------- |
| `Json` | `Json` | `json` | [JsonObject](https://github.com/Urigo/graphql-scalars#jsonobject) |
| `DateTime` | `DateTime` | `dateTime` | [DateTime](https://github.com/Urigo/graphql-scalars#datetime) |
| `Bytes` | `Bytes` | `bytes` | [Bytes](https://www.graphql-scalars.dev/docs/scalars/byte/) |
| Prisma | GraphQL | Nexus `t` Helper | GraphQL Scalar Implementation | Additional Info |
| ---------- | ---------- | ---------------- | --------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| `Json` | `Json` | `json` | [JsonObject](https://www.graphql-scalars.dev/docs/scalars/jsonobject) | |
| `DateTime` | `DateTime` | `dateTime` | [DateTime](https://www.graphql-scalars.dev/docs/scalars/datetime) | |
| `BigInt` | `BigInt` | `bigInt` | [BigInt](https://www.graphql-scalars.dev/docs/scalars/big-int) | [JavaScript BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt) |
| `Bytes` | `Bytes` | `bytes` | [Byte](https://www.graphql-scalars.dev/docs/scalars/byte/) | [Node.js Buffer](https://nodejs.org/api/buffer.html#buffer_buffer) |

> **Note:** Not all Prisma scalar mappings are implemented yet: `BigInt`, `Decimal`, `Unsupported`
> **Note:** Not all Prisma scalar mappings are implemented yet: `Decimal`, `Unsupported`
> **Note:** BigInt is supported in Node.js since version [10.4.0](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#browser_compatibility) however to support BigInt in `JSON.parse`/`JSON.stringify` you must use [`json-bigint-patch`](https://github.com/ardatan/json-bigint-patch) otherwise BigInt values will be serialized as strings.
You can use your own GraphQL Scalar Implementation, however, you _must adhear to the above Prisma/GraphQL name mapping defined above_.

Expand Down
20 changes: 12 additions & 8 deletions src/entrypoints/scalars.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
import { BigInt } from '../scalars/BigInt'
import { Bytes } from '../scalars/Bytes'
import { DateTime } from '../scalars/DateTime'
import { Json } from '../scalars/Json'
import { Bytes } from '../scalars/Bytes'

/**
* Predefined Nexus scalar type definitions to satisfy all custom scalars needed in GraphQL to map to the
* native scalars in Prisma. The mapping is as follows:
*
* | Prisma | GraphQL | Nexus `t` Helper | GraphQL Scalar Implementation |
* | ---------- | ---------- | ---- | ----------------------------------------------------------------- |
* | `Json` | `Json` | `json` | [JsonObject](https://github.com/Urigo/graphql-scalars#jsonobject) |
* | `DateTime` | `DateTime` | `datetime` | [DateTime](https://github.com/Urigo/graphql-scalars#datetime) |.
* | Prisma | GraphQL | Nexus `t` Helper | GraphQL Scalar Implementation | Additional Info |
* | ---------- | ---------- | ---------------- | --------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
* | `Json` | `Json` | `json` | [JsonObject](https://www.graphql-scalars.dev/docs/scalars/jsonobject) | |
* | `DateTime` | `DateTime` | `dateTime` | [DateTime](https://www.graphql-scalars.dev/docs/scalars/datetime) | |
* | `BigInt` | `BigInt` | `bigInt` | [BigInt](https://www.graphql-scalars.dev/docs/scalars/big-int) | [JavaScript BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt) |
* | `Bytes` | `Bytes` | `bytes` | [Byte](https://www.graphql-scalars.dev/docs/scalars/byte/) | [Node.js Buffer](https://nodejs.org/api/buffer.html#buffer_buffer) |
*
* @example
*
* // Use this defualt export
* // Use this default export
*
* import { makeSchema, objectType } from 'nexus'
* import NexusPrismaScalars from 'nexus-prisma/scalars'
Expand All @@ -35,7 +38,7 @@ import { Bytes } from '../scalars/Bytes'
*
* @example
*
* // Use only select precefined custom scalars
* // Use only select predefined custom scalars
*
* import { makeSchema, objectType } from 'nexus'
* import { Json } from 'nexus-prisma/scalars'
Expand Down Expand Up @@ -70,11 +73,12 @@ import { Bytes } from '../scalars/Bytes'
* API. For convenience you can use these ones.
*/
const NexusPrismaScalars = {
BigInt,
Bytes,
DateTime,
Json,
}

export default NexusPrismaScalars

export { Bytes, DateTime, Json }
export { BigInt, Bytes, DateTime, Json }
16 changes: 8 additions & 8 deletions src/generator/models/declaration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { DMMF } from '@prisma/generator-helper'
import dedent from 'dindist'
import * as OS from 'os'
import { LiteralUnion } from 'type-fest'
import { StandardGraphQLScalarType, StandardgraphQLScalarTypes } from '../../helpers/graphql'
import { StandardGraphQLScalarType, StandardGraphQLScalarTypes } from '../../helpers/graphql'
import { PrismaScalarType } from '../../helpers/prisma'
import { allCasesHandled } from '../../helpers/utils'
import { Gentime } from '../gentime/settingsSingleton'
Expand Down Expand Up @@ -276,25 +276,25 @@ export function fieldTypeToGraphQLType(

if (field.isId) {
if (field.type === 'String' || (field.type === 'Int' && settings.projectIdIntToGraphQL === 'ID')) {
return StandardgraphQLScalarTypes.ID
return StandardGraphQLScalarTypes.ID
}
}

switch (typeName) {
case 'String': {
return StandardgraphQLScalarTypes.String
return StandardGraphQLScalarTypes.String
}
case 'Int': {
return StandardgraphQLScalarTypes.Int
return StandardGraphQLScalarTypes.Int
}
case 'Boolean': {
return StandardgraphQLScalarTypes.Boolean
return StandardGraphQLScalarTypes.Boolean
}
case 'Float': {
return StandardgraphQLScalarTypes.Float
return StandardGraphQLScalarTypes.Float
}
case 'BigInt': {
return StandardgraphQLScalarTypes.String
return 'BigInt'
}
case 'DateTime': {
return 'DateTime'
Expand All @@ -306,7 +306,7 @@ export function fieldTypeToGraphQLType(
return 'Bytes'
}
case 'Decimal': {
return StandardgraphQLScalarTypes.String
return StandardGraphQLScalarTypes.String
}
default: {
return allCasesHandled(typeName)
Expand Down
2 changes: 1 addition & 1 deletion src/helpers/graphql.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export type StandardGraphQLScalarType = 'ID' | 'String' | 'Int' | 'Float' | 'Boolean'

export const StandardgraphQLScalarTypes = {
export const StandardGraphQLScalarTypes = {
ID: 'ID',
String: 'String',
Float: 'Float',
Expand Down
39 changes: 39 additions & 0 deletions src/scalars/BigInt.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { GraphQLScalarType } from 'graphql'
import { BigIntResolver } from 'graphql-scalars'
import { asNexusMethod } from 'nexus'

/**
* A Nexus scalar type definition for the `BigInt` scalar type
*
* Contributes a scalar to your GraphQL schema called `BigInt`.
*
* Contributes a `t` `[1]` helper method called `bigInt`
*
* `[1]` A `t` helper method refers to a method on the argument given to a `definition` method. Helper methods
* here typically help you quickly create new fields.
*
* @example
*
* import { makeSchema, objectType } from 'nexus'
* import { BigInt } from 'nexus-prisma/scalars'
*
* SomeObject = objectType({
* name: 'SomeObject',
* definition(t) {
* t.bigInt('someBigIntField')
* },
* })
*
* makeSchema({
* types: [BigInt, SomeObject],
* })
*
*/
export const BigInt = asNexusMethod(
new GraphQLScalarType({
...BigIntResolver,
description: `The \`BigInt\` scalar type represents non-fractional signed whole numeric values.
@see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt`,
}),
'bigInt'
)
26 changes: 26 additions & 0 deletions tests/e2e/__snapshots__/e2e.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,18 @@ Object {
"bars": Array [
Object {
"foo": Object {
"BigIntManually": null,
"BytesManually": null,
"DateTimeManually": null,
"JsonManually": null,
"someBigIntField": "9007199254740991",
"someBytesField": Object {
"data": Array [],
"type": "Buffer",
},
"someDateTimeField": "2021-05-10T20:42:46.609Z",
"someEnumA": "alpha",
"someJsonField": Object {},
},
},
],
Expand All @@ -29,6 +32,12 @@ type Bar {
foo: Foo
}
\\"\\"\\"
The \`BigInt\` scalar type represents non-fractional signed whole numeric values.
@see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt
\\"\\"\\"
scalar BigInt
\\"\\"\\"The \`Byte\` scalar type represents byte value as a Buffer\\"\\"\\"
scalar Bytes
Expand All @@ -38,9 +47,11 @@ A date-time string at UTC, such as 2007-12-03T10:15:30Z, compliant with the \`da
scalar DateTime
type Foo {
BigIntManually: BigInt
BytesManually: Bytes
DateTimeManually: DateTime
JsonManually: Json
someBigIntField: BigInt!
someBytesField: Bytes!
someDateTimeField: DateTime!
someEnumA: SomeEnumA
Expand Down Expand Up @@ -74,6 +85,11 @@ import type * as PrismaClient from \\".prisma/client\\"
import type { core } from \\"nexus\\"
declare global {
interface NexusGenCustomInputMethods<TypeName extends string> {
/**
* The \`BigInt\` scalar type represents non-fractional signed whole numeric values.
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt
*/
bigInt<FieldName extends string>(fieldName: FieldName, opts?: core.CommonInputFieldConfig<TypeName, FieldName>): void // \\"BigInt\\";
/**
* The \`Byte\` scalar type represents byte value as a Buffer
*/
Expand All @@ -90,6 +106,11 @@ declare global {
}
declare global {
interface NexusGenCustomOutputMethods<TypeName extends string> {
/**
* The \`BigInt\` scalar type represents non-fractional signed whole numeric values.
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt
*/
bigInt<FieldName extends string>(fieldName: FieldName, ...opts: core.ScalarOutSpread<TypeName, FieldName>): void // \\"BigInt\\";
/**
* The \`Byte\` scalar type represents byte value as a Buffer
*/
Expand Down Expand Up @@ -123,6 +144,7 @@ export interface NexusGenScalars {
Float: number
Boolean: boolean
ID: string
BigInt: any
Bytes: any
DateTime: any
Json: any
Expand All @@ -149,9 +171,11 @@ export interface NexusGenFieldTypes {
foo: NexusGenRootTypes['Foo'] | null; // Foo
}
Foo: { // field return type
BigIntManually: NexusGenScalars['BigInt'] | null; // BigInt
BytesManually: NexusGenScalars['Bytes'] | null; // Bytes
DateTimeManually: NexusGenScalars['DateTime'] | null; // DateTime
JsonManually: NexusGenScalars['Json'] | null; // Json
someBigIntField: NexusGenScalars['BigInt']; // BigInt!
someBytesField: NexusGenScalars['Bytes']; // Bytes!
someDateTimeField: NexusGenScalars['DateTime']; // DateTime!
someEnumA: NexusGenEnums['SomeEnumA'] | null; // SomeEnumA
Expand All @@ -167,9 +191,11 @@ export interface NexusGenFieldTypeNames {
foo: 'Foo'
}
Foo: { // field return type name
BigIntManually: 'BigInt'
BytesManually: 'Bytes'
DateTimeManually: 'DateTime'
JsonManually: 'Json'
someBigIntField: 'BigInt'
someBytesField: 'Bytes'
someDateTimeField: 'DateTime'
someEnumA: 'SomeEnumA'
Expand Down
25 changes: 24 additions & 1 deletion tests/e2e/e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { inspect } from 'util'
import { assertBuildPresent } from '../__helpers__/helpers'
import { createPrismaSchema } from '../__helpers__/testers'
import { setupTestProject, TestProject } from '../__helpers__/testProject'
import * as GQLScalars from 'graphql-scalars'

const d = debug('e2e')

Expand Down Expand Up @@ -120,6 +121,7 @@ it('When bundled custom scalars are used the project type checks and generates e
someJsonField Json
someDateTimeField DateTime
someBytesField Bytes
someBigIntField BigInt
someEnumA SomeEnumA
bar Bar?
}
Expand Down Expand Up @@ -154,7 +156,8 @@ it('When bundled custom scalars are used the project type checks and generates e
id: 'foo1',
someDateTimeField: new Date("2021-05-10T20:42:46.609Z"),
someBytesField: Buffer.from([]),
someJsonField: JSON.stringify({}),
someJsonField: {},
someBigIntField: BigInt(9007199254740991),
someEnumA: 'alpha',
bar: {
create: {
Expand Down Expand Up @@ -211,6 +214,8 @@ it('When bundled custom scalars are used the project type checks and generates e
t.json('JsonManually')
t.dateTime('DateTimeManually')
t.bytes('BytesManually')
t.bigInt('BigIntManually')
t.field(Foo.someBigIntField)
t.field(Foo.someJsonField)
t.field(Foo.someDateTimeField)
t.field(Foo.someBytesField)
Expand Down Expand Up @@ -321,6 +326,9 @@ it('When bundled custom scalars are used the project type checks and generates e
expect(stripAnsi(results.runFirstBuild.stdout)).toMatch(
/.*error TS2339: Property 'dateTime' does not exist on type 'ObjectDefinitionBlock<any>'.*/
)
expect(stripAnsi(results.runFirstBuild.stdout)).toMatch(
/.*error TS2339: Property 'bigInt' does not exist on type 'ObjectDefinitionBlock<any>'.*/
)

expect(results.runReflectPrisma.exitCode).toBe(0)

Expand Down Expand Up @@ -372,9 +380,12 @@ it('When bundled custom scalars are used the project type checks and generates e
JsonManually
DateTimeManually
BytesManually
BigIntManually
someEnumA
someJsonField
someDateTimeField
someBytesField
someBigIntField
}
}
}
Expand All @@ -390,4 +401,16 @@ it('When bundled custom scalars are used the project type checks and generates e
d(`stopped server`)

expect(data).toMatchSnapshot('client request 1')

const [{ foo }] = data.bars

expect(foo.JsonManually).toBeNull()
expect(foo.DateTimeManually).toBeNull()
expect(foo.BytesManually).toBeNull()
expect(foo.BigIntManually).toBeNull()
expect(typeof foo.someEnumA).toEqual('string')
expect(typeof foo.someJsonField).toEqual('object')
expect(typeof foo.someDateTimeField).toEqual('string')
expect(typeof foo.someBytesField).toEqual('object')
expect(typeof GQLScalars.BigIntResolver.parseValue(foo.someBigIntField)).toEqual('bigint')
}, 60_000)
1 change: 1 addition & 0 deletions tests/integration/json.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ testIntegration({
apiSchema({ Foo }) {
return [
NexusPrismaScalars.Bytes,
NexusPrismaScalars.BigInt,
NexusPrismaScalars.DateTime,
NexusPrismaScalars.Json,
objectType({
Expand Down
16 changes: 9 additions & 7 deletions tests/unit/customScalarsModule.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,20 @@ assertBuildPresent()

it('scalars can be accessed via namespace import', () => {
expect(Object.keys(NexusPrismaScalarsNS)).toMatchInlineSnapshot(`
Array [
"Bytes",
"DateTime",
"Json",
"default",
]
`)
Array [
"BigInt",
"Bytes",
"DateTime",
"Json",
"default",
]
`)
})

it('scalars can be accessed via a default import', () => {
expect(Object.keys(NexusPrismaScalars)).toMatchInlineSnapshot(`
Array [
"BigInt",
"Bytes",
"DateTime",
"Json",
Expand Down
Loading

0 comments on commit 67ce824

Please sign in to comment.