How can I troubleshoot an error: lib/graphql has no exported mutation - for a mutation I have defined and which appears in graphql.tsx - graphql

I'm trying to figure out what I need to do in order to have lib/graphql recognise the mutations I have made.
I have an issue.tsx (which is a form). It imports:
import {
IssueInput,
useUpdateIssueMutation,
useAllIssuesQuery,
useCreateIssueMutation,
useDeleteIssueMutation,
Issue as IssueGQLType,
} from "lib/graphql"
Other than IssueInput and Issue, I'm getting errors in my terminal that say these queries and mutations are not exported members.
However when I try to load the issue page in local host, I get an error that says:
error - GraphQLError [Object]: Syntax Error: Expected Name, found
. It points to the line where Issue is imported.
I made all of these queries and mutations in my resolver as follows:
import { Arg, Mutation, Query, Resolver } from "type-graphql"
import { Issue } from "./issue.model"
import { IssueService } from "./issue.service"
import { IssueInput } from "./inputs/create.input"
import { Inject, Service } from "typedi"
import { UseAuth } from "../shared/middleware/UseAuth"
import { Role } from "#generated"
#Service()
#Resolver(() => Issue)
export default class IssueResolver {
#Inject(() => IssueService)
issueService: IssueService
#Query(() => [Issue])
async allIssues() {
return await this.issueService.getAllIssues()
}
#Query(() => [Issue])
async futureRiskIssues() {
return await this.issueService.getFutureRiskIssues()
}
#Query(() => Issue)
async issue(#Arg("id") id: string) {
return await this.issueService.getIssue(id)
}
#UseAuth([Role.ADMIN])
#Mutation(() => Issue)
async createIssue(#Arg("data") data: IssueInput) {
return await this.issueService.createIssue(data)
}
#UseAuth([Role.ADMIN])
#Mutation(() => Issue)
async deleteIssue(#Arg("id") id: string) {
return await this.issueService.deleteIssue(id)
}
#UseAuth([Role.ADMIN])
#Mutation(() => Issue)
async updateIssue(#Arg("id") id: string, #Arg("data") data: IssueInput) {
return await this.issueService.updateIssue(id, data)
}
}
I can also see from my graphql.tsx file, that these functions are recognised as follows:
export type Mutation = {
__typename?: 'Mutation';
createIssue: Issue;
createUser: User;
deleteIssue: Issue;
destroyAccount: Scalars['Boolean'];
forgotPassword: Scalars['Boolean'];
getBulkSignedS3UrlForPut?: Maybe<Array<SignedResponse>>;
getSignedS3UrlForPut?: Maybe<SignedResponse>;
login: AuthResponse;
register: AuthResponse;
resetPassword: Scalars['Boolean'];
updateIssue: Issue;
updateMe: User;
};
export type MutationCreateUserArgs = {
data: UserCreateInput;
};
export type MutationDeleteIssueArgs = {
id: Scalars['String'];
};
export type MutationUpdateIssueArgs = {
data: IssueInput;
id: Scalars['String'];
};
I have run the codegen several times and can't think of anything else to try to force these mutations and queries to be recognised. Can anyone see a way to trouble shoot this?
My codegen.yml has:
schema: http://localhost:5555/graphql
documents:
- "src/components/**/*.{ts,tsx}"
- "src/lib/**/*.{ts,tsx}"
- "src/pages/**/*.{ts,tsx}"
overwrite: true
generates:
src/lib/graphql.tsx:
config:
withMutationFn: false
addDocBlocks: false
scalars:
DateTime: string
plugins:
- add:
content: "/* eslint-disable */"
- typescript
- typescript-operations
- typescript-react-apollo
When I look at the mutations available on the authentication objects (that are provided with the [boilerplate app][1] that I am trying to use), I can see that there are mutations and queries that are differently represented in the lib/graphql file. I just can't figure out how to force the ones I write to be included in this way:
export function useLoginMutation(baseOptions?: Apollo.MutationHookOptions<LoginMutation, LoginMutationVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useMutation<LoginMutation, LoginMutationVariables>(LoginDocument, options);
}
Instead, I get all of these things, but none of them look like the above and I can't figure out which one to import into my front end form so that I can make an entry in the database. None of them look like the queries or mutations I defined in my resolver
export type IssueInput = {
description: Scalars['String'];
issueGroup: Scalars['String'];
title: Scalars['String'];
};
export type IssueListRelationFilter = {
every?: InputMaybe<IssueWhereInput>;
none?: InputMaybe<IssueWhereInput>;
some?: InputMaybe<IssueWhereInput>;
};
export type IssueRelationFilter = {
is?: InputMaybe<IssueWhereInput>;
isNot?: InputMaybe<IssueWhereInput>;
};
export type IssueWhereInput = {
AND?: InputMaybe<Array<IssueWhereInput>>;
NOT?: InputMaybe<Array<IssueWhereInput>>;
OR?: InputMaybe<Array<IssueWhereInput>>;
createdAt?: InputMaybe<DateTimeFilter>;
description?: InputMaybe<StringFilter>;
id?: InputMaybe<UuidFilter>;
issueGroup?: InputMaybe<IssueGroupRelationFilter>;
issueGroupId?: InputMaybe<UuidFilter>;
subscribers?: InputMaybe<UserIssueListRelationFilter>;
title?: InputMaybe<StringFilter>;
updatedAt?: InputMaybe<DateTimeFilter>;
};
export type IssueWhereUniqueInput = {
id?: InputMaybe<Scalars['String']>;
};
I do have this record in my graphql.tsx file:
export type Mutation = {
__typename?: 'Mutation';
createIssue: Issue;
createIssueGroup: IssueGroup;
createUser: User;
deleteIssue: Issue;
deleteIssueGroup: IssueGroup;
destroyAccount: Scalars['Boolean'];
forgotPassword: Scalars['Boolean'];
getBulkSignedS3UrlForPut?: Maybe<Array<SignedResponse>>;
getSignedS3UrlForPut?: Maybe<SignedResponse>;
login: AuthResponse;
register: AuthResponse;
resetPassword: Scalars['Boolean'];
updateIssue: Issue;
updateIssueGroup: IssueGroup;
updateMe: User;
};
but I can't say: createIssueMutation as an import in my issue.tsx where I'm trying to make a form to use to post to the database.
[1]: https://github.com/NoQuarterTeam/boilerplate
In the issue form, I get an error that says:
"resource": "/.../src/pages/issue.tsx", "owner": "typescript",
"code": "2305", "severity": 8, "message": "Module '"lib/graphql"'
has no exported member 'useCreateIssueMutation'.", "source": "ts",
"startLineNumber": 7, "startColumn": 27, "endLineNumber": 7,
"endColumn": 54 }]
and the same thing for the query

check your codegen.yml
overwrite: true
schema: "http://localhost:4000/graphql"
documents: "src/graphql/**/*.graphql"
generates:
src/generated/graphql.tsx:
plugins:
- "typescript"
- "typescript-operations"
- "typescript-react-apollo"
./graphql.schema.json:
plugins:
- "introspection"
or try something like #Resolver(Issue)

It seems like you are not generating the hooks that you are trying to import.
You can update your codegen.yml file to add the generated hooks:
schema: http://localhost:5555/graphql
documents:
- "src/components/**/*.{ts,tsx}"
- "src/lib/**/*.{ts,tsx}"
- "src/pages/**/*.{ts,tsx}"
overwrite: true
generates:
src/lib/graphql.tsx:
config:
withMutationFn: false
addDocBlocks: false
scalars:
DateTime: string
withHooks: true # <--------------------- this line
plugins:
- add:
content: "/* eslint-disable */"
- typescript
- typescript-operations
- typescript-react-apollo

Related

Vite Library Mode Build Different between Windows and Linux

This is a crosspost from my github discussion, but I wanted to see if anyone has any thoughts here.
I'm using vite for an npm package that's in "library mode." I have 2 files:
utilities.ts
export function thisGetsRemovedInBundle() {
console.log('This should be in the bundle!');
}
export function thisIsUsed() {
console.log('Used!');
return 1;
}
components/index.ts
import { thisIsUsed } from '../utilities';
export default {
Hello: thisIsUsed(),
};
vite.config.ts
import { defineConfig } from 'vite';
import vue from '#vitejs/plugin-vue';
export default defineConfig({
plugins: [vue()],
build: {
lib: {
entry: 'components/index.ts',
formats: ['es'],
},
rollupOptions: {
external: ['vue'],
input: {
index: 'components/index.ts',
utilities: 'utilities.ts',
},
output: {
dir: 'dist',
format: 'es',
entryFileNames: '[name].js',
},
},
},
});
When I build this on a Windows 10 machine, it produces the following in index.js:
function thisIsUsed() {
console.log("Used!");
return 1;
}
var index = {
Hello: thisIsUsed()
};
export { index as default };
On Ubuntu, it produces this:
import { thisIsUsed } from "./utilities.js";
var index = {
Hello: thisIsUsed()
};
export { index as default };
Notice in the Windows build, it doesn't import the function but rather adds it as if it's part of index.js. I would expect it to be like the latter because in the Windows bundle, it has duplicated code (both files have a copy of thisIsUsed).
Is there some fundamental npm or node magic that I'm missing between these builds? If so, how do I solve this issue so my build always looks like the Ubuntu build (without having to use a Linux machine or Unix shell).
I ended up reporting a bug here, and while it appears valid, it can be fixed easily by using path.resolve and __dirname when defining the path to the input files. e.g. in the vite.config.ts above, it should be:
import { defineConfig } from 'vite';
import vue from '#vitejs/plugin-vue';
import path from 'path';
export default defineConfig({
plugins: [vue()],
build: {
lib: {
entry: 'components/index.ts',
formats: ['es'],
},
rollupOptions: {
external: ['vue'],
input: {
index: path.resolve(__dirname, 'components/index.ts'),
utilities: path.resolve(__dirname, 'utilities.ts'),
},
output: {
dir: 'dist',
format: 'es',
entryFileNames: '[name].js',
},
},
},
});

