Generate TypeGraphQL class definition from JSON/Remote API/JSON File.
npm i json-type-graphql --save
yarn add json-type-graphql --save
pnpm i json-type-graphql --saveThis project is still under heavy development, the documentation is far from ready, but basic features are already supported:
- Support
nested object type(like:{ foo: { bar: { baz:{} } } }) andarray entry json(like:[{},{}]) type generation. - Normal generator order as
P-C1-C11-C12-C2-C21-C3-C31. - Customizable processing flow: reader / preprocessor / postprocessor / writer
- ...
Run
yarn demoto explore!
JSON:
{
"booleanField": true,
"numberField": 200,
"stringField": "success",
"primitiveArrayField": [1, 2, 3, 4, 5],
"mixedField": [
1,
2,
{
"a": "1111111"
}
],
"emptyArrayField": [],
"nestedField": {
"booleanField": true,
"numberField": 200,
"stringField": "success",
"primitiveArrayField": [1, 2, 3, 4, 5],
"mixedFieldrs": [1, 2]
}
}import path from "path";
import fs from "fs-extra";
import transformer from "json-type-garphql";
(async () => {
await transformer({
// Provide json file path
reader: { path: path.join(__dirname, "./demo.json") },
// Customize parser behaviour
parser: {
forceNonNullable: false,
},
// Customize generator behaviour
generator: { entryClassName: "Root", sort: true },
// Check can generated TypeGraphQL class be used normally
checker: {
disable: false,
},
// Write generated file!
writter: {
outputPath: path.join(__dirname, "./generated.ts"),
},
});
})();More options will be introduced below.
generated:
import { ObjectType, Field, Int, ID } from "type-graphql";
@ObjectType()
export class MixedField {
@Field()
a!: string;
}
@ObjectType()
export class EmptyArrayField {}
@ObjectType()
export class NestedField {
@Field({ nullable: true })
booleanField?: boolean;
@Field((type) => Int, { nullable: true })
numberField?: number;
@Field({ nullable: true })
stringField?: string;
@Field((type) => [Int], { nullable: true })
primitiveArrayField?: number[];
@Field((type) => [Int], { nullable: true })
mixedFieldrs?: number[];
}
@ObjectType()
export class Root {
@Field({ nullable: true })
booleanField?: boolean;
@Field((type) => Int, { nullable: true })
numberField?: number;
@Field({ nullable: true })
stringField?: string;
@Field((type) => [Int], { nullable: true })
primitiveArrayField?: number[];
@Field((type) => [MixedField], { nullable: true })
mixedField?: MixedField[];
@Field((type) => [EmptyArrayField], { nullable: true })
emptyArrayField?: EmptyArrayField[];
@Field((type) => NestedField, { nullable: true })
nestedField?: NestedField;
}import {
reader,
parser,
preprocessor,
generator,
writter,
} from "json-type-graphql";
export default async function handler(options: Options): Promise<void> {
// read from data source you want
// you can also use custom reader
const content = await reader(options.reader);
// make some custom processing
const preprocessed = preprocessor(content, normalizedPreprocessorOptions);
// parse content
const parsedInfo = parser(preprocessed, normalizedParserOptions);
fs.ensureFileSync(normalizedWritterOptions.outputPath);
const source = new Project().addSourceFileAtPath(
normalizedWritterOptions.outputPath
);
// generate AST and result!
generator(source, parsedInfo, normalizedGeneratorOptions);
// write!
writter(normalizedWritterOptions);
}Reader is responsible for reading data from different sources including JSON File / URL Request / Raw JavaScript Object, you must provide one of reader.path / reader.url / reader.raw options.
path(string): Absoulte JSON file path.url(string) &options(Got Options): Using got for data fetching:got(url, options).raw(object|array): Vanilla JavaScript Object / Array.
After content acquisition got completed, the content will be passed to next handler called preprocessor.
Preprocessor will perform some extra pre-processing works in the incoming content:
- Recursively delete object field which value is kind of nested array like
[[]], this is not supported yet which may cause unexpected behaviours or errors. - Ensure array contains either primitive type values or object type values, by default,only obejct values will be preserved when the array
contains both kinds of members(You can control this behaviour by
preprocessor.preserveObjectOnlyInArray).
preserveObjectOnlyInArray(boolean):default: truecustomPreprocessor((raw: object | array) => object | array): Use your own custom preprocessor, which accepts content from reader, and should return JavaScript Object / Array.
Parser will transform the pre-processed content to specific object structure,
which will be consumed by generator.
Array entry structure(like
[]) and object entry structure(like{}) will be parsed differently.
-
forceNonNullable(boolean): Mark all field as non-nullale.default: true -
forceNonNullableListItem(boolean): Mark all list item as non-nullale.default: false -
forceReturnType(boolean): Generate return type for evenstring/booleanfield like@Field((type) => String).default: false -
arrayEntryProp(string): When parsing array-entry structure, use specified prop name like:data: Data[].default: 'data'. For example,[{ foo: 1 }]will be parsed to:class Data { foo: number; } class Root { data: Data[]; }
Generator will traverse the parsed info, perform corresponding AST operations to generate class definitions with TypeGraphQL decorators.
-
entryClassName(string): The top-level generated entry class name.default: 'Root'. -
prefix(boolean|string): Prefix for generated class name, you can setprefix: trueto simply avoid repeated class specifier.default: false. By using parent class in child class name's prefix, likeRootChildSomeChildPropis from:class Root { child: RootChild; } class RootChild { someChildProp: RootChildSomeChildProp; } class RootChildSomeChildProp {}
-
suffix(boolean|string): Suffix for generated class name, e.g.RootType,Typeis the specified suffix.default: false. -
publicProps(string[]): Prop names included by it will be attatched withpublickeyword. -
readonlyProps(string[]): Prop names included by it will be attatched withreadonlykeyword. -
sort(boolean): Should sort generated class in normal order likeP-C1-C11-C12-C2-C21-C3-C31.default: true.
Postprocessor is used to apply some post-process works on generated source (TypeScript SourceFile), you can use ts-morph for simple and flexiable AST operations, which also powers the generator part indeed.
customPostprocessor((source: SourceFile) => SourceFile): Custom post-processor accepts the AST source file.
Checker will use generated class definitions to create a tmp reoslver, invoking TypeGraphQL.buildSchemaSync method to check if generated file works correctly.
We're using ts-node tmp-file.ts --compiler-options [options] to perform the check under the hood.
disable(boolean): Disable checker.default: truekeep(boolean): Keey generated tmp checker file.default: falseexecaOptions(Execa Options): Extra options passed to execa.executeOptions(Ts-node compile Options): Extra options passed to ts-node--compiler-options, which keeps same with TypeSctipt CompilerOptions.
Writer will format generated source file.
outputPath(string): Output path. required.format(boolean): Should perform formatting byPrettier.default: true.formatOptions(Prettier Options): Options passed toPrettier.format.