Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot get PrismaTypes in the schema when running the build with npm run start (Remix.run, pothos, prisma and graphql-yoga) #1289

Open
LorenzoJokhan opened this issue Sep 4, 2024 · 1 comment

Comments

@LorenzoJokhan
Copy link

LorenzoJokhan commented Sep 4, 2024

Hello,

I am having an issue with using the generated PrismaTypes in my graphql server instance using graphql-yoga.
When i am running the remix application with npm run dev, then the types are set and i see my scheme:

{ schema: GraphQLSchema { ...TheUsualFields, Organisation: [GraphQLObjectType], Teacher: [GraphQLObjectType], } }

However using npm run build and npm run start results in the scheme without all my types:
{ schema: GraphQLSchema { ...otherFields, _typeMap: [Object: null prototype] { Boolean: [GraphQLScalarType], DateTime: [GraphQLScalarType], Float: [GraphQLScalarType], ID: [GraphQLScalarType], Int: [GraphQLScalarType], JSONObject: [GraphQLScalarType], Mutation: [GraphQLObjectType], Query: [GraphQLObjectType], String: [GraphQLScalarType], __Directive: [GraphQLObjectType], __DirectiveLocation: [GraphQLEnumType], __EnumValue: [GraphQLObjectType], __Field: [GraphQLObjectType], __InputValue: [GraphQLObjectType], __Schema: [GraphQLObjectType], __Type: [GraphQLObjectType], __TypeKind: [GraphQLEnumType] }, _subTypeMap: [Object: null prototype] {}, _implementationsMap: [Object: null prototype] {} } }

[ tsconfig.json ]
{
"include": [
"**/*.d.ts",
"**/*.ts",
"**/*.tsx",
"**/.server/**/*.ts",
"**/.server/**/*.d.ts",
"**/.server/**/*.tsx",
"**/.client/**/*.d.ts",
"**/.client/**/*.tsx"
],
"exclude": ["node_modules"],
"compilerOptions": {
"lib": ["DOM", "DOM.Iterable", "ES2022"],
"types": ["@remix-run/node", "vite/client"],
"isolatedModules": true,
"esModuleInterop": true,
"jsx": "react-jsx",
"module": "ESNext",
"moduleResolution": "Bundler",
"resolveJsonModule": true,
"target": "ES2022",
"strict": true,
"allowJs": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"baseUrl": ".",
"paths": {
"~/": ["./app/"]
},

// Vite takes care of building everything, not tsc.
"noEmit": true

}
}

[vite.config]
import { sentryVitePlugin } from '@sentry/vite-plugin';
import { vitePlugin as remix } from '@remix-run/dev';
import { defineConfig } from 'vite';
import { installGlobals } from '@remix-run/node';
import tsconfigPaths from 'vite-tsconfig-paths';
import { vercelPreset } from '@vercel/remix/vite';

installGlobals();

export default defineConfig(({}) => {
const isProduction = process.env.NODE_ENV === 'production';

return {
plugins: [
remix({
presets: [vercelPreset()],
future: {
v3_fetcherPersist: true,
v3_relativeSplatPath: true,
v3_throwAbortReason: true,
},
}),
tsconfigPaths(),
sentryVitePlugin({
org: 'savvycodes',
project: 'lessenplan-lvs',
}),
],

build: {
  sourcemap: !isProduction,
},

};
});

[Graphql server]
// graphql-server.js
import { createYoga } from 'graphql-yoga';

import { schema } from './app/services.server/graphql/schema';
export const config = {
api: {
// Disable body parsing (required for file uploads)
bodyParser: false,
},
};

const plugins = [];

if (process.env.NODE_ENV === 'production') {
// Disable introspection in production build
// @todo: persisted-operations or graphql-armor would be a better solution.
/* eslint-disable react-hooks/rules-of-hooks */
// plugins.push(useDisableIntrospection());
}

console.info({ schema });