TypeORM EntityMetadataNotFoundError: No metadata for \"Score\" was found

My project has 2 seperate connection datasources. One is to a readonly mysql database, the other is a postgres database.
Everything on the mysql database works fine. But I'm having issues with the postgres database.
Whenever I run the createQueryBuilder to getMany I get the following error.
EntityMetadataNotFoundError: No metadata for \"Score\" was found.
In my index.ts file where I spin up my server, I initialize the postgres db datasouce
pgDataSource.initialize();
And here is pgDataSource
import 'reflect-metadata';
import 'dotenv-safe/config';
import { DataSource } from 'typeorm';
import path from 'path';
import Score from '../entities/Score';
const pgDataSource = new DataSource({
name: 'favourites',
type: 'postgres',
url: process.env.DATABASE_URL,
logging: true,
synchronize: true,
migrations: [path.join(__dirname, './migrations/*')],
entities: [Score],
});
export const Manager = pgDataSource.manager;
export const ScoreRepository = pgDataSource.getRepository(Score);
export default pgDataSource;
At this point, everything seems fine, it creates a table in my database with the Score entity, so the connection exists and it acknowledges that there is an Entity as it creates the corresponding table.
Here is my Score entity
import { ObjectType, Field, Int } from 'type-graphql';
import { Entity, PrimaryGeneratedColumn, Column, BaseEntity } from 'typeorm';
#ObjectType()
#Entity({ database: 'pp_favourites', name: 'score' })
class Score extends BaseEntity {
#Field()
#PrimaryGeneratedColumn()
id!: number;
#Field(() => Int)
#Column()
propertyId!: number;
}
export default Score;
I have a GraphQL resolver for the mysql database and a resolver for postgres database.
However, when I run this query
await ScoreRepository.createQueryBuilder().getMany()
In a Query decorator function, I get the No metadata for \"Score\" was found. error
Although, if I run it through a useMiddleware decorator, it strangely works?
This is what my mysql resolver looks like with the query on the ScoreRespository
import { Resolver, Query } from 'type-graphql';
import Shortlist from '../entities/Shortlist';
import { ShortlistRepository } from '../datasources/ppDataSource';
import Property from '../entities/Property';
import { ScoreRepository } from '../datasources/pgDataSource';
#Resolver(Shortlist)
class ShortlistResolver {
#Query(() => [Property], { nullable: true })
async getShortlist(): Promise<Property[]> {
await ScoreRepository.createQueryBuilder().getMany()
}
}
export default ShortlistResolver;
Perhaps, also worth mentioning, as this could potentially be the issue. But in my index file where i spin up my server. I setup my ApolloServer which uses the now deprecated getConnectionfunction from typeorm
const apolloServer = new ApolloServer({
schema: await buildSchema({
resolvers: [ShortlistResolver, ScoreResolver],
validate: true,
}),
context: ({ req, res }) => ({
req,
res,
}),
plugins: [
// dataloaders with TypeormLoader in type-graphql-dataloader
ApolloServerLoaderPlugin({
typeormGetConnection: getConnection, // for use with TypeORM
}),
// use older playground
ApolloServerPluginLandingPageGraphQLPlayground({
settings: {
'request.credentials': 'include',
},
}),
],
});
I've tried
Adding #Resolver(Score) as a decorator to the ShortlistResolver
Removing the dist folder
creating a new database and new connection
Logged Manager.connection.entityMetadatas from pgDataSource which shows there are no entity's, despite being defined.
Any help would be greatly appreciated. I don't really know if the issue lies with TypeORM or GraphQL.

