diff --git a/dev.js b/dev.js index 5b5cc2b26..fb4b492c8 100644 --- a/dev.js +++ b/dev.js @@ -3,6 +3,7 @@ require("ts-node/register/transpile-only"); // require("./examples/authorization/index.ts"); // require("./examples/automatic-validation/index.ts"); // require("./examples/enums-and-unions/index.ts"); +// require("./examples/generic-types/index.ts"); // require("./examples/interfaces-inheritance/index.ts"); // require("./examples/middlewares/index.ts"); // require("./examples/redis-subscriptions/index.ts"); diff --git a/docs/examples.md b/docs/examples.md index ba93d19b9..1372683b2 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -12,10 +12,11 @@ All examples has a `examples.gql` file with sample queries/mutations/subscriptio ## Advanced - [Enums and unions](https://github.com/19majkel94/type-graphql/tree/master/examples/enums-and-unions) -- [Interfaces and types inheritance](https://github.com/19majkel94/type-graphql/tree/master/examples/interfaces-inheritance) - [Subscriptions (simple)](https://github.com/19majkel94/type-graphql/tree/master/examples/simple-subscriptions) - [Subscriptions (using Redis)](https://github.com/19majkel94/type-graphql/tree/master/examples/redis-subscriptions) +- [Interfaces and types inheritance](https://github.com/19majkel94/type-graphql/tree/master/examples/interfaces-inheritance) - [Resolvers inheritance](https://github.com/19majkel94/type-graphql/tree/master/examples/resolvers-inheritance) +- [Generic types](https://github.com/19majkel94/type-graphql/tree/master/examples/generic-types) ## Features usage - [Dependency injection (IoC container)](https://github.com/19majkel94/type-graphql/tree/master/examples/using-container) diff --git a/examples/README.md b/examples/README.md index aeb5a2c50..3bc44b5cc 100644 --- a/examples/README.md +++ b/examples/README.md @@ -8,10 +8,11 @@ All examples has a `examples.gql` file with sample queries/mutations/subscriptio ## Advanced - [Enums and unions](./enums-and-unions) -- [Interfaces and types inheritance](./interfaces-inheritance) - [Subscriptions (simple)](./simple-subscriptions) - [Subscriptions (using Redis)](./redis-subscriptions) +- [Interfaces and types inheritance](./interfaces-inheritance) - [Resolvers inheritance](./resolvers-inheritance) +- [Generic types](./generic-types) ## Features usage - [Dependency injection (IoC container)](./using-container) diff --git a/examples/generic-types/examples.gql b/examples/generic-types/examples.gql new file mode 100644 index 000000000..20e582ab7 --- /dev/null +++ b/examples/generic-types/examples.gql @@ -0,0 +1,16 @@ +query GetRecipes { + recipes(first: 3) { + items { + title + ratings + } + total + hasMore + } +} + +mutation AddRecipe { + addSampleRecipe { + title + } +} diff --git a/examples/generic-types/index.ts b/examples/generic-types/index.ts new file mode 100644 index 000000000..4f1dfb4ce --- /dev/null +++ b/examples/generic-types/index.ts @@ -0,0 +1,23 @@ +import "reflect-metadata"; +import { ApolloServer } from "apollo-server"; +import * as path from "path"; +import { buildSchema } from "../../src"; + +import RecipeResolver from "./recipe.resolver"; + +async function bootstrap() { + const schema = await buildSchema({ + resolvers: [RecipeResolver], + emitSchemaFile: path.resolve(__dirname, "schema.gql"), + }); + + const server = new ApolloServer({ + schema, + playground: true, + }); + + const { url } = await server.listen(4000); + console.log(`Server is running, GraphQL Playground available at ${url}`); +} + +bootstrap().catch(console.error); diff --git a/examples/generic-types/paginated-response.type.ts b/examples/generic-types/paginated-response.type.ts new file mode 100644 index 000000000..693e4fbaa --- /dev/null +++ b/examples/generic-types/paginated-response.type.ts @@ -0,0 +1,17 @@ +import { ClassType, Field, ObjectType, Int } from "../../src"; + +export default function PaginatedResponse(TItemClass: ClassType) { + // `isAbstract` decorator option is mandatory to prevent registering in schema + @ObjectType({ isAbstract: true }) + abstract class PaginatedResponseClass { + @Field(type => [TItemClass]) + items: TItem[]; + + @Field(type => Int) + total: number; + + @Field() + hasMore: boolean; + } + return PaginatedResponseClass; +} diff --git a/examples/generic-types/recipe.resolver.ts b/examples/generic-types/recipe.resolver.ts new file mode 100644 index 000000000..d1ba1c1c9 --- /dev/null +++ b/examples/generic-types/recipe.resolver.ts @@ -0,0 +1,45 @@ +import { ObjectType, Query, Mutation, Arg, Int, Resolver } from "../../src"; + +import PaginatedResponse from "./paginated-response.type"; +import Recipe from "./recipe.type"; +import createSampleRecipes from "./recipe.samples"; + +// we need to create a temporary class for the abstract, generic class "instance" +@ObjectType() +class RecipesResponse extends PaginatedResponse(Recipe) { + // simple helper for creating new instances easily + constructor(recipesResponse: RecipesResponse) { + super(); + Object.assign(this, recipesResponse); + } + + // you can add more fields here if you need +} + +@Resolver() +export default class RecipeResolver { + private readonly recipes = createSampleRecipes(); + + @Query({ name: "recipes" }) + getRecipes( + @Arg("first", type => Int, { nullable: true, defaultValue: 10 }) first: number, + ): RecipesResponse { + const total = this.recipes.length; + return new RecipesResponse({ + items: this.recipes.slice(0, first), + hasMore: total > first, + total, + }); + } + + @Mutation() + addSampleRecipe(): Recipe { + const recipe: Recipe = { + title: "Sample recipe", + description: "Sample description", + ratings: [1, 2, 3, 4], + }; + this.recipes.push(recipe); + return recipe; + } +} diff --git a/examples/generic-types/recipe.samples.ts b/examples/generic-types/recipe.samples.ts new file mode 100644 index 000000000..97839695b --- /dev/null +++ b/examples/generic-types/recipe.samples.ts @@ -0,0 +1,21 @@ +import Recipe from "./recipe.type"; + +export default function createSampleRecipes(): Recipe[] { + return [ + { + description: "Desc 1", + title: "Recipe 1", + ratings: [0, 3, 1], + }, + { + description: "Desc 2", + title: "Recipe 2", + ratings: [4, 2, 3, 1], + }, + { + description: "Desc 3", + title: "Recipe 3", + ratings: [5, 4], + }, + ]; +} diff --git a/examples/generic-types/recipe.type.ts b/examples/generic-types/recipe.type.ts new file mode 100644 index 000000000..fa54fa4d6 --- /dev/null +++ b/examples/generic-types/recipe.type.ts @@ -0,0 +1,13 @@ +import { Field, ObjectType, Int } from "../../src"; + +@ObjectType() +export default class Recipe { + @Field() + title: string; + + @Field() + description?: string; + + @Field(type => [Int]) + ratings: number[]; +} diff --git a/examples/generic-types/schema.gql b/examples/generic-types/schema.gql new file mode 100644 index 000000000..0a56b53e7 --- /dev/null +++ b/examples/generic-types/schema.gql @@ -0,0 +1,24 @@ +# ----------------------------------------------- +# !!! THIS FILE WAS GENERATED BY TYPE-GRAPHQL !!! +# !!! DO NOT MODIFY THIS FILE BY YOURSELF !!! +# ----------------------------------------------- + +type Mutation { + addSampleRecipe: Recipe! +} + +type Query { + recipes(first: Int = 10): RecipesResponse! +} + +type Recipe { + title: String! + description: String! + ratings: [Int!]! +} + +type RecipesResponse { + items: [Recipe!]! + total: Int! + hasMore: Boolean! +} diff --git a/examples/tsconfig.json b/examples/tsconfig.json new file mode 100644 index 000000000..6692783ce --- /dev/null +++ b/examples/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "declaration": false + } +}