graphql-nexus / nexus-plugin-prisma

Deprecated
MIT License
828 stars 118 forks source link

Return JSON object from `t.field` resolver #881

Open jamesopti opened 4 years ago

jamesopti commented 4 years ago

Since prisma2 does not yet support JSON/B columns, I'm storing my JSON data in Posgres as a string.

This is fine, but Id like to return JSON to my consumers.

How can I change the return type of a string field to an object in my graphql schema?

The example below returns this error: "message": "String cannot represent value: {}",

I think I need to tell GraphQL that the field metadata actually returns an object now.

t.field("metadata", {
  type: "String",
  resolve(root, args, ctx) {
  const str = root["metadata"];
    if (!str) {
      return {};
    }
    return JSON.parse(str);
  }
});

Im relatively new to GraphQL and prisma, so any help is appreciated! Thanks

tre-dev commented 4 years ago

Good question. It's also worth mentioning that seemingly it's faster to parse JSON. If I understood the video below correctly, it should be faster to parse the JSON as string on the client, than to parse the actual JSON.

https://www.youtube.com/watch?v=ff4fgQxPaO0

AndresRodH commented 4 years ago

Make a new JSON scalar type if you do not have one available already. The following snippet is just a very basic snippet that will pass-through anything. I'd recommend looking at graphql-type-json for a more thorough implementation:

// jsonScalar.ts
import { GraphQLScalarType } from 'graphql'

export const JSONScalar = new GraphQLScalarType({
  name: 'JSON',
  serialize: (data: any) => data,
  parseValue: (data: any) => data,
});

// updated example
t.field('metadata', {
  type: 'JSON',
  resolve(root, args, ctx) {
  const str = root["metadata"];
    if (!str) {
      return {};
    }
    return JSON.parse(str);
  }
})

If you end up implementing serialize and parseValue like graphql-type-json and if json is a common output then I'd suggest using asNexusMethod and the scalarType constructor from the library.

// jsonScalar.ts
import { scalarType } from 'nexus'

export const JSONScalar = scalarType({
  name: 'JSON',
  asNexusMethod: 'json',
  // check out graphql-type-json for inspiration on how to handle the rest of the scalar constructor
})

// updated example
t.json('metadata') // that's it! The scalar type would do all the parsing for you

Hope this helps 😄

5achinJani commented 4 years ago

For people using prisma can fix this by just adding a nexus-prisma type which has a field with type json in their schema and there you go you can now use json as a scalar type.

playerx commented 4 years ago
import { GraphQLJSONObject } from 'graphql-type-json'
import { scalarType } from 'nexus'

export const JSONScalar = scalarType({
  name: 'JSON',
  serialize: GraphQLJSONObject.serialize,
  parseValue: GraphQLJSONObject.parseValue,
  parseLiteral: GraphQLJSONObject.parseLiteral,
})
geyang commented 2 years ago

Can I use this as a query input argument?