Import build schema from .graphql file - javascript

I want to create a GraphQL API with the following schema
app.use(
"/graphql",
graphQlHttp({
schema: buildSchema(`
type Event {
_id: ID!
title: String!
description: String!
price: Float!
}
input EventInput {
title: String!
description: String!
price: Float!
}
type QueryResolver {
getEvents: [Event!]!
}
type MutationResolver {
createEvent(eventInput: EventInput): [Event!]!
}
schema {
query: QueryResolver
mutation: MutationResolver
}
`),
rootValue: {}
})
);
Currently I am using it as a string in in the my main file. I want to separate it out in a graphql file. One way can be that we can read the file but I think it will not be efficient.
Can you please tell me how can I do this?

You can add this node module:
https://github.com/ardatan/graphql-import-node
Then move your schema to something like mySchema.graphql.
Then, in JS:
const mySchema = require('./mySchema.graphql');
or in TypeScript:
import * as mySchema from './mySchema.graphql';

I don't have the reputation to comment so I'll put this here.
If anyone following #Tim O'Connell's answer is wondering how to pass the imported Schema to BuildSchema(), since it's not a string anymore but a DocumentNode, you actually need to use the function BuildASTSchema().
Note that BuildSchema() is just a wrapper around BuildASTSchema() that parses the given string into a DocumentNode. (source)
So to recap, do something like this :
import 'graphql-import-node';
import { buildASTSchema } from 'graphql';
import * as mySchema from './schema.graphql';
export = buildASTSchema(mySchema);

Just import all of your schemas using the gql module. Install it by:
npm i graphql-tag

You can use the graphql-tool.
const { loadSchemaSync } = require("#graphql-tools/load");
const { GraphQLFileLoader } = require("#graphql-tools/graphql-file-loader");
const { addResolversToSchema } = require("#graphql-tools/schema");
const { join } = require("path");
const schemaWithResolvers = addResolversToSchema({
schema: loadSchemaSync(join(__dirname, "./(your graphql file).graphql"), {
loaders: [new GraphQLFileLoader()],
}),
resolvers: {},
});
And, your graphql file is
type Event {
_id: ID!
title: String!
description: String!
price: Float!
}
input EventInput {
title: String!
description: String!
price: Float!
}
type QueryResolver {
getEvents: [Event!]!
}
type MutationResolver {
createEvent(eventInput: EventInput): [Event!]!
}
schema {
query: QueryResolver
mutation: MutationResolver
}
Then, you just need to use schemaWithResolvers.builldSchema is unnecessary .
app.use(
"/graphql",
graphQlHttp({
schema: schemaWithResolvers
})
);

Related

Cannot fetch data in Graphql Relay

I'm learning Relay to use in a React-Relay project. After my research and learning on the internet, I've run into problems with my graphql schema and resolvers. I can't seem to figure out what resolvers do I need and how go about it correctly. If someone can point me in the right direction, that would be great. I have attached my code below. All I'm trying to do is fetch elements of a list using the relay graphql server specification.
Graphql Schema
interface Node {
id: ID!
}
type Link implements Node {
id: ID!
title: String!
description: String!
}
type LinkConnection {
edges: [LinkEdge]
pageInfo: PageInfo!
}
type LinkEdge {
cursor: String!
node: Link
}
type Query {
links(after: String, before: String, first: Int, last: Int): LinkConnection
node(id: ID!): Node
}
type PageInfo {
hasNextPage: Boolean!
hasPreviousPage: Boolean!
startCursor: String
endCursor: String
}
Resolvers
const resolvers = {
Query,
Node: {
__resolveType(node, context, info){
if(node.title){
return 'Link'
}
return null
}
},
}
Query Resolver
const node = async (parent, args, {prisma}) => {
try{
console.log('hit')
const data = await prisma.link.findUnique({
where: {
id: Number(args.id)
}
})
console.log(data)
return data
} catch(err){
return err
}
}
export default {
node,
}
P.S. Im using Apollo Server and Prisma under the hood
Edit:
I solved this issue by realizing that the resolves are invoked in the same order the schema is nested. So by writing separate resolvers for each type and passing the information in parent argument, things worked.