Vercel app with graphql-codegen endpoint error Unable to find any GraphQL type definitions for the following pointers

I load my GraphQL schema like:
const schema = loadSchemaSync('./src/graphql/server/schema/*.gql', {
loaders: [new GraphQLFileLoader()],
})
This works fine locally, however, when deploying to vercel I get the error:
Unable to find any GraphQL type definitions for the following pointers:
- ./src/graphql/server/schema/*.gql
I think this is because vercel is dropping the relevant files after build?
The problem is that you cannot use dynamic loaders in Vercel Serverless Functions.
A workaround for this problem is to use a inline GraphQL schema.
// src/graphql/schema.ts
import { gql } from "apollo-server-core";
export default gql`
type Query {
greet: String!
}
`;
// src/pages/api/graphql.ts
import { ApolloServerPluginLandingPageGraphQLPlayground } from "apollo-server-core";
import Schema from "../../graphql/schema";
const apolloServer = new ApolloServer({
typeDefs: Schema,
resolvers,
plugins: [ApolloServerPluginLandingPageGraphQLPlayground],
introspection: true,
});
If you are using tools like codegen:
// codegen.ts
import { CodegenConfig } from "#graphql-codegen/cli";
const config: CodegenConfig = {
schema: "src/graphql/schema.ts",
documents: ["./src/**/*.{ts,tsx}"],
ignoreNoDocuments: true,
generates: {
"src/graphql/types/server.ts": {
plugins: [
"#graphql-codegen/typescript",
"#graphql-codegen/typescript-resolvers",
],
},
"src/graphql/types/client/": {
preset: "client",
plugins: [],
},
},
};
export default config;

NestJS + GraphQL Federation and schema first GraphQLDefinitionsFactory

