Skip to content

Commit

Permalink
Safer serialization for BigInt
Browse files Browse the repository at this point in the history
  • Loading branch information
ardatan committed Mar 27, 2023
1 parent ee66d2a commit ef0585a
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 13 deletions.
6 changes: 6 additions & 0 deletions .changeset/witty-cherries-tickle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'graphql-scalars': patch
---

If JSON serializer is missing for `BigInt` use `number` serialization for safe integers and `string`
for unsafe integers by warning the users.
48 changes: 35 additions & 13 deletions src/scalars/BigInt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,33 @@ import { GraphQLScalarType, GraphQLScalarTypeConfig, print } from 'graphql';
import { createGraphQLError } from '../error.js';
import { serializeObject } from './utilities.js';

export const GraphQLBigIntConfig: GraphQLScalarTypeConfig<bigint | number, bigint | string | number> = /*#__PURE__*/ {
let warned = false;

function isSafeInteger(val: bigint): boolean {
return val <= Number.MAX_SAFE_INTEGER && val >= Number.MIN_SAFE_INTEGER;
}

function serializeSafeBigInt(val: bigint): bigint | number | string {
if ('toJSON' in BigInt.prototype) {
return val;
}
if (isSafeInteger(val)) {
return Number(val);
}
if (!warned) {
warned = true;
console.warn(
'By default, BigInts are not serialized to JSON as numbers but instead as strings which may lead an unintegrity in your data. ' +
'To fix this, you can use "json-bigint-patch" to enable correct serialization for BigInts.',
);
}
return val.toString();
}

export const GraphQLBigIntConfig: GraphQLScalarTypeConfig<
bigint | number,
bigint | string | number
> = /*#__PURE__*/ {
name: 'BigInt',
description: 'The `BigInt` scalar type represents non-fractional signed whole numeric values.',
serialize(outputValue) {
Expand Down Expand Up @@ -33,22 +59,14 @@ export const GraphQLBigIntConfig: GraphQLScalarTypeConfig<bigint | number, bigin
if (!Number.isInteger(coercedValue)) {
throw createGraphQLError(`BigInt cannot represent non-integer value: ${coercedValue}`);
}
return coercedValue;
num = BigInt(coercedValue);
}

if (typeof num !== 'bigint') {
throw createGraphQLError(`BigInt cannot represent non-integer value: ${coercedValue}`);
}

if ('toJSON' in BigInt.prototype) {
return num;
}

if (Number.isSafeInteger(Number(num))) {
return num;
}

return num.toString();
return serializeSafeBigInt(num);
},
parseValue(inputValue) {
const bigint = BigInt(inputValue.toString());
Expand All @@ -59,7 +77,9 @@ export const GraphQLBigIntConfig: GraphQLScalarTypeConfig<bigint | number, bigin
},
parseLiteral(valueNode) {
if (!('value' in valueNode)) {
throw createGraphQLError(`BigInt cannot represent non-integer value: ${print(valueNode)}`, { nodes: valueNode });
throw createGraphQLError(`BigInt cannot represent non-integer value: ${print(valueNode)}`, {
nodes: valueNode,
});
}
const strOrBooleanValue = valueNode.value;
const bigint = BigInt(strOrBooleanValue);
Expand All @@ -77,4 +97,6 @@ export const GraphQLBigIntConfig: GraphQLScalarTypeConfig<bigint | number, bigin
},
};

export const GraphQLBigInt: GraphQLScalarType = /*#__PURE__*/ new GraphQLScalarType(GraphQLBigIntConfig);
export const GraphQLBigInt: GraphQLScalarType = /*#__PURE__*/ new GraphQLScalarType(
GraphQLBigIntConfig,
);

0 comments on commit ef0585a

Please sign in to comment.