/**

  • Create graphql server
    */
    export default createYoga<{
    req: Request;
    res: Response;
    }>({
    // Needed to be defined explicitly because our endpoint lives at a different path other than /graphql
    graphqlEndpoint: '/api/graphql',
    schema,

// We won't handle file uploads via GraphQL so we disable multipart requests which can be used for CSRF attacks
multipart: false,
plugins,
maskedErrors: false, // Boolean(process.env.NODE_ENV === 'production'),
});

[Graphql route through remix]
import {
ActionFunctionArgs,
json,
LoaderFunctionArgs,
} from '@remix-run/server-runtime';
import yoga from '../../graphql.server';

export const loader = async ({ request }: LoaderFunctionArgs) => {
return yoga.handleRequest(request, { req: request, res: new Response() });
};

export const action = async ({ request }: ActionFunctionArgs) => {
return yoga.handleRequest(request, { req: request, res: new Response() });
};

In development everything works great, but with a production build it does not have any of my own types.
Hope that someone can push me in the right direction.

PS: When i navigate to the route /api/graphql i see the message in the editor " "message": "Type Mutation must define one or more fields."
"

@atman-33
Copy link

I encountered a similar issue.

In my case, I separated type definitions into individual files (such as post.model.ts) and imported them into an index file for the schema.

Code Before Fix

post.model.ts

import { builder } from '~/lib/graphql/builder';

export const PostStatus = builder.enumType('PostStatus', {
  values: ['DRAFT', 'PUBLIC'] as const,
});

builder.prismaObject('PostTag', {
  fields: (t) => ({
    id: t.exposeString('id'),
    post: t.relation('post'),
    tag: t.relation('tag'),
  }),
});

builder.prismaNode('Post', {
  id: { field: 'id' },
  findUnique: (id) => ({ id }),
  fields: (t) => ({
    title: t.exposeString('title'),
    emoji: t.exposeString('emoji'),
    content: t.exposeString('content'),
    status: t.expose('status', { type: PostStatus }),
    author: t.relation('author'),
    createdAt: t.expose('createdAt', { type: 'DateTime' }),
    updatedAt: t.expose('updatedAt', { type: 'DateTime' }),
    tags: t.relation('tags'),
  }),
});

post/index.ts

import './post.model';
import './post.mutation';
import './post.query';

schema/index.ts

import { builder } from '../builder';

import './post';
import './tag';
import './user';

export const schema = builder.toSchema();

In this setup, during the Vite build, the type definition files imported in schema/index.ts were being excluded from the build process.

Code After Fix

To work around this, I created a load function. Since empty functions are still omitted during the build, I added console logs to ensure the code is included.

post.model.ts

import { builder } from '~/lib/graphql/builder';

export const PostStatus = builder.enumType('PostStatus', {
  values: ['DRAFT', 'PUBLIC'] as const,
});

builder.prismaObject('PostTag', {
  fields: (t) => ({
    id: t.exposeString('id'),
    post: t.relation('post'),
    tag: t.relation('tag'),
  }),
});

builder.prismaNode('Post', {
  id: { field: 'id' },
  findUnique: (id) => ({ id }),
  fields: (t) => ({
    title: t.exposeString('title'),
    emoji: t.exposeString('emoji'),
    content: t.exposeString('content'),
    status: t.expose('status', { type: PostStatus }),
    author: t.relation('author'),
    createdAt: t.expose('createdAt', { type: 'DateTime' }),
    updatedAt: t.expose('updatedAt', { type: 'DateTime' }),
    tags: t.relation('tags'),
  }),
});

// add below
export const loadPostModel = () => {
  console.log('Post model loaded');
};

post/index.ts

import { loadPostModel } from './post.model';
import { loadPostMutation } from './post.mutation';
import { loadPostQuery } from './post.query';

export const loadPost = () => {
  loadPostModel();
  loadPostMutation();
  loadPostQuery();
};

schema/index.ts

import { builder } from '../builder';
import { loadPost } from './post';
import { loadTag } from './tag';
import { loadUser } from './user';

// load schemas
loadPost();
loadTag();
loadUser();

export const schema = builder.toSchema();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants