How to reference generated Prisma schema in my schema.graphql file for a query that filters by id - node.js

Trying to add a Query that filters by the unique Id of this object.
Query.js
async function getAbility (root, args, context, info) {
return await context.prisma.ability({
where : {id : args.abilityId}
}, info)
}
This is also defined in my schema.graphql file.
getAbility(where: AbilityWhereUniqueInput) : Ability
I recognize that AbilityWhereUniqueInput comes from the schema generation done with the Prisma CLI however I am unsure how to reference it for the schema.graphql file.
I have attempted to add this at the top of the file:
# import * from './generated/prisma-client/prisma-schema'
But whenever I try to run the application, it is saying that it encounters an unexpected character '.', referring to the first part of the file path i'm providing for the import.
Other Relevant Declarations:
schema.graphql
type Ability {
id: ID!
name: String!
description: String!
imagePath: String!
}

prisma.yml
# Specifies the HTTP endpoint of your Prisma API (deployed to a Prisma Demo server).
endpoint: https://eu1.prisma.sh/public-cookiearm-769/exp-graphql-subscription/dev
# Defines your models, each model is mapped to the database as a table.
datamodel: datamodel.prisma
# Specifies the language and directory for the generated Prisma client.
generate:
- generator: javascript-client
output: ../src/generated/prisma-client
- generator: graphql-schema
output: ../src/generated/prisma.graphql
# Seed your service with initial data based on `seed.graphql`.
seed:
import: seed.graphql
# Ensures Prisma client is re-generated after a datamodel change.
hooks:
post-deploy:
- prisma generate
# If specified, the `secret` must be used to generate a JWT which is attached
# to the `Authorization` header of HTTP requests made against the Prisma API.
# Info: https://www.prisma.io/docs/prisma-graphql-api/reference/authentication-ghd4/
# secret: mysecret123
Here you see I'm generating two files. One for prisma client and other for importing types in schema.graphql.
schema.graphql
# import * from './generated/prisma.graphql'
type Query {
feed: [Post!]!
drafts: [Post!]!
post(id: ID!): Post
}
type Mutation {
createDraft(title: String!, content: String): Post
editPost(id: String!, data: PostUpdateInput!): Post
deletePost(id: ID!): Post
publish(id: ID!): Post
}
type Subscription {
post: Post!
}
type Post {
id: ID!
published: Boolean!
title: String!
content: String!
}
See this line
# import * from './generated/prisma.graphql'
Now I can use PostUpdateInput in schema.graphql
Make sure that you changes the path following yours.

Make sure the imported generated schema is .graphql extension, because you can't import non-graphql files in a graphql file.

Related

Gatsby `S3Object` that doesn't exist in the schema. Use `createTypes` to add the type before adding resolvers

Im trying to pull remote images from my S3 Bucket in Gatsby with the below code. I have a schema.graphql that builds a classic S3Object. But Gatsby Node throws the below error. I've been in the docs for days in this issue, can anyone point me in the right direction? I just need to get the image into the data layer of Gatsby so I can use Gatsby-image.
I have a feeling I need to update the S3Object to extend the Node Interface, i'm working on this now.
Error:
warn `createResolvers` passed resolvers for type `S3Object` that doesn't exist in the schema. Use `createTypes` to add the type before adding resolvers.
schema.graphql
type PortfolioItem #model #key(fields: ["title", "createdAt"]) {
id: ID!
title: String!
file: S3Object
thumbnailUrl: String
...
}
type S3Object {
bucket: String!
key: String!
region: String!
}
gatsby-node.js
exports.createResolvers = ({ actions, cache, createNodeId, createResolvers, store, reporter }) => {
const { createNode } = actions;
createResolvers({
S3Object: {
imageFile: {
type: `File`,
resolve(source, args, context, info) {
return createRemoteFileNode({
url: 'https://my-aws-bucket-url.s3.us-east-2.amazonaws.com/' + source.key,
store,
cache,
createNode,
createNodeId,
reporter,
});
},
},
},
});
};
Ok guys,
After literal weeks of investigating this. This is the answer, I know this will help dozens of people out there dealing with Gatsby and remote Images.
The entire goal here, was to have direct access to a remote image on a field on a Model in graphql DIRECTlY. No looping over long arrays of images from some Gatsby listAllImages query and crap like that. This adds an actual USEABLE GATSBY-IMAGE directly onto a field of your model to be used instantly.
Define an S3Object in your Schema.Graphql like below.
type S3Object {
bucket: String!
key: String!
region: String!
}
The answer here is creating a custom resolver in your gatsby node file. And the trick here, is going into the gatsby level schema and seeing the REAL name of your model. This is the secret. You need to run:
gatsby repl
This will put you into Gatsby's CLI and then enter:
schema
THIS WILL SHOW YOU THE COMPILED SCHEMA LEVEL NAMES. So your S3Object is actually called <your_api_name>_s3object. Using this name, that aparently had your api name prepended to it....smfh....is to be used with a custom resolver WITH a gql query inside of it to add a file to your model. See below. Now, where ever you add S3Object as a field type, you will have access to the image with an image-sharp query! You must also use gatsby-s3-image-source to grab all the images to be used into your files system!
This was a super complicated solve for a noob like me to Gatsby, I hope this solves someone elses remote file issue like mine!
exports.createResolvers = ({ createResolvers }) => {
const resolvers = {
ryanthedev_S3Object: {
imageFile: {
type: 'File', // this needs to be 'File', since it's not returning an 'ImageSharp' node anymore
resolve: (source, args, context, info) => {
// A promise is expected to be returned anyway so don't need
// to `await` the result here, if all we're doing is returning
// the `File` node
return context.nodeModel.runQuery({
query: {
filter: {
base: { eq: source.key },
},
},
type: 'File',
firstOnly: true,
});
},
},
},
};
createResolvers(resolvers);
};