I'am trying to use the GraphQL federation with the schema first approach. Before I switch to federation, I was using ts-node and a little script to generate my typings like this :
import { GraphQLDefinitionsFactory } from '#nestjs/graphql';
import { join } from 'path';
const definitionsFactory = new GraphQLDefinitionsFactory();
definitionsFactory.generate({
typePaths: ['./src/**/*.graphql'],
path: join(process.cwd(), 'src/graphql.schema.ts'),
outputAs: 'class',
});
This was working well until I switch to the federation approach and modifying my schema adding the #Key() directive in my sites.graphql file (schema first!) :
type Site #key(fields: "siteId") {
siteId: ID!
contractId: Int
dateStart: String
siteName: String
}
type Query {
GetSite(id: ID!): Site
}
Now, when I generate my classes, I have the following error :
> ts-node src/tools/generate-typings.ts
(node:84388) UnhandledPromiseRejectionWarning: Error: Unknown directive "#key".
The #key directive does not seem to be recognized. Do I miss something?
Thank you for your help
Ok, digging in the source code of graphql-definitions.factory.ts I found an undocumented option federation.
Changing my script to :
import { GraphQLDefinitionsFactory } from '#nestjs/graphql';
import { join } from 'path';
const definitionsFactory = new GraphQLDefinitionsFactory();
definitionsFactory.generate({
typePaths: ['./src/**/*.graphql'],
path: join(process.cwd(), 'src/graphql.schema.ts'),
outputAs: 'class',
federation: true
});
And it works now.
Ps: to run the project, don't forget to eventually disable the installSubscriptionHandlers in the GraphQLModule options.
For federated subgraphs you should used GraphQLFederationDefinitionsFactory instead:
import { join } from 'path';
import { GraphQLFederationDefinitionsFactory } from '#nestjs/graphql';
const definitionsFactory = new GraphQLFederationDefinitionsFactory();
definitionsFactory.generate({
typePaths: ['./src/**/*.graphql'],
path: join(process.cwd(), 'src/graphql.ts'),
defaultScalarType: 'unknown',
customScalarTypeMapping: {
DateTime: 'Date',
},
watch: true,
});
I have created the script to generate the federation schema
import { writeFileSync, readFileSync } from 'fs';
import { buildSubgraphSchema } from '#apollo/subgraph';
import { printSchema, DocumentNode } from 'graphql';
import gql from 'graphql-tag';
async function generateSchema() {
// auto generate schema file
const subGraphschema = readFileSync('./src/schema/schema.graphql', {
encoding: 'utf8',
flag: 'r',
});
const schema: DocumentNode = gql(subGraphschema);
const generatedSchema = buildSubgraphSchema({
typeDefs: schema,
});
writeFileSync(
'./public/schema.graphql',
printSchema(generatedSchema),
'utf8',
);
console.log(`Generated GraphQL schema:\n\n${printSchema(generatedSchema)}`);
}
generateSchema();

How to setup a multipage project with fuse-box?

I'm not able to import files in my fusebox project and keep seeing the following error:
GET http://localhost:4444/hello.ts 404 (Not Found)
I've set my import statements correctly and don't understand what's causing the error. My project structure looks like this:
The config file:
Sparky.task("config", () => {
fuse = FuseBox.init({
homeDir: "src",
output: "dist/$name.js",
hash: isProduction,
sourceMaps: !isProduction,
plugins: [
[SassPlugin(), CSSPlugin()],
CSSPlugin(),
WebIndexPlugin({
target: "index.html",
template: "src/index.html"
}),
WebIndexPlugin({
target: "login.html",
template: "src/login.html"
}),
isProduction && UglifyJSPlugin()
],
});
// vendor should come first
vendor = fuse.bundle("vendor")
.instructions("~ js/indexView.ts");
// out main bundle
app = fuse.bundle("app")
.instructions(`!> js/indexView.ts`);
if (!isProduction) {
fuse.dev();
}
});
Hello.ts:
export function hello(name: string) {
return `Hello ${name}`;
}
IndexView.ts:
import {hello} from "./hello.ts";
const message: string = `This is the index page`;
console.log(hello(message));
You can also find this project here on Github.

Resources