Extended Joi not implementing custom operators

I have issues extending joi class with custom operators. I want to validate mongodb Ids, but the extended object throws following error:
error: uncaughtException: JoiObj.string(...).objectId is not a function
TypeError: JoiObj.string(...).objectId is not a function
Code is following:
import Joi from 'joi';
import * as mongodb from 'mongodb';
interface ExtendedStringSchema extends Joi.StringSchema {
objectId(): this;
}
interface ExtendedJoi extends Joi.Root {
string(): ExtendedStringSchema;
}
const JoiObj: ExtendedJoi = Joi.extend({
base: Joi.string(),
type: 'objectId',
messages: {
'objectId.invalid': '"{{#label}}" must be a valid mongo id'
},
validate(value, helpers) {
if (!mongodb.ObjectId.isValid(value)) {
return helpers.error('objectId.invalid');
}
return value;
}
});
const objIdSchema = JoiObj.object({
id: JoiObj.string().objectId()
});
I found 2 examples:
https://github.com/sideway/joi/issues/2357
How to extend a module from npm using TypeScript?
however they use different properties than what is described in TS definition file and thus does not work.
You want to extend the Joi.string() base. Keep in mind, that you can't validate new mongodb.ObjectID() because it is of type object. You extended Joi.string() and this checks first if your value is of type string. And it will stop validating if it isn't. You can only validate new mongodb.ObjectID().toHexString() which looks like: "5f91a1449b13e3010c5548a2".
This answers is using joi 17.2.1 and mongodb 3.6.2
import Joi from 'joi';
import * as mongodb from 'mongodb';
interface ExtendedStringSchema extends Joi.StringSchema {
objectId(): this;
}
interface ExtendedJoi extends Joi.Root {
string(): ExtendedStringSchema;
}
const stringObjectExtension: Joi.Extension = {
type: 'string',
base: Joi.string(),
messages: {
'string.objectId': '{{#label}} must be a valid mongo id'
},
rules: {
objectId: {
validate: (value: any, helpers) => {
if (!mongodb.ObjectId.isValid(value)) {
return helpers.error('string.objectId')
}
return value;
}
}
}
};
// create extended Joi
const JoiObj: ExtendedJoi = Joi.extend(stringObjectExtension);
// create new mongodb id
const id = new mongodb.ObjectID();
const objIdSchema = JoiObj.object({
id: JoiObj.string().objectId()
});
// will fail because it won't pass the Joi.string() validation
const data1 = {
id: id
};
console.log(objIdSchema.validate(data1).error);
// will succeed
const data2 = {
id: id.toHexString()
};
console.log(objIdSchema.validate(data2).error);
I also had this problem. I solved it with this package.
https://www.npmjs.com/package/joi-oid
const Joi = require('joi-oid')
const schema = Joi.object({
id: Joi.objectId(),
name: Joi.string(),
age: Joi.number().min(18),
})
Good luck :)

Object as input variable in mutation: GraphQL - Apollo - React

I have a React client-side project and a Node.js/GraphQL api in two separate repo's.
In my React app, I want to pass an object as variable type into my mutation. Here's how my mutation looks like:
export const CREATE_SPEAKER = gql`
input Expertise {
title: String!
domain: String!
}
mutation CreateSpeaker(
$name: String!
$age: String!
$nationality: String!
$avatar: String!
$expertise: Expertise!
) {
createSpeaker(
speakerInput: {
name: $name
age: $age
nationality: $nationality
avatar: $avatar
expertise: $expertise
}
) {
name
age
nationality
avatar
expertise {
title
domain
}
}
}
`;
In my Node.js project I have the following schema:
input SpeakerInput {
name: String!
age: String!
expertise: ExpertiseInput!
nationality: String!
avatar: String
}
input ExpertiseInput {
title: String!
domain: String!
}
And my resolver:
createSpeaker: async args => {
const { name, age, nationality, avatar, expertise } = args.speakerInput;
const newSpeaker = new Speaker({
name,
age,
nationality,
avatar,
expertise: {
title: expertise.title,
domain: expertise.domain
}
});
try {
return await newSpeaker.save();
} catch (error) {
throw ("Failed to create speaker:: ", error);
}
}
But I'm getting the following error when trying to create the speaker:
Uncaught (in promise) Invariant Violation: Schema type definitions not
allowed in queries. Found: "InputObjectTypeDefinition"
Any suggestions/ideas how to do this?
You can't define additional types when sending requests to a GraphQL service and you don't need to -- just use the types you've already defined on the server (in this case ExpertiseInput:
$expertise: ExpertiseInput!
However, there's no need to use this many variables in the first place:
mutation CreateSpeaker($input: SpeakerInput!) {
createSpeaker(speakerInput: $input) {
name
age
nationality
avatar
expertise {
title
domain
}
}
}

How to get GraphQL syntax features in my .js file on VSCode? [duplicate]

I Want to syntax highlight the code inside typeDef. It is possible?
There is a extension for this? Or I have to code the typeDef other way?
export const typeDef = `
type User {
_id: ID!
email: String!
password: String
createdEvents: [Event!]
}
type AuthData {
userId: ID!
token: String!
tokenExpiration: Int!
}
input UserInput {
email: String!
password: String!
}
`;
Use String.raw to trick VSCode into Syntax Highlighting GraphQL. It works for other languages as well.
export const gql = String.raw
export const typeDef = gql`
type User {
_id: ID!
email: String!
password: String
createdEvents: [Event!]
}
type AuthData {
userId: ID!
token: String!
tokenExpiration: Int!
}
input UserInput {
email: String!
password: String!
}
`
Assuming you're using the right extension, you need to use the gql tag from graphql-tag.
const gql = require('graphql-tag')
const typeDefs = gql`
type User { ... }
`
The tag parses the provided string and returns a DocumentNode object, which is what should be passed to makeExecutableSchema or the ApolloServer constructor. On the client side, the queries used by ApolloClient are also expected to be DocumentNode objects and should be wrapped the same way.
The extension is able to detect usage of the tag and apply syntax highlighting accordingly.

Mongoose MissingSchemaError on a .populate even though the schema is included

I have dug all I can and every "solution" I find is either a mistyped schema name or a missing / misordered require, which I'm quite certain is not what I'm running into.
I am using Typescript 2.6.2, Node 8.9.4, and Mongoose 5.0.2. I have two models. One is for Accounts and one for Organizations. When I run a findOne with a populate against the Accounts model, I get: 'MissingSchemaError: Schema hasn't been registered for model "Organization".'
/src/models/Organization.model.ts
import * as mongoose from 'mongoose';
export type OrganizationModel = mongoose.Document & {
name: string,
suborganizations: [OrganizationModel]
};
const OrganizationSchema = new mongoose.Schema({
name: {type String, required: true},
suborganizations: [
{type: mongoose.Schema.Types.ObjectId, ref: 'Organization'}
]
});
const Organization = mongoose.model<OrganizationModel>('Organization', OrganizationSchema);
export default Organization;
/src/models/Account.model.ts
import * as mongoose from 'mongoose';
import { default as Organization, OrganizationModel } from './Organization.model';
export type AccountModel = mongoose.Document & {
username: string,
organization: OrganizationModel
};
const Account = new mongoose.Schema({
username: {type: String, required: true},
organization: {type: mongoose.Schema.Types.ObjectId, ref: 'Organization'}
});
const Account = mongoose.model<AccountModel>('Account', AccountSchema);
export default Account;
/src/controller/account.ts
import * as mongoose from 'mongoose';
// I've tried without and with the following import
import { default as Organization, OrganizationModel } from '../models/Organization.model';
import { default as Account, AccountModel } from '../models/Account.model';
const AccountManager = {
getAccount: async ({ username }) => {
// Here is the troubled line
const account = await Account.findOne({ username }).populate('organization organization.suborganizations');
return account;
}
};
export default AccountManager
I had the same problem and i guess this happened because your unused import was removed by compiler.
Try import './Organization.model';

Categories