Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an option (or a way) to force Prisma return all scalar entity properties #194

Closed
terion-name opened this issue Jun 30, 2018 · 8 comments

Comments

@terion-name
Copy link

IMHO it is a must-have feature and one of those, that make it very hard to make anything more than a primitive CRUD on top of Prisma.

We use prisma bindings mostly to proxy client requests to Prisma, passing down info object. In response it will return the data, that is requested and only it. But this approach fails with custom fields.

Example situation:
We have a File in datamodel:

type File {
    id: ID! @unique
    path: String!
    filename: String!
    mimetype: String!
    encoding: String!
}

I want to add a calculated field url, that will generate a public link to storage (private s3-comaptible cloud)

So my application schema contains:

type File {
    id: ID!
    path: String!
    filename: String!
    mimetype: String!
    encoding: String!
    url: String
}

And resolver:

File {
   url(parent) => generateUrl(parent.path);
}

Fail is that this will only work if client will request both url and path, because otherwise response from Prisma simply will not contain path, because it contains only requested fields.

As a workaround one can manually request all fields in resolver, but this just kills the entire idea of Prisma and bindings, also gives a lot of problems handling relations

@marktani
Copy link
Contributor

marktani commented Jul 5, 2018

Hey @terion-name 👋 This is a great question. I would suggest you check out fragments.

fragments are a way to tell Prisma binding which data is necessary for your resolver to be correctly executed. I suggest you use them in situations where you are resolving a subfield of a particular type - in your case url of File.

The graphql-server-example showcases the use of fragments. The particular file that would be of interest for you is here: https://github.com/prismagraphql/graphql-server-example/blob/master/src/resolvers/Home.ts.

In the Home resolver and in particular the numRatings field, we are asking for the id of the Home node whenever our user wants to find the number of ratings. In your situation, fragment would probably look like this: fragment FilePath on File { path }, and the path variable would then be accessible in the parent argument of the resolve function:

export const File = {
  fragment: `fragment FilePath on File { path }`,
  resolve: async ({ path }, args, ctx, info) => {
    return generateUrl(path)
  }
}

This should resolve your use case, so I'm closing this issue 🙂Let me know if there's anything else you want to learn about this!

@terion-name
Copy link
Author

terion-name commented Jul 20, 2018

@marktani I can't make it work :(

Not with files, with another model:

type Link {
    id: ID! @unique
    link: String!
    order: Int!
    status: LINK_STATUS @default(value: "INACTIVE")
    translations: [LinkTranslation!]!
}

resolver:

Link:
   { link:
      { fragment: 'fragment ForceOrder on Link { order }',
        resolve: [Function] } },

resolver:

function (p, args, ctx, info) {
    console.log(p);
    return p.link;
  }

Query:

query {
  links {
    status
    link
  }
}

Console output:

{ status: 'INACTIVE', link: 'http://foo.bar' }

Server — graphql-yoga

It does not contain order :(

This also doesn't work:

{ Query:
   { links:
      { fragment: 'fragment ForceOrder on Link { order }',
        resolve: [Function] },
}}

@gazzer82
Copy link

I’m also struggling with this and graphql-yoga, did you ever get it to work?

@terion-name
Copy link
Author

@gazzer82 nope (

@maticzav
Copy link
Collaborator

Hey @terion-name 👋,

Can you double check that you extracted fragment replacements from resolvers and applied them to Prisma? Usually it's something similar to this;

// resolvers.ts
import { extractFragmentReplacements } from 'prisma-binding'
// Query
import { Query } from './Query'
...


export const resolvers = {
  Query,
  ...
  UserFollowedBy,
  AuthPayload,
}

export const fragmentReplacements = extractFragmentReplacements(resolvers)

// index.ts

import { resolvers, fragmentReplacements } from './resolvers'

const server = new GraphQLServer({
  typeDefs: './src/schema.graphql',
  resolvers,
  context: req => ({
    ...req,
    db: new Prisma({
      fragmentReplacements, // <--
      endpoint: process.env.PRISMA_ENDPOINT,
      secret: process.env.PRISMA_SECRET,
    }),
  }),
})

I hope this helps 🙂

@terion-name
Copy link
Author

@maticzav and how does resolver (e.g. UserFollowedBy ) should look like in your example?

@maticzav
Copy link
Collaborator

UserFollowedBy is not a great example to portray the idea. Take a look at the case below; I believe it should be pretty self-explanatory;

# schema.graphql

type Image {
  id: ID!
  url: String!
}

# datamodel.graphql

type Image {
  id: ID!
  createdAt: DateTime!
  updatedAt: DateTime!
  owner: User! @relation(name: "UserImage")
}
import { Context } from '../utils'

export const Image = {
  url: {
    fragment: `fragment ImageID on Image { id }`,
    resolve: async ({ id }, args, ctx: Context, info) => {
      return `http://cdn.myservice.com/${id}`
    },
  }
}

@faurehu
Copy link

faurehu commented Dec 17, 2018

Hey @marktani. The sample project does not have a Home resolver anymore so your link on your first response is broken. Maybe also provide a link to fragment docs?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants