I defined the below query in my GraphQL schema
type Query {
getShowByName(showSchemaName: String!): String!
}
with the corresponding resolver function as shown below
const resolvers = ()=>({
Query:{
getShowByName: function(args){
console.log("out1"+args);
console.log("out3"+args.showSchemaName);
return "hardcoded return from getShowByName";
},
},
});
In the graphql playground, i provided the below inputs
{
getShowByName(showSchemaName:"input to getShowByName")
}
The graphql playground provides hardcoded return from getShowByName as output in the playground page but in the terminal i get args as undefined. Hence, i am unable to parse the input fed in from the graphql playground.
Please help me in understanding where i am going wrong and how i can rectify the issue.
The first argument in the resolver is an object that contains the result of parent resolver. In your case, the root level Query will receive undefined. Instead you should extract the args from the second argument.
const resolvers = ()=> ({
Query: {
getShowByName: function(_, args) {
console.log("out1" + args);
console.log("out3" + args.showSchemaName);
return "hardcoded return from getShowByName";
},
},
});
Related
Am playing around with Typescript, Mongoose, NodeJS and Express, using the sample MongoDB data based on restaurants.
Am attempting to create simple CRUD operations, with the intent on returning All restaurants, then a specific restaurant and eventually filter/sort etc.
See the function below:
const fetchRestaurants = async (request: Request, response: Response): Promise<RestaurantInterface[]> => {
try {
const restaurants: RestaurantInterface[] = await RestaurantModel.find();
response.status(200).json({
restaurants
});
} catch (error) {
throw error;
}
}
If I remove this line Promise<RestaurantInteface[] (or switch strict: false) I have no issues otherwise it alternates between.
A function whose declared type is neither 'void' nor 'any' must return a value.ts(2355) and if I attempt to return the response Type 'Response<any, Record<string, any>>' is missing the following properties from type 'RestaurantInterface[]': length, pop, push, concat, and 26 more.ts(2740)
Do I have basically loosen up the ts compiler or is there something I've misunderstood?
// in another file
export interface RestaurantInterface {
name: string,
borough: string,
cuisine: string,
restaurant_id: string
address: Address,
grades: Grades[],
};
Well it is expected, because the function actually returns void. When you write function name(): Promise<...> it means that you should return something. Whereas you are just calling the json method of the Express. So it is expected.
I've made simple CRUD app with React and Apollo client on NestJS server with GraphQL API.
I have this simple Mutations:
schema.gql:
type Mutation {
createUser(input: CreateUserInput!): User! // CreateUserInput type you can see in user.input.ts below
updateUser(id: ID!, input: UpdateUserInput!): User!
deleteUser(id: ID!): User!
}
user.input.ts:
import { InputType, Field } from "#nestjs/graphql";
import { EmailScalar } from "../email.scalar-type";
#InputType()
export class CreateUserInput {
// EmailScalar is a custom Scalar GraphQL Type that i took from the internet and it worked well
#Field(() => EmailScalar)
readonly email: string;
#Field()
readonly name: string;
}
"EmailScalar" type checks if "email" input has *#*.* format basically
And when i make createUser Query to GraphQL API like this:
It cannot pass validation
(because Email type works fine)
But when Query sent from client - it passes validation:
NestJS server log (from code below)
users.resolver.ts:
#Mutation(() => User)
async createUser(#Args('input') input: CreateUserInput) { // Type from user.input.ts
Logger.log(input); // log from screenshot, so if it's here it passed validation
return this.usersService.create(input); // usersService makes requests to MongoDB
}
And it gets into MongoDB
Here is client side part:
App.tsx:
...
// CreateUserInput class is not imported to App.tsx (it is at server part) but it seems to be fine with it
const ADD_USER = gql`
mutation AddMutation($input: CreateUserInput!) {
createUser(input: $input) {
id
name
email
}
}
`
function App(props: any) {
const { loading, error, data } = useQuery(GET_USERS);
const [addUser] = useMutation(
ADD_USER,
{
update: (cache: any, { data: { createUser } }: any) => {
const { users } = cache.readQuery({ query: GET_USERS });
cache.writeQuery({
query: GET_USERS,
data: {
users: [createUser, ...users],
},
})
}
}
);
...
if (loading) return <p>Loading...</p>;
if (error) return <p>Error :(</p>;
return <UserTable users={data.users} addUser={addUser} updateUser={updateUser} deleteUser={deleteUser} />;
}
Can someone please explain to me, how does client Query passes validation and what have i done wrong?
Even two empty strings can pass through.
Never worked with NestJS, Apollo, React or GraphQL before, so I'm kinda lost.
For full code:
https://github.com/N238635/nest-react-crud-test
This is how your custom scalar's methods are defined:
parseValue(value: string): string {
return value;
}
serialize(value: string): string {
return value;
}
parseLiteral(ast: ValueNode): string {
if (ast.kind !== Kind.STRING) {
throw new GraphQLError('Query error: Can only parse strings got a: ' + ast.kind, [ast]);
}
// Regex taken from: http://stackoverflow.com/a/46181/761555
var re = /^([\w-]+(?:\.[\w-]+)*)#((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/i;
if (!re.test(ast.value)) {
throw new GraphQLError('Query error: Not a valid Email', [ast]);
}
return ast.value;
}
parseLiteral is called when parsing literal values inside the query (i.e. literal strings wrapped in double quotes). parseValue is called when parsing variable values. When your client sends the query, it sends the value as a variable, not as a literal value. So parseValue is used instead of parseLiteral. But your parseValue does not do any kind of validation -- you just return the value as-is. You need to implement the validation logic in both methods.
It would also be a good idea to implement the serialize method so that your scalar can be used for both input and response validation.
I define a schema like this:
const query = new GraphQLObjectType({
name: 'Query',
fields: {
quote: {
type: queryType,
args: {
id: { type: QueryID }
},
},
},
});
const schema = new GraphQLSchema({
query,
});
The QueryID is a customised scalar type.
const QueryID = new GraphQLScalarType({
name: 'QueryID',
description: 'query id field',
serialize(dt) {
// value sent to the client
return dt;
},
parseLiteral(ast) {
if (ast.kind === 'IntValue') {
return Number(ast.value);
}
return null;
},
parseValue(v) {
// value from the client
return v;
},
});
client query
query {
quote(queryType: 1)
}
I found that the parseValue method is not called when clients send query to my server. I can see parseLiteral is called correctly.
In most of the document I can find, they use gql to define schema and they need to put scalar QueryID in their schema definition. But in my case, I am using GraphQLSchema object for schema. Is this the root cause of that? If yes, what is the best way to make it works? I don't want to switch to gql format because I need to construct my schema at runtime.
serialize is only called when sending the scalar back to the client in the response. The value it receives as a parameter is the value returned in the resolver (or if the resolver returned a Promise, the value the Promise resolved to).
parseLiteral is only called when parsing a literal value in a query. Literal values include strings ("foo"), numbers (42), booleans (true) and null. The value the method receives as a parameter is the AST representation of this literal value.
parseValue is only called when parsing a variable value in a query. In this case, the method receives as a parameter the relevant JSON value from the variables object submitted along with the query.
So, assuming a schema like this:
type Query {
someField(someArg: CustomScalar): String
someOtherField: CustomScalar
}
serialize:
query {
someOtherField: CustomScalar
}
parseLiteral:
query {
someField(someArg: "something")
}
parseValue:
query ($myVariable: CustomScalar) {
someField(someArg: $myVariable)
}
I'm really new to Graphql (just yesterday actually). I am "playing" around and try the various tools of the ecosystem (apollo-server, graphql.js ...ect).
For the sake of experimenting, I am trying to call a query from within nodejs (and not from a client in the browser, such as a react application)
First of all this is my simple schema along with resolvers:
export const mySchema = gql`
type User {
id: ID!
name:
surname: String
}
# root query has been defined in another file
extend type Query {
users: [User]
test: [User]
}
`
export const myResolvers = {
users: () => [ __array_of_users__ ],
test: () => /* this is where I would like to re-invoke the 'users query'
}
Using the makeExecutableSchema function, I create a schema object with my types and my resolvers and I export this schema into the apollo server application. Every thing works fine so far.
Now following this stackoverflow suggested solution, I created a helper function which should allow me to invoke a query defined in my schema as following:
import { graphql } from "graphql";
import { schema } from "./my-schema";
export const execute = str => {
return graphql(schema, str );
};
With this helper function, my resolvers become:
import { gql } from "apollo-server-express";
import { execute } from '__path_to_helper_function__';
export const myResolvers = {
users: () => [ __array_of_users__ ],
test: () => execute( gql`
query users {
name
}
`)
}
But in the playground, when I try the query:
{
test {
name
}
}
I get the following error:
I don't even know if what I am trying to do (to call a query from within node) can be done. Any suggestion will be greatly appreciated.
Thnaks
graphql-tag takes a string and parses it into a DocumentNode object. This is effectively the same as passing a String to the parse function. Some functions exported by the graphql module, like execute, expect to be passed in a DocumentNode object -- the graphql function does not. It should be passed just a plain String as the request, as you can see from the signature:
graphql(
schema: GraphQLSchema,
requestString: string,
rootValue?: ?any,
contextValue?: ?any,
variableValues?: ?{[key: string]: any},
operationName?: ?string
): Promise<GraphQLResult>
So, just drop the gql tag. You can see an (incomplete) API reference here.
I can't get GraphQL to recognize the JSON scalar type.
I followed the [apollo docs] (http://dev.apollodata.com/tools/graphql-tools/scalars.html#Using-a-package) to define a JSON GraphQL scalar type for my schema:
Schema:
const SchemaDefinition = `
scalar JSON
schema {
query: Query
mutation: Mutation
}
`
export default [
SchemaDefinition,
Query,
Mutation,
...
]
Test type:
const Test = `
type Test {
bodyJson: JSON
}`
Resolver:
import GraphQLJSON from 'graphql-type-json'
const QueryResolver = {
Query: {
viewer(root, args, ctx) {
return User.query()
.where('id', ctx.state.user)
.first()
}
}
}
const scalarJSON = {
JSON: GraphQLJSON
}
export default {
...QueryResolver,
...ViewerResolver,
...scalarJSON
...
}
I'm using PostgreSQL and the column I'm querying (body_json) is of data type jsonb.
If I test my schema through GraphiQL, when I return the value straight from the db (I use Knex to query) I get this error message from GraphQL:
Expected a value of type \"JSON\" but received: [object Object]
If I use JSON.stringify first on the returned value, I get this error:
"Expected a value of type \"JSON\" but received: {\"key\":\"test\"}"
Any suggestion as to what I might be doing wrong?
I resolved custom scalar JSON like this in resolvers
JSON: {
__serialize(value) {
return GraphQLJSON.parseValue(value);
} }
And It worked fine for me. I think it will help you
I couldn't get a custom scalar working with a text-based schema (using buildSchema('...') from the core "graphql" library), but it worked when I built the schema programmatically from scratch (using new GraphQLSchema(...)).