How to create custom graphql scalars on Nestjs? Graphql Scalars - graphql

I am implementing a framework using Nestjs on Apollo Server using GraphQL and I would like to use some custom GraphQL scalars. I found this site, https://www.graphql-scalars.dev/docs/quick-start, which is helpful for importing custom scalars without actually implementing them as written on https://docs.nestjs.com/graphql/scalars#create-a-custom-scalar. To be specific, I would like to use BigInt, Time, and URL.
From the docs on the quick start page, I am uncertain where the code belongs at. Should I code this at app.module.ts?
// or import specific typeDefs only with ES6 Import
import { ScalarNameTypeDefinition } from 'graphql-scalars';
// or import specific typeDefs only with CommonJS
const { ScalarNameTypeDefinition } = require('graphql-scalars');
// or import all typeDefs once with ES6 Import
import { typeDefs as scalarTypeDefs } from 'graphql-scalars';
// or import all typeDefs once with CommonJS
const { typeDefs: scalarTypeDefs } = require('graphql-scalars');
const typeDefs = [
...scalarTypeDefs,
// other typeDefs
];
// or
const typeDefs = [
ScalarNameTypeDefinition,
// other typeDefs
];
my current GraphQLModule:
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
typePaths: ['./**/**/**/*.graphql'],
definitions: {
path: join(process.cwd(), 'src/graphql.ts'),
outputAs: 'class',
},
}),
How about the resolver map? Where should the code belong at? assets.resolver.ts? I also don't understand where this code belongs to?
In short, how to use graphql-scalars package in the Nestjs framework on Apollo Server? Is there any open-source GitHub repository to look into?

Have a look here NestJs Import a custom scalar
This is how my app.module.ts looks:
import { BigIntResolver, DateResolver, DateTimeResolver } from 'graphql-scalars';
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
typePaths: ['./**/*.graphql'],
definitions: {
path: join(process.cwd(), 'src/graphql/graphql-types.ts'),
customScalarTypeMapping: {
BigInt: 'bigint',
DateTime: 'Date',
},
},
resolvers: {
BigInt: BigIntResolver,
Date: DateResolver,
DateTime: DateTimeResolver,
},
playground: true,
debug: true,
}),
In my .graphql file I can then use those types:
scalar BigInt
scalar Date
scalar DateTime
input WorkperiodContent {
editedAt: DateTime
startDate: Date
endDate: Date
}
After I did this I could successfully run queries on the GraphQL Playground using those new scalars.
You don't even need to create your own custom scalar. You can just import the three that you need and you are good to go.

Related

GraphQL Shield with Yoga and Modules

