feat: introduce gql schema sdl generation to the backend (#35)

* feat: introduce gql schema sdl generation to the backend

* chore: update gql-codegen consumers to get schema from generated sdl

* chore: hoppscotch-backend generates gql sdl on postinstall

* fix: add back missed part of generate-gql-sdl script

* chore: updated generate sdl script to hardcode whitelisted domains

* chore: add prisma generate on postinstall script

---------

Co-authored-by: ankitsridhar16 <ankit.sridhar16@gmail.com>
This commit is contained in:
Andrew Bastin
2023-03-13 18:52:50 +05:30
committed by GitHub
parent 44402ac6e1
commit 65719b560b
9 changed files with 120 additions and 5 deletions

View File

@@ -38,3 +38,6 @@ lerna-debug.log*
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
# Generated artifacts (GQL Schema SDL generation etc.)
gen/

View File

@@ -8,6 +8,7 @@
"scripts": {
"prebuild": "rimraf dist",
"build": "nest build",
"generate-gql-sdl": "cross-env GQL_SCHEMA_EMIT_LOCATION='../../../gql-gen/backend-schema.gql' GENERATE_GQL_SCHEMA=true WHITELISTED_ORIGINS='' nest start",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "nest start --watch",
@@ -15,6 +16,7 @@
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"postinstall": "prisma generate && pnpm run generate-gql-sdl",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
@@ -76,6 +78,7 @@
"@types/supertest": "^2.0.12",
"@typescript-eslint/eslint-plugin": "^5.45.0",
"@typescript-eslint/parser": "^5.45.0",
"cross-env": "^7.0.3",
"eslint": "^8.29.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.2.1",

View File

@@ -0,0 +1,95 @@
import { NestFactory } from '@nestjs/core';
import {
GraphQLSchemaBuilderModule,
GraphQLSchemaFactory,
} from '@nestjs/graphql';
import { printSchema } from 'graphql/utilities';
import * as path from 'path';
import * as fs from 'fs';
import { ShortcodeResolver } from './shortcode/shortcode.resolver';
import { TeamCollectionResolver } from './team-collection/team-collection.resolver';
import { TeamEnvironmentsResolver } from './team-environments/team-environments.resolver';
import { TeamInvitationResolver } from './team-invitation/team-invitation.resolver';
import { TeamRequestResolver } from './team-request/team-request.resolver';
import { TeamMemberResolver } from './team/team-member.resolver';
import { TeamResolver } from './team/team.resolver';
import { UserCollectionResolver } from './user-collection/user-collection.resolver';
import { UserEnvironmentsResolver } from './user-environment/user-environments.resolver';
import { UserHistoryResolver } from './user-history/user-history.resolver';
import { UserRequestResolver } from './user-request/resolvers/user-request.resolver';
import { UserSettingsResolver } from './user-settings/user-settings.resolver';
import { UserResolver } from './user/user.resolver';
import { Logger } from '@nestjs/common';
/**
* All the resolvers present in the application.
*
* NOTE: This needs to be KEPT UP-TO-DATE to keep the schema accurate
*/
const RESOLVERS = [
ShortcodeResolver,
TeamResolver,
TeamMemberResolver,
TeamCollectionResolver,
TeamEnvironmentsResolver,
TeamInvitationResolver,
TeamRequestResolver,
UserResolver,
UserCollectionResolver,
UserEnvironmentsResolver,
UserHistoryResolver,
UserCollectionResolver,
UserRequestResolver,
UserSettingsResolver,
];
/**
* All the custom scalars present in the application.
*
* NOTE: This needs to be KEPT UP-TO-DATE to keep the schema accurate
*/
const SCALARS = [];
/**
* Generates the GraphQL Schema SDL definition and writes it into the location
* specified by the `GQL_SCHEMA_EMIT_LOCATION` environment variable.
*/
export async function emitGQLSchemaFile() {
const logger = new Logger('emitGQLSchemaFile');
try {
const destination = path.resolve(
__dirname,
process.env.GQL_SCHEMA_EMIT_LOCATION ?? '../gen/schema.gql',
);
logger.log(`GQL_SCHEMA_EMIT_LOCATION: ${destination}`);
const app = await NestFactory.create(GraphQLSchemaBuilderModule);
await app.init();
const gqlSchemaFactory = app.get(GraphQLSchemaFactory);
logger.log(
`Generating Schema against ${RESOLVERS.length} resolvers and ${SCALARS.length} custom scalars`,
);
const schema = await gqlSchemaFactory.create(RESOLVERS, SCALARS);
const schemaString = printSchema(schema, {
commentDescriptions: true,
});
logger.log(`Writing schema to GQL_SCHEMA_EMIT_LOCATION (${destination})`);
// Generating folders if required to emit to the given output
fs.mkdirSync(path.dirname(destination), { recursive: true });
fs.writeFileSync(destination, schemaString);
logger.log(`Wrote schema to GQL_SCHEMA_EMIT_LOCATION (${destination})`);
} catch (e) {
logger.error(
`Failed writing schema to GQL_SCHEMA_EMIT_LOCATION. Reason: ${e}`,
);
}
}

View File

@@ -3,6 +3,7 @@ import { json } from 'express';
import { AppModule } from './app.module';
import * as cookieParser from 'cookie-parser';
import { VersioningType } from '@nestjs/common';
import { emitGQLSchemaFile } from './gql-schema';
async function bootstrap() {
console.log(`Running in production: ${process.env.PRODUCTION}`);
@@ -38,4 +39,9 @@ async function bootstrap() {
app.use(cookieParser());
await app.listen(process.env.PORT || 3170);
}
bootstrap();
if (!process.env.GENERATE_GQL_SCHEMA) {
bootstrap();
} else {
emitGQLSchemaFile();
}

View File

@@ -1,6 +1,5 @@
overwrite: true
schema:
- ${VITE_BACKEND_GQL_URL}
schema: "../../gql-gen/*.gql"
generates:
src/helpers/backend/graphql.ts:
documents: "src/**/*.graphql"

View File

@@ -1,5 +1,5 @@
overwrite: true
schema: ${VITE_BACKEND_GQL_URL}
schema: "../../gql-gen/*.gql"
generates:
src/helpers/backend/graphql.ts:
documents: 'src/**/*.graphql'