Type sharing between frontend and backend without bundling the ORM

In a fullstack typescript project i wish to share the interface/types that i use on the backend project in the frontend project to avoid code duplication and keep consistency.
From search i found that this can be done by having a third "shared" project with these types and then using typescript's referenced projects to include it.
So i would have a Frontend project, Backend project and a Shared project that would be referenced by the first two.
Although my types extend the mongoose's Document type, witch would cause the need for mongoose to be bundled in the frontend project in order to use the same types in both the frontend and backend.
What is the proper way to share these interfaces/types without bundling the extra backend packages to the frontend? (and keeping the _id property that comes from the Document extension as well while we are at it).
RoadBook.ts (IRoadBook is the interface/type to be shared and mongoose schema,model declaration)
import mongoose, {Schema,Model,Document} from 'mongoose';
export interface IRoadBook extends Document {
Name : string;
Description : string;
}
const RoadBookSchema = new Schema({
Name : {type : String, required : true, unique: true},
Description : {type : String }
});
export default mongoose.model<IRoadBook>('RoadBook',RoadBookSchema);
Might be important to reference that the backend is a simple typescript/express/mongoose app and the frontend a react webapp.
Thank you in advance.
What you could do is define the IRoadBook interface in your ORM which doesn't extend Document except for the members that you need in both the front end and the back end. Then, in the back end, define the schema.
ORM
export interface IRoadBook {
_id: string; // or ObjectID or whatever type mongoose.Document['_id'] is
Name: string;
Description: string;
}
Back end
import mongoose, { Schema, Model, Document } from 'mongoose';
import { IRoadBook } from 'my-orm';
const RoadBookSchema = new Schema({
Name: { type: String, required: true, unique: true },
Description: { type: String }
});
export default mongoose.model<IRoadBook & Document>('RoadBook', RoadBookSchema);

How can I get mapped type names for a graphQL type using type-graphql

Okay, so I'm starting to dig into graphql a little bit, and I've built an api using koa, type-graphql, and sequelize-typescript. Everything works pretty well.... I managed to get a query working, and even managed to optimize a little bit by using graphql-fields to filter the columns I query in the database... However when I've aliased a field name, I can't seem to get the mapped name.....
For example, given the following ObjectType/Sequelize Model....
export interface IDepartment {
departmentId: number;
name: string;
description: string;
}
#ObjectType()
#Table({ underscored: true })
export class Department extends Model<Department> implements IDepartment {
#Field({ name: 'id' })
#PrimaryKey
#Column({ field: 'department_id'})
public departmentId: number;
#Field()
#Length({ max: 100 })
#Column
name: string;
#Field()
#Length({ max: 100 })
#AllowNull
#Column
description: string;
}
and sample query....
query {
department(name: "Test Dept") {
id
name,
description
}
}
sample resolver...
async department(#Arg('name') name: string, #Info() info: GraphQLResolveInfo) {
return Department.findOne({
where: { name }
});
}
This works just fine.... but when I do
async department(#Arg('name') name: string, #Info() info: GraphQLResolveInfo) {
let fields = Object.keys(getFields(info))
return Department.findOne({
attributes: fields,
where: { name }
});
}
(getFields is graphql-fields), it fails because the query specified field name id, which is what graphql-fields returns, but the column name is department_id (sequelize model name departmentId).
I've gone through the schema with a fine tooth comb, using the introspectionFromSchema function to see a detailed copy of my schema, but nowhere is there a mention of departmentId or department_id.... However I know it's out there somewhere because when I exclude the attributes field from my sequelize query, even though sequelize returns departmentId as the property name, when I return it from my resolver and it reaches the client, the property name is id.
Any help would be appreciated.... I'm trying to optimize everything by only fetching requested properties and not the entire object. I could always store the maps as separate constants and use those in my #Field definition, but I want to do that as a last resort, however if I can I'm trying to keep the code as lean as possible....
Thank you all in advance.
Unfortunately, the name option was introduced mostly to support resolvers inheritance. Using this for mapping the schema field names is a kinda undocumented feature so it's doesn't provide any mapping or exposing mapping metadata.
Using the name option for input or args types will be even worse - it will result in no access to the fields and the properties being undefined.
For now my recommendation is to just keep it simple and don't map the field names until a proper fix arrives.

Resolving nested data in express GraphQL

I am currently trying to resolve a simple recipe list that has a reference to ingredients.
The data layout looks like this:
type Ingredient {
name: String!
amount: Int!
unit: Unit!
recipe: Recipe
}
type Recipe {
id: Int!
name: String!
ingredients: [Ingredient]!
steps: [String]!
pictureUrl: String!
}
As I understand it, my resolvers should look like this:
The first one resolves the recipes and second one resolves the ingredient field in the recipe. It can (from my understanding) use the argument provided by recipe. In my recipe object, the ingredient is referenced by id (int), so this should be the argument (at least that's what I think).
var root = {
recipe: (argument) => {
return recipeList;
},
Recipe: {
ingredients: (obj, args, context) => {
//resolve ingredients
}
},
These resolvers are passed to the app like this:
app.use('/graphql', graphqlHTTP({
schema: schema,
graphiql: true,
rootValue: root,
}));
However, my resolver does not seem to be called. I would like the ingredients to be resolved on the fly when queried in my query.
The endpoint works, but as soon as I query for ingredients, an error with this message "message": "Cannot return null for non-nullable field Ingredient.name.", is returned.
When trying to log the incoming arguments in my resolver, I can see that it is never executed. Unfortunately, I can't find examples on how to do this with express-graphql when using it like I am.
How do I write seperate resolvers for nested types in express-graphQL?
Only resolvers for queries and mutations can be defined through root, and even then this is bad practice. I'm guessing you're building your schema using buildSchema, which is generally a bad idea since the generated schema will only use default resolvers.
The only way to define resolvers for a field like ingredients when using plain GraphQL.js is to not use buildSchema. Instead of generating your schema from a string, you would define it programatically (i.e. defining a GraphQLSchema and all the types it uses).
Doing the above is a huge pain, especially if you already have your schema defined in a string or document. So the alternative option is to use graphql-tools' makeExecutableSchema, which lets you inject those resolvers into your type definitions like you're trying to do. makeExecutableSchema returns a GraphQLSchema object, so you can use it with your existing code (you don't have to change your middleware to apollo-server if you don't want to).

Defining Schema with graphql-tools

I am trying to define my schema for my API. I am running into an issue where each resource has many different sections. Ideally I would like to just be able to say sections is a JSON object rather than define all the different modules within the sections. Is there a way I can do this? As far as I can tell, there doesn't seem to be a JSON type definition using graphql-tools
// Define your types here.
const typeDefs = `
type Resource {
id: ID,
title: String!,
slug: String!,
path: String!,
template: String!,
published: String!,
sections: Sections
}
type Sections {
// ...
}
type Query {
allLinks(filter: LinkFilter): [Link!]!
findResource(filter: ResourceFilter): [Resource!]!
}
`;
You'll need to import a custom JSON scalar. This module is one of the more popular ones available.
Anywhere in your typeDefs, add the following line:
scalar JSON
And then inside the resolvers object you pass to makeExecutableSchema:
const GraphqlJSON = require('graphql-type-json')
const resolvers = {
Query: //queries
Mutation: //mutations
// All your other types
JSON: GraphqlJSON
}
One word of warning: anytime you use JSON as a scalar, you lose flexibility. Part of the charm of GraphQL is that it allows clients to only query the fields they need. When you use JSON, your client will only be able to either query the whole chunk of JSON, or not at all -- they won't be able to pick and choose parts of it.

Resources