I've been developing a site that uses Yoga Server, and Modules, and looking at using Shield for security.
However no matter what I seem to do, I can't get Shield to integrate with Yoga, and Modules.
Here is my code (note I have stripped parts out for ease of understanding):
index.ts
import { useGraphQLModules as GraphQLModules } from '#envelop/graphql-modules';
import { createYoga } from 'graphql-yoga';
import type { NextApiRequest, NextApiResponse } from 'next';
import application from './schema';
export default createYoga<{req: NextApiRequest; res: NextApiResponse;}>({
plugins: [GraphQLModules(application)],`
});
schema.ts
import { createApplication } from 'graphql-modules';
import accounting from './accounting';
import users from './users';
const application = createApplication({
modules: [
accounting,
users
],
});
export default application;
accounting.ts
import { createModule, gql } from 'graphql-modules';
export default createModule({
id: 'accounting',
dirname: __dirname,
typeDefs: [
gql`
type Query {
fees: [Fee]
}
type Fee {
id: UUID!
feeName: String
dueDate: Date
amount: Float
description: String
createdDateTime: DateTime
emailSentDateTime: DateTime
}
`,
],
resolvers: {
Query: { ... }
},
});
My code all works fine, but I can't for the life of me get Shield to work when I use Modules...

Graphql upload file shows as empty object

when I'm trying to upload file via Graphql mutation (apollo-angular) with parameter $file: Upload!, the variable value is shown as an empty object in developer tools:
But I'm checking the value of the variable right before the request and the value is correct, it is in fact a File, not an empty object.
import gql from 'graphql-tag';
import { Apollo } from 'apollo-angular';
this.apollo.mutate(
{
mutation: gql`
${uploadMutation}
`,
variables: {
companyId,
file <-- Here the value is correct, it is a File
},
context: {
hasUpload: true,
useMultipart: true
}
},
'upload'
);
Code is simplified for the sake of this example.
Is it just developer tools issue, am I doing something wrong or what is the problem please?
You have to config the Apollo client with this:
import { createUploadLink } from 'apollo-upload-client'
const link = createUploadLink(/* Options */)
Resource: https://github.com/jaydenseric/apollo-upload-client/issues/89#issuecomment-384923295

Split the graphql resolvers file into seperatefiles

I'm working with GraphQL and have a resolvers.js file that looks like this:
const User = require("../models/User");
const Post = require("../models/Post");
module.exports = {
Query: {
async users(){...},
async user(){...},
async posts(){...},
async post(){...},
},
User: {...},
Post: {...},
Mutation: {
createUser(){...},
login(){...},
createPost(){...},
},
}
But if I have more models, queries and mutations the file is gonna be very long. How can I split this into seperate files? One for user queries and mutations, one for posts and so. Or is that not possible? Maybe there's a way to combine this with the schema.js file? So that I can split the schema too and put schema/resolver from User into a file. I'm still a beginner in coding.
I found a way to do it very easy actually. In the schema.js I can use lodash merge to combine multiple resolver files and for the typedefs I just use an array. This way I can split everything into seperate files.
const { merge } = require("lodash");
module.exports = makeExecutableSchema({
typeDefs: [typeDefs, userTypeDefs],
resolvers: merge(resolvers, userResolvers)
});
Just in case somebody is looking for an answer in 2020,
I had the similar issue,
and I tried to adapt the method mentioned,
but found an easier way to solve the problem.
I used graphql-tools's mergeResolvers to solve the issue - https://www.graphql-tools.com/docs/merge-resolvers/
Example code would be like this
const { mergeResolvers } = require('#graphql-tools/merge');
const clientResolver = require('./clientResolver');
const productResolver = require('./productResolver');
const resolvers = [
clientResolver,
productResolver,
];
module.exports mergeResolvers(resolvers);
The lodash merge would not differentiate Query and Mutation,
thus throwing an error in my case.
Here is how I did it. Do note that this is in Typescript.
You would define your resolvers in separate files, such as this:
import { DateBidListResolvers } from "../../types/generated";
export const DateBidList: DateBidListResolvers.Type = {
...DateBidListResolvers.defaultResolvers,
list: (_, __) => { // This is an example resolver of Type DateBidList
throw new Error("Resolver not implemented");
}
};
Then you would aggregate them together in a single file like this:
import { Resolvers } from "../../types/generated";
import { Query } from "./Query";
import { User } from "./User";
import { DateBid } from "./DateBid";
import { DateItem } from "./DateItem";
import { Match } from "./Match";
import { Mutation } from "./Mutation";
import { Subscription } from "./Subscription";
import { DateBidList } from "./DateBidList";
import { DateList } from "./DateList";
import { Following } from "./Following";
import { MatchList } from "./MatchList";
import { Message } from "./Message";
import { MessageItem } from "./MessageItem";
import { Queue } from "./Queue";
export const resolvers: Resolvers = {
DateBid,
DateBidList,
DateItem,
DateList,
Following,
Match,
MatchList,
Message,
MessageItem,
Mutation,
Query,
Queue,
Subscription,
User
};
You could then import that resolvers export into your configuration setup:
import { resolvers } from './resolvers/index';
// ... other imports here
export const server = {
typeDefs,
resolvers,
playground,
context,
dataSources,
};
export default new ApolloServer(server);
I hope this helps!

Switching from graphql-js to native graphql schemas?

Currently trying to switch from graphql-js to literal GraphQL types/schemas, I'd like to know if anyone has had any experience with this.
Let's take this really simple one :
const Person = new GraphQLObjectType({
name: 'Person',
fields: () => ({
name: {
type: GraphQLString,
description: 'Person name',
},
}),
});
I'd like to switch to the native GraphQL schema syntax i.e
type Person {
# Person name
name: String
}
However this would have to be incremental, and given the use of graphql-js, the best solution for now would be to parse GraphQL template literals to GraphQLObjectType (or any other type for that matter). Does anyone have experience doing this, I cannot seem to find any library for it unfortunately.
import { printType } from 'graphql';
printType(Person)
output:
type Person {
"""Person name"""
name: String
}
Here is the demo:
import { expect } from 'chai';
import { printType, printSchema, buildSchema, GraphQLSchema } from 'graphql';
import { logger } from '../util';
import { Person } from './';
describe('test suites', () => {
it('convert constructor types to string types', () => {
const stringTypeDefs = printType(Person).replace(/\s/g, '');
logger.info(printType(Person));
const expectValue = `
type Person {
"""Person name"""
name: String
}
`.replace(/\s/g, '');
expect(stringTypeDefs).to.be.equal(expectValue);
});
it('buildSchema', () => {
const stringTypeDefs = printType(Person);
const schema = buildSchema(stringTypeDefs);
expect(schema).to.be.an.instanceof(GraphQLSchema);
});
it('printSchema', () => {
const stringTypeDefs = printType(Person);
const schema = printSchema(buildSchema(stringTypeDefs));
logger.info(schema);
const expectValue = `
type Person {
"""Person name"""
name: String
}
`.replace(/\s/g, '');
expect(schema.replace(/\s/g, '')).to.be.eql(expectValue);
});
});
source code:
https://github.com/mrdulin/nodejs-graphql/blob/master/src/convert-constructor-types-to-string-types/index.spec.ts
You can use graphql-cli to extract a native graphql schema from a graphql server. All you need to do is..
Download the tool | npm i -g graphql-cli
Run graphql init in the directory of your project to
create .graphqlconfig file
Start your graphql server
Run graphql get-schema and this will generate a your schema in native graphql
SAMPLE .graphqlconfig
{
"projects": {
"my_sample_project": {
"schemaPath": "schema.graphql",
"extensions": {
"endpoints": {
"local": "http://localhost:8080/graphql"
}
}
}
}
}
We leverage the auto-generation of graphql schema/queries/mutations for our CI workflows.

How to pass existing GraphQLSchema object to Apollo makeExecutableSchema function

How can I pass an existing GraphQLSchema object like graphql-iso-date to the makeExecutableSchema function, to use it along with my string-defined types and resolver functions? Say in following type definition I want the date property to be GraphQLDate from the mentioned package.
import { GraphQLDate, GraphQLTime, GraphQLDateTime } from 'graphql-iso-date';
let typeDefs = [];
typeDefs.push(`
type MyType {
date: Date
}
`);
let resolvers = {
Query: () => { /* ... */ },
};
makeExecutableSchema({ typeDefs, resolvers });
It turns out the resolvers map, which is passed to the makeExecutableSchema does accept the GraphQLScalarType and the Date type is scalar. We still have to add the types into the typeDefs manually though...
typeDefs.push('scalar Date');
and
resolvers.Date = GraphQLDate;
So I've created a module of external scalars in my project and am doing
import externalTypes from './externalTypes';
import printType from 'graphql';
// Define my typeDefs and resolvers here
for (let externalType of externalTypes) {
let { name } = externalType;
typeDefs.push(printType(externalType));
resolvers[name] = externalType;
}
makeExecutableSchema({ typeDefs, resolvers });
I figured it by trial/failure and only then found it in the docs, thus posting. Also I still don't know how would I add a non-scalar type this way (well other than manually writing it's type definition).
Also the printType function, which prints schema definition from type object passed, becomes handy here (see this question for more detail).

Resources