diff --git a/CHANGELOG.md b/CHANGELOG.md index 39841062c..ac8c96082 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,32 +1,17 @@ # Changelog and release notes -## Unreleased + -- **Breaking Change**: update `graphql-js` peer dependency to `^15.3.0` -- update `graphql-query-complexity` dependency to `^0.7.0` - -## v1.0.0-rc.3 -### Features -- **Breaking Change**: remove legacy array inference - now explicit array syntax (`[Item]`) is required -- **Breaking Change**: update `graphql-js` peer dependency to `^15.1.0` -- update deps to newest major versions (`tslib`, `graphql-query-complexity`) -## v1.0.0-rc.2 -### Features -- expose `createResolversMap` utility that generates apollo-like resolvers object -- support IoC containers which `.get()` method returns a `Promise` of resolver instance -### Fixes -- properly inherit directives while extending `@InputType` or `@ObjectType` classes (#626) -- skip transforming empty array items into input classes - -## v1.0.0-rc.1 +## v1.0.0 ### Features - **Breaking Change**: emit in schema only types actually used by provided resolvers classes (#415) -- **Breaking Change**: update `graphql-js` peer dependency to `^15.0.0` -- **Breaking Change**: update `graphql-query-complexity` dependency to `^0.5.0` and drop support for `fieldConfigEstimator` (use `fieldExtensionsEstimator` instead) +- **Breaking Change**: update `graphql-js` peer dependency to `^15.3.0` +- **Breaking Change**: update `graphql-query-complexity` dependency to `^0.7.0` and drop support for `fieldConfigEstimator` (use `fieldExtensionsEstimator` instead) - **Breaking Change**: introduce `sortedSchema` option in `PrintSchemaOptions` and emit sorted schema file by default - **Breaking Change**: make `class-validator` a peer dependency of version `>=0.12.0` that needs to be installed manually (#366) - **Breaking Change**: remove `CannotDetermineTypeError` and make other error messages more detailed and specific +- **Breaking Change**: remove legacy array inference - now explicit array syntax (`[Item]`) is required - update `TypeResolver` interface to match with `GraphQLTypeResolver` from `graphql-js` - add basic support for directives with `@Directive()` decorator (#369) - add possibility to tune up the performance and disable auth & middlewares stack for simple field resolvers (#479) @@ -35,10 +20,13 @@ - add support for defining arguments and implementing resolvers for interface types fields (#579) - add `{ autoRegisterImplementations: false }` option to prevent automatic emitting in schema all the object types that implements used interface type (#595) - allow interfaces to implement other interfaces (#602) +- expose `createResolversMap` utility that generates apollo-like resolvers object +- support IoC containers which `.get()` method returns a `Promise` of resolver instance +- update deps to newest major versions (`tslib`, `graphql-query-complexity`) ### Fixes - **Breaking Change**: stop returning null for `GraphQLTimestamp` and `GraphQLISODateTime` scalars when returned value is not a `Date` instance - now it throws explicit error instead +- **Breaking Change**: fix transforming and validating nested inputs and arrays (#462) - refactor union types function syntax handling to prevent possible errors with circular refs -- fix transforming and validating nested inputs and arrays (#462) - remove duplicated entries for resolver classes that use inheritance (#499) - fix using `name` option on interface fields (#567) - fix not calling `authChecker` during subscribe phase for subscriptions (#578) @@ -47,6 +35,8 @@ - fix calling field resolver without providing resolver class to `buildSchema` - fix generated TS union type for union type of object type classes extending themselves (#587) - fix using shared union and interface types in multiple schemas when `resolveType` is used +- properly inherit directives while extending `@InputType` or `@ObjectType` classes (#626) +- skip transforming empty array items into input classes ### Others - **Breaking Change**: change build config to ES2018 - drop support for Node.js < 10.3 - **Breaking Change**: remove deprecated `DepreciationOptions` interface diff --git a/website/i18n/en.json b/website/i18n/en.json index c675bfb4b..b98b01c00 100644 --- a/website/i18n/en.json +++ b/website/i18n/en.json @@ -374,87 +374,78 @@ "version-0.17.6/version-0.17.6-custom-decorators": { "title": "Custom decorators" }, - "version-1.0.0-rc.1/version-1.0.0-rc.1-bootstrap": { + "version-1.0.0/version-1.0.0-bootstrap": { "title": "Bootstrapping" }, - "version-1.0.0-rc.1/version-1.0.0-rc.1-browser-usage": { + "version-1.0.0/version-1.0.0-browser-usage": { "title": "Browser usage" }, - "version-1.0.0-rc.1/version-1.0.0-rc.1-complexity": { + "version-1.0.0/version-1.0.0-complexity": { "title": "Query complexity" }, - "version-1.0.0-rc.1/version-1.0.0-rc.1-custom-decorators": { + "version-1.0.0/version-1.0.0-custom-decorators": { "title": "Custom decorators" }, - "version-1.0.0-rc.1/version-1.0.0-rc.1-dependency-injection": { + "version-1.0.0/version-1.0.0-dependency-injection": { "title": "Dependency injection" }, - "version-1.0.0-rc.1/version-1.0.0-rc.1-directives": { + "version-1.0.0/version-1.0.0-directives": { "title": "Directives" }, - "version-1.0.0-rc.1/version-1.0.0-rc.1-emit-schema": { + "version-1.0.0/version-1.0.0-emit-schema": { "title": "Emitting the schema SDL" }, - "version-1.0.0-rc.1/version-1.0.0-rc.1-examples": { + "version-1.0.0/version-1.0.0-examples": { "title": "Examples", "sidebar_label": "List of examples" }, - "version-1.0.0-rc.1/version-1.0.0-rc.1-extensions": { + "version-1.0.0/version-1.0.0-extensions": { "title": "Extensions" }, - "version-1.0.0-rc.1/version-1.0.0-rc.1-generic-types": { + "version-1.0.0/version-1.0.0-generic-types": { "title": "Generic Types" }, - "version-1.0.0-rc.1/version-1.0.0-rc.1-getting-started": { + "version-1.0.0/version-1.0.0-getting-started": { "title": "Getting started" }, - "version-1.0.0-rc.1/version-1.0.0-rc.1-installation": { + "version-1.0.0/version-1.0.0-installation": { "title": "Installation" }, - "version-1.0.0-rc.1/version-1.0.0-rc.1-interfaces": { + "version-1.0.0/version-1.0.0-interfaces": { "title": "Interfaces" }, - "version-1.0.0-rc.1/version-1.0.0-rc.1-middlewares": { + "version-1.0.0/version-1.0.0-middlewares": { "title": "Middleware and guards" }, - "version-1.0.0-rc.1/version-1.0.0-rc.1-performance": { + "version-1.0.0/version-1.0.0-nestjs": { + "title": "NestJS Integration", + "sidebar_label": "NestJS" + }, + "version-1.0.0/version-1.0.0-performance": { "title": "Performance" }, - "version-1.0.0-rc.1/version-1.0.0-rc.1-resolvers": { + "version-1.0.0/version-1.0.0-prisma": { + "title": "Prisma 2 Integration", + "sidebar_label": "Prisma 2" + }, + "version-1.0.0/version-1.0.0-resolvers": { "title": "Resolvers" }, - "version-1.0.0-rc.1/version-1.0.0-rc.1-subscriptions": { + "version-1.0.0/version-1.0.0-scalars": { + "title": "Scalars" + }, + "version-1.0.0/version-1.0.0-subscriptions": { "title": "Subscriptions" }, - "version-1.0.0-rc.1/version-1.0.0-rc.1-types-and-fields": { + "version-1.0.0/version-1.0.0-types-and-fields": { "title": "Types and Fields" }, - "version-1.0.0-rc.1/version-1.0.0-rc.1-unions": { + "version-1.0.0/version-1.0.0-unions": { "title": "Unions" }, - "version-1.0.0-rc.1/version-1.0.0-rc.1-validation": { + "version-1.0.0/version-1.0.0-validation": { "title": "Argument and Input validation", "sidebar_label": "Validation" - }, - "version-1.0.0-rc.2/version-1.0.0-rc.2-examples": { - "title": "Examples", - "sidebar_label": "List of examples" - }, - "version-1.0.0-rc.3/version-1.0.0-rc.3-bootstrap": { - "title": "Bootstrapping" - }, - "version-1.0.0-rc.3/version-1.0.0-rc.3-browser-usage": { - "title": "Browser usage" - }, - "version-1.0.0-rc.3/version-1.0.0-rc.3-examples": { - "title": "Examples", - "sidebar_label": "List of examples" - }, - "version-1.0.0-rc.3/version-1.0.0-rc.3-resolvers": { - "title": "Resolvers" - }, - "version-1.0.0-rc.3/version-1.0.0-rc.3-types-and-fields": { - "title": "Types and Fields" } }, "links": { diff --git a/website/versioned_docs/version-1.0.0-rc.1/bootstrap.md b/website/versioned_docs/version-1.0.0-rc.1/bootstrap.md deleted file mode 100644 index e3c6883fa..000000000 --- a/website/versioned_docs/version-1.0.0-rc.1/bootstrap.md +++ /dev/null @@ -1,132 +0,0 @@ ---- -title: Bootstrapping -id: version-1.0.0-rc.1-bootstrap -original_id: bootstrap ---- - -After creating our resolvers, type classes, and other business-related code, we need to make our app run. First we have to build the schema, then we can expose it with an HTTP server, WebSockets or even MQTT. - -## Create Executable Schema - -To create an executable schema from type and resolver definitions, we need to use the `buildSchema` function. -It takes a configuration object as a parameter and returns a promise of a `GraphQLSchema` object. - -In the configuration object we must provide a `resolvers` property, which can be an array of resolver classes: - -```typescript -import { FirstResolver, SecondResolver } from "../app/src/resolvers"; -// ... -const schema = await buildSchema({ - resolvers: [FirstResolver, SecondResolver], -}); -``` - -Be aware that only operations (queries, mutation, etc.) defined in the resolvers classes (and types directly connected to them) will be emitted in schema. - -So if we have defined some object types (that implements an interface type [with disabled auto registering](interfaces.md#registering-in-schema)) but are not directly used in other types definition (like a part of an union, a type of a field or a return type of an operation), we need to provide them manually in `orphanedTypes` options of `buildSchema`: - -```typescript -import { FirstResolver, SecondResolver } from "../app/src/resolvers"; -import { FirstObject } from "../app/src/types"; -// ... -const schema = await buildSchema({ - resolvers: [FirstResolver, SecondResolver], - // here provide all the types that are missing in schema - orphanedTypes: [FirstObject], -}); -``` - -However, when there are several resolver classes, manual imports can be cumbersome. -So we can also provide an array of paths to resolver module files instead, which can include globs: - -```typescript -const schema = await buildSchema({ - resolvers: [__dirname + "/modules/**/*.resolver.{ts,js}", __dirname + "/resolvers/**/*.{ts,js}"], -}); -``` - -> Be aware that in case of providing paths to resolvers files, TypeGraphQL will emit all the operations and types that are imported in the resolvers files or their dependencies. - -There are also other options related to advanced features like [authorization](authorization.md) or [validation](validation.md) - you can read about them in docs. - -To make `await` work, we need to declare it as an async function. Example of `main.ts` file: - -```typescript -import { buildSchema } from "type-graphql"; - -async function bootstrap() { - const schema = await buildSchema({ - resolvers: [__dirname + "/**/*.resolver.{ts,js}"], - }); - - // other initialization code, like creating http server -} - -bootstrap(); // actually run the async function -``` - -## Create an HTTP GraphQL endpoint - -In most cases, the GraphQL app is served by an HTTP server. After building the schema we can create the GraphQL endpoint with a variety of tools such as [`graphql-yoga`](https://github.com/prisma/graphql-yoga) or [`apollo-server`](https://github.com/apollographql/apollo-server). Here is an example using [`apollo-server`](https://github.com/apollographql/apollo-server): - -```typescript -import { ApolloServer } from "apollo-server"; - -const PORT = process.env.PORT || 4000; - -async function bootstrap() { - // ... Building schema here - - // Create the GraphQL server - const server = new ApolloServer({ - schema, - playground: true, - }); - - // Start the server - const { url } = await server.listen(PORT); - console.log(`Server is running, GraphQL Playground available at ${url}`); -} - -bootstrap(); -``` - -Remember to install the `apollo-server` package from npm - it's not bundled with TypeGraphQL. - -Of course you can use the `express-graphql` middleware, `graphql-yoga` or whatever you want 😉 - -## Create typeDefs and resolvers map - -TypeGraphQL provides a second way to generate the GraphQL schema - the `buildTypeDefsAndResolvers` function. - -It accepts the same `BuildSchemaOptions` as the `buildSchema` function but instead of an executable `GraphQLSchema`, it creates a typeDefs and resolversMap pair that you can use e.g. with [`graphql-tools`](https://github.com/apollographql/graphql-tools): - -```typescript -import { makeExecutableSchema } from "graphql-tools"; - -const { typeDefs, resolvers } = await buildTypeDefsAndResolvers({ - resolvers: [FirstResolver, SecondResolver], -}); - -const schema = makeExecutableSchema({ typeDefs, resolvers }); -``` - -Or even with other libraries that expect the schema info in that shape, like [`apollo-link-state`](https://github.com/apollographql/apollo-link-state): - -```typescript -import { withClientState } from "apollo-link-state"; - -const { typeDefs, resolvers } = await buildTypeDefsAndResolvers({ - resolvers: [FirstResolver, SecondResolver], -}); - -const stateLink = withClientState({ - // ...other options like `cache` - typeDefs, - resolvers, -}); - -// ...the rest of `ApolloClient` initialization code -``` - -Be aware that some of the TypeGraphQL features (i.a. [query complexity](complexity.md)) might not work with the `buildTypeDefsAndResolvers` approach because they use some low-level `graphql-js` features. diff --git a/website/versioned_docs/version-1.0.0-rc.1/browser-usage.md b/website/versioned_docs/version-1.0.0-rc.1/browser-usage.md deleted file mode 100644 index db836033c..000000000 --- a/website/versioned_docs/version-1.0.0-rc.1/browser-usage.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -title: Browser usage -id: version-1.0.0-rc.1-browser-usage -original_id: browser-usage ---- - -## Using classes in a client app - -Sometimes we might want to use the classes we've created and annotated with TypeGraphQL decorators, in our client app that works in the browser. For example, reusing the args or input classes with `class-validator` decorators or the object type classes with some helpful custom methods. - -Since TypeGraphQL is a Node.js framework, it doesn't work in a browser environment, so we may quickly get an error, e.g. `ERROR in ./node_modules/fs.realpath/index.js`, while trying to build our app with Webpack. To correct this, we have to configure Webpack to use the decorator shim instead of the normal module. We simply add this plugin code to our webpack config: - -```js -module.exports = { - // ... the rest of the webpack config - plugins: [ - // ... here are any other existing plugins that we already have - new webpack.NormalModuleReplacementPlugin(/type-graphql$/, resource => { - resource.request = resource.request.replace(/type-graphql/, "type-graphql/dist/browser-shim.js"); - }), - ]; -} -``` - -However, in some TypeScript projects like the ones using Angular, which AoT compiler requires that a full `*.ts` file is provided instead of just a `*.js` and `*.d.ts` files, to use this shim we have to simply set up our TypeScript configuration in `tsconfig.json` to use this file instead of a normal TypeGraphQL module: - -```json -{ - "compilerOptions": { - "baseUrl": ".", - "paths": { - "type-graphql": ["./node_modules/type-graphql/dist/browser-shim.ts"] - } - } -} -``` - -Thanks to this, our bundle will be much lighter as we don't need to embed the whole TypeGraphQL library code in our app. diff --git a/website/versioned_docs/version-1.0.0-rc.1/examples.md b/website/versioned_docs/version-1.0.0-rc.1/examples.md deleted file mode 100644 index 9c7ac1479..000000000 --- a/website/versioned_docs/version-1.0.0-rc.1/examples.md +++ /dev/null @@ -1,45 +0,0 @@ ---- -title: Examples -sidebar_label: List of examples -id: version-1.0.0-rc.1-examples -original_id: examples ---- - -On the [GitHub repository](https://github.com/MichalLytek/type-graphql) there are a few simple examples of how to use different TypeGraphQL features and how well they integrate with 3rd party libraries. - -All examples have an `examples.gql` file with sample queries/mutations/subscriptions that we can execute. - -## Basics - -- [Simple usage of fields, basic types and resolvers](https://github.com/MichalLytek/type-graphql/tree/v1.0.0-rc.1/examples/simple-usage) - -## Advanced - -- [Enums and unions](https://github.com/MichalLytek/type-graphql/tree/v1.0.0-rc.1/examples/enums-and-unions) -- [Subscriptions (simple)](https://github.com/MichalLytek/type-graphql/tree/v1.0.0-rc.1/examples/simple-subscriptions) -- [Subscriptions (using Redis)](https://github.com/MichalLytek/type-graphql/tree/v1.0.0-rc.1/examples/redis-subscriptions) -- [Interfaces](https://github.com/MichalLytek/type-graphql/tree/v1.0.0-rc.1/examples/interfaces-inheritance) - -## Features usage - -- [Dependency injection (IoC container)](https://github.com/MichalLytek/type-graphql/tree/v1.0.0-rc.1/examples/using-container) - - [scoped container](https://github.com/MichalLytek/type-graphql/tree/v1.0.0-rc.1/examples/using-scoped-container) -- [Authorization](https://github.com/MichalLytek/type-graphql/tree/v1.0.0-rc.1/examples/authorization) -- [Validation](https://github.com/MichalLytek/type-graphql/tree/v1.0.0-rc.1/examples/automatic-validation) -- [Types inheritance](https://github.com/MichalLytek/type-graphql/tree/v1.0.0-rc.1/examples/interfaces-inheritance) -- [Resolvers inheritance](https://github.com/MichalLytek/type-graphql/tree/v1.0.0-rc.1/examples/resolvers-inheritance) -- [Generic types](https://github.com/MichalLytek/type-graphql/tree/v1.0.0-rc.1/examples/generic-types) -- [Mixin classes](https://github.com/MichalLytek/type-graphql/tree/v1.0.0-rc.1/examples/mixin-classes) -- [Middlewares and Custom Decorators](https://github.com/MichalLytek/type-graphql/tree/v1.0.0-rc.1/examples/middlewares-custom-decorators) - -## 3rd party libs integration - -- [TypeORM (manual, synchronous) \*](https://github.com/MichalLytek/type-graphql/tree/v1.0.0-rc.1/examples/typeorm-basic-usage) -- [TypeORM (automatic, lazy relations) \*](https://github.com/MichalLytek/type-graphql/tree/v1.0.0-rc.1/examples/typeorm-lazy-relations) -- [Typegoose](https://github.com/MichalLytek/type-graphql/tree/v1.0.0-rc.1/examples/typegoose) -- [Apollo Engine (Apollo Cache Control) \*\*](https://github.com/MichalLytek/type-graphql/tree/v1.0.0-rc.1/examples/apollo-engine) -- [Apollo client state](https://github.com/MichalLytek/type-graphql/tree/v1.0.0-rc.1/examples/apollo-client) - -_\* Note that we need to edit the TypeORM example's `index.ts` with the credentials of our local database_ - -_\*\* Note that we need to provide an `APOLLO_ENGINE_API_KEY` env variable with our own API key_ diff --git a/website/versioned_docs/version-1.0.0-rc.1/resolvers.md b/website/versioned_docs/version-1.0.0-rc.1/resolvers.md deleted file mode 100644 index 235ebce57..000000000 --- a/website/versioned_docs/version-1.0.0-rc.1/resolvers.md +++ /dev/null @@ -1,346 +0,0 @@ ---- -title: Resolvers -id: version-1.0.0-rc.1-resolvers -original_id: resolvers ---- - -Besides [declaring GraphQL's object types](types-and-fields.md), TypeGraphQL allows us to easily create queries, mutations and field resolvers - like normal class methods, similar to REST controllers in frameworks like Java `Spring`, .NET `Web API` or TypeScript [`routing-controllers`](https://github.com/typestack/routing-controllers). - -## Queries and Mutations - -### Resolver classes - -First we create the resolver class and annotate it with the `@Resolver()` decorator. This class will behave like a controller from classic REST frameworks: - -```typescript -@Resolver() -class RecipeResolver {} -``` - -We can use a DI framework (as described in the [dependency injection docs](dependency-injection.md)) to inject class dependencies (like services or repositories) or to store data inside the resolver class - it's guaranteed to be a single instance per app. - -```typescript -@Resolver() -class RecipeResolver { - private recipesCollection: Recipe[] = []; -} -``` - -Then we can create class methods which will handle queries and mutations. For example, let's add the `recipes` query to return a collection of all recipes: - -```typescript -@Resolver() -class RecipeResolver { - private recipesCollection: Recipe[] = []; - - async recipes() { - // fake async in this example - return await this.recipesCollection; - } -} -``` - -We also need to do two things. -The first is to add the `@Query` decorator, which marks the class method as a GraphQL query. -The second is to provide the return type. Since the method is async, the reflection metadata system shows the return type as a `Promise`, so we have to add the decorator's parameter as `returns => [Recipe]` to declare it resolves to an array of `Recipe` object types. - -```typescript -@Resolver() -class RecipeResolver { - private recipesCollection: Recipe[] = []; - - @Query(returns => [Recipe]) - async recipes() { - return await this.recipesCollection; - } -} -``` - -### Arguments - -Usually, queries have some arguments - it might be the id of a resource, a search phrase or pagination settings. TypeGraphQL allows you to define arguments in two ways. - -First is the inline method using the `@Arg()` decorator. The drawback is the need to repeating the argument name (due to a limitation of the reflection system) in the decorator parameter. As we can see below, we can also pass a `defaultValue` option that will be reflected in the GraphQL schema. - -```typescript -@Resolver() -class RecipeResolver { - // ... - @Query(returns => [Recipe]) - async recipes( - @Arg("title", { nullable: true }) title?: string, - @Arg("servings", { defaultValue: 2 }) servings: number, - ): Promise { - // ... - } -} -``` - -This works well when there are 2 - 3 args. But when you have many more, the resolver's method definitions become bloated. In this case we can use a class definition to describe the arguments. It looks like the object type class but it has the `@ArgsType()` decorator on top. - -```typescript -@ArgsType() -class GetRecipesArgs { - @Field(type => Int, { nullable: true }) - skip?: number; - - @Field(type => Int, { nullable: true }) - take?: number; - - @Field({ nullable: true }) - title?: string; -} -``` - -We can define default values for optional fields in the `@Field()` decorator using the `defaultValue` option or by using a property initializer - in both cases TypeGraphQL will reflect this in the schema by setting the default value and making the field nullable. - -Also, this way of declaring arguments allows you to perform validation. You can find more details about this feature in the [validation docs](validation.md). You can also define helper fields and methods for your args or input class. - -```typescript -import { Min, Max } from "class-validator"; - -@ArgsType() -class GetRecipesArgs { - @Field(type => Int, { defaultValue: 0 }) - @Min(0) - skip: number; - - @Field(type => Int) - @Min(1) - @Max(50) - take = 25; - - @Field({ nullable: true }) - title?: string; - - // helpers - index calculations - get startIndex(): number { - return this.skip; - } - get endIndex(): number { - return this.skip + this.take; - } -} -``` - -Then all that is left to do is use the args class as the type of the method parameter. -We can use the destructuring syntax to gain access to single arguments as variables, instead of the reference to the whole args object. - -```typescript -@Resolver() -class RecipeResolver { - // ... - @Query(returns => [Recipe]) - async recipes(@Args() { title, startIndex, endIndex }: GetRecipesArgs) { - // sample implementation - let recipes = this.recipesCollection; - if (title) { - recipes = recipes.filter(recipe => recipe.title === title); - } - return recipes.slice(startIndex, endIndex); - } -} -``` - -This declaration will result in the following part of the schema in SDL: - -```graphql -type Query { - recipes(skip: Int = 0, take: Int = 25, title: String): [Recipe!] -} -``` - -### Input types - -GraphQL mutations can be similarly created: Declare the class method, use the `@Mutation` decorator, create arguments, provide a return type (if needed) etc. But for mutations we usually use `input` types, hence TypeGraphQL allows us to create inputs in the same way as [object types](types-and-fields.md) but by using the `@InputType()` decorator: - -```typescript -@InputType() -class AddRecipeInput {} -``` - -To ensure we don't accidentally change the property type we leverage the TypeScript type checking system by implementing the `Partial` type: - -```typescript -@InputType() -class AddRecipeInput implements Partial {} -``` - -We then declare any input fields we need, using the `@Field()` decorator: - -```typescript -@InputType({ description: "New recipe data" }) -class AddRecipeInput implements Partial { - @Field() - title: string; - - @Field({ nullable: true }) - description?: string; -} -``` - -After that we can use the `AddRecipeInput` type in our mutation. We can do this inline (using the `@Arg()` decorator) or as a field of the args class like in the query example above. - -We may also need access to the context. To achieve this we use the `@Ctx()` decorator with the optional user-defined `Context` interface: - -```typescript -@Resolver() -class RecipeResolver { - // ... - @Mutation() - addRecipe(@Arg("data") newRecipeData: AddRecipeInput, @Ctx() ctx: Context): Recipe { - // sample implementation - const recipe = RecipesUtils.create(newRecipeData, ctx.user); - this.recipesCollection.push(recipe); - return recipe; - } -} -``` - -Because our method is synchronous and explicitly returns `Recipe`, we can omit the `@Mutation()` type annotation. - -This declaration will result in the following part of the schema in SDL: - -```graphql -input AddRecipeInput { - title: String! - description: String -} -``` - -```graphql -type Mutation { - addRecipe(data: AddRecipeInput!): Recipe! -} -``` - -By using parameter decorators, we can get rid of unnecessary parameters (like `root`) that bloat our method definition and have to be ignored by prefixing the parameter name with `_`. Also, we can achieve a clean separation between GraphQL and our business code by using decorators, so our resolvers and their methods behave just like services which can be easily unit-tested. - -## Field resolvers - -Queries and mutations are not the only type of resolvers. We often create object type field resolvers (e.g. when a `user` type has a `posts` field) which we have to resolve by fetching relational data from the database. - -Field resolvers in TypeGraphQL are very similar to queries and mutations - we create them as a method on the resolver class but with a few modifications. First we declare which object type fields we are resolving by providing the type to the `@Resolver` decorator: - -```typescript -@Resolver(of => Recipe) -class RecipeResolver { - // queries and mutations -} -``` - -Then we create a class method that will become the field resolver. -In our example we have the `averageRating` field in the `Recipe` object type that should calculate the average from the `ratings` array. - -```typescript -@Resolver(of => Recipe) -class RecipeResolver { - // queries and mutations - - averageRating(recipe: Recipe) { - // ... - } -} -``` - -We then mark the method as a field resolver with the `@FieldResolver()` decorator. Since we've already defined the field type in the `Recipe` class definition, there's no need to redefine it. We also decorate the method parameters with the `@Root` decorator in order to inject the recipe object. - -```typescript -@Resolver(of => Recipe) -class RecipeResolver { - // queries and mutations - - @FieldResolver() - averageRating(@Root() recipe: Recipe) { - // ... - } -} -``` - -For enhanced type safety we can implement the `ResolverInterface` interface. -It's a small helper that checks if the return type of the field resolver methods, like `averageRating(...)`, matches the `averageRating` property of the `Recipe` class and whether the first parameter of the method is the actual object type (`Recipe` class). - -```typescript -@Resolver(of => Recipe) -class RecipeResolver implements ResolverInterface { - // queries and mutations - - @FieldResolver() - averageRating(@Root() recipe: Recipe) { - // ... - } -} -``` - -Here is the full implementation of the sample `averageRating` field resolver: - -```typescript -@Resolver(of => Recipe) -class RecipeResolver implements ResolverInterface { - // queries and mutations - - @FieldResolver() - averageRating(@Root() recipe: Recipe) { - const ratingsSum = recipe.ratings.reduce((a, b) => a + b, 0); - return recipe.ratings.length ? ratingsSum / recipe.ratings.length : null; - } -} -``` - -For simple resolvers like `averageRating` or deprecated fields that behave like aliases, you can create field resolvers inline in the object type class definition: - -```typescript -@ObjectType() -class Recipe { - @Field() - title: string; - - @Field({ deprecationReason: "Use `title` instead" }) - get name(): string { - return this.title; - } - - @Field(type => [Rate]) - ratings: Rate[]; - - @Field(type => Float, { nullable: true }) - averageRating(@Arg("since") sinceDate: Date): number | null { - const ratings = this.ratings.filter(rate => rate.date > sinceDate); - if (!ratings.length) return null; - - const ratingsSum = ratings.reduce((a, b) => a + b, 0); - return ratingsSum / ratings.length; - } -} -``` - -However, if the code is more complicated and has side effects (i.e. api calls, fetching data from a databases), a resolver class method should be used instead. This way we can leverage the dependency injection mechanism, which is really helpful in testing. For example: - -```typescript -import { Repository } from "typeorm"; - -@Resolver(of => Recipe) -class RecipeResolver implements ResolverInterface { - constructor( - private userRepository: Repository, // dependency injection - ) {} - - @FieldResolver() - async author(@Root() recipe: Recipe) { - const author = await this.userRepository.findById(recipe.userId); - if (!author) throw new SomethingWentWrongError(); - return author; - } -} -``` - -Note that if a field name of a field resolver doesn't exist in the resolver object type, it will create a field in the schema with this name. This feature is useful when the field is purely calculable (eg. `averageRating` from `ratings` array) and to avoid polluting the class signature. - -## Resolver Inheritance - -Resolver class `inheritance` is an advanced topic covered in the [resolver inheritance docs](inheritance.md#resolvers-inheritance). - -## Examples - -These code samples are just made up for tutorial purposes. -You can find more advanced, real examples in the [examples folder on the repository](https://github.com/MichalLytek/type-graphql/tree/master/examples). diff --git a/website/versioned_docs/version-1.0.0-rc.1/types-and-fields.md b/website/versioned_docs/version-1.0.0-rc.1/types-and-fields.md deleted file mode 100644 index 186199e27..000000000 --- a/website/versioned_docs/version-1.0.0-rc.1/types-and-fields.md +++ /dev/null @@ -1,129 +0,0 @@ ---- -title: Types and Fields -id: version-1.0.0-rc.1-types-and-fields -original_id: types-and-fields ---- - -The main idea of TypeGraphQL is to automatically create GraphQL schema definitions from TypeScript classes. To avoid the need for schema definition files and interfaces describing the schema, we use decorators and a bit of reflection magic. - -Let's start by defining our example TypeScript class which represents our `Recipe` model with fields for storing the recipe data: - -```typescript -class Recipe { - id: string; - title: string; - ratings: Rate[]; - averageRating?: number; -} -``` - -The first thing we must do is decorate the class with the `@ObjectType` decorator. It marks the class as the `type` known from the GraphQL SDL or `GraphQLObjectType` from `graphql-js`: - -```typescript -@ObjectType() -class Recipe { - id: string; - title: string; - ratings: Rate[]; - averageRating: number; -} -``` - -Then we declare which class properties should be mapped to the GraphQL fields. -To do this, we use the `@Field` decorator, which is also used to collect metadata from the TypeScript reflection system: - -```typescript -@ObjectType() -class Recipe { - @Field() - id: string; - - @Field() - title: string; - - @Field() - ratings: Rate[]; - - @Field() - averageRating: number; -} -``` - -For simple types (like `string` or `boolean`) this is all that's needed but due to a limitation in TypeScript's reflection, we need to provide info about generic types (like `Array` or `Promise`). So to declare the `Rate[]` type, we have two options available: - -- `@Field(type => [Rate])` (recommended, explicit `[ ]` syntax for Array types) -- `@Field(itemType => Rate)` (`array` is inferred from reflection - also works but is prone to errors) - -For nested arrays however, the explicit `[ ]` notation is required to determine the depth of the array. `@Field(type => [[Int]])` would tell the compiler we expect an integer array of depth 2. - -Why use function syntax and not a simple `{ type: Rate }` config object? Because, by using function syntax we solve the problem of circular dependencies (e.g. Post <--> User), so it was adopted as a convention. You can use the shorthand syntax `@Field(() => Rate)` if you want to save some keystrokes but it might be less readable for others. - -By default, all fields are non nullable, just like properties in TypeScript. However, you can change that behavior by providing `nullableByDefault: true` option in `buildSchema` settings, described in [bootstrap guide](./bootstrap.md). - -So for nullable properties like `averageRating` which might not be defined when a recipe has no ratings yet, we mark the class property as optional with a `?:` operator and also have to pass the `{ nullable: true }` decorator parameter. We should be aware that when we declare our type as a nullable union (e.g. `string | null`), we need to explicitly provide the type to the `@Field` decorator. - -In the case of lists, we may also need to define their nullability in a more detailed form. The basic `{ nullable: true | false }` setting only applies to the whole list (`[Item!]` or `[Item!]!`), so if we need a sparse array, we can control the list items' nullability via `nullable: "items"` (for `[Item]!`) or `nullable: "itemsAndList"` (for the `[Item]`) option. Be aware that setting `nullableByDefault: true` option will also apply to lists, so it will produce `[Item]` type, just like with `nullable: "itemsAndList"`. - -For nested lists, those options apply to the whole depth of the array: `@Field(() => [[Item]]` would by defaut produce `[[Item!]!]!`, setting `nullable: "itemsAndList"` would produce `[[Item]]` while `nullable: "items"` would produce `[[Item]]!` - -In the config object we can also provide the `description` and `deprecationReason` properties for GraphQL schema purposes. - -So after these changes our example class would look like this: - -```typescript -@ObjectType({ description: "The recipe model" }) -class Recipe { - @Field(type => ID) - id: string; - - @Field({ description: "The title of the recipe" }) - title: string; - - @Field(type => [Rate]) - ratings: Rate[]; - - @Field({ nullable: true }) - averageRating?: number; -} -``` - -Which will result in generating the following part of the GraphQL schema in SDL: - -```graphql -type Recipe { - id: ID! - title: String! - ratings: [Rate!]! - averageRating: Float -} -``` - -Similarly, the `Rate` type class would look like this: - -```typescript -@ObjectType() -class Rate { - @Field(type => Int) - value: number; - - @Field() - date: Date; - - user: User; -} -``` - -which results in this equivalent of the GraphQL SDL: - -```graphql -type Rate { - value: Int! - date: Date! -} -``` - -As we can see, for the `id` property of `Recipe` we passed `type => ID` and for the `value` field of `Rate` we passed `type => Int`. This way we can overwrite the inferred type from the reflection metadata. We can read more about the ID and Int scalars in [the scalars docs](scalars.md). There is also a section about the built-in `Date` scalar. - -Also the `user` property doesn't have a `@Field()` decorator - this way we can hide some properties of our data model. In this case, we need to store the `user` field of the `Rate` object to the database in order to prevent multiple rates, but we don't want to make it publicly accessible. - -Note that if a field of an object type is purely calculable (e.g. `averageRating` from `ratings` array) and we don't want to pollute the class signature, we can omit it and just implement the field resolver (described in [resolvers doc](resolvers.md)). diff --git a/website/versioned_docs/version-1.0.0-rc.3/examples.md b/website/versioned_docs/version-1.0.0-rc.3/examples.md deleted file mode 100644 index 3cdad7e14..000000000 --- a/website/versioned_docs/version-1.0.0-rc.3/examples.md +++ /dev/null @@ -1,48 +0,0 @@ ---- -title: Examples -sidebar_label: List of examples -id: version-1.0.0-rc.3-examples -original_id: examples ---- - -On the [GitHub repository](https://github.com/MichalLytek/type-graphql) there are a few simple examples of how to use different TypeGraphQL features and how well they integrate with 3rd party libraries. - -All examples have an `examples.gql` file with sample queries/mutations/subscriptions that we can execute. - -## Basics - -- [Simple usage of fields, basic types and resolvers](https://github.com/MichalLytek/type-graphql/tree/master/examples/simple-usage) - -## Advanced - -- [Enums and unions](https://github.com/MichalLytek/type-graphql/tree/master/examples/enums-and-unions) -- [Subscriptions (simple)](https://github.com/MichalLytek/type-graphql/tree/master/examples/simple-subscriptions) -- [Subscriptions (using Redis)](https://github.com/MichalLytek/type-graphql/tree/master/examples/redis-subscriptions) -- [Interfaces](https://github.com/MichalLytek/type-graphql/tree/master/examples/interfaces-inheritance) -- [Extensions (metadata)](https://github.com/MichalLytek/type-graphql/tree/master/examples/extensions) - -## Features usage - -- [Dependency injection (IoC container)](https://github.com/MichalLytek/type-graphql/tree/master/examples/using-container) - - [Scoped containers](https://github.com/MichalLytek/type-graphql/tree/master/examples/using-scoped-container) -- [Authorization](https://github.com/MichalLytek/type-graphql/tree/master/examples/authorization) -- [Validation](https://github.com/MichalLytek/type-graphql/tree/master/examples/automatic-validation) -- [Types inheritance](https://github.com/MichalLytek/type-graphql/tree/master/examples/interfaces-inheritance) -- [Resolvers inheritance](https://github.com/MichalLytek/type-graphql/tree/master/examples/resolvers-inheritance) -- [Generic types](https://github.com/MichalLytek/type-graphql/tree/master/examples/generic-types) -- [Mixin classes](https://github.com/MichalLytek/type-graphql/tree/master/examples/mixin-classes) -- [Middlewares and Custom Decorators](https://github.com/MichalLytek/type-graphql/tree/master/examples/middlewares-custom-decorators) -- [Query complexity](https://github.com/MichalLytek/type-graphql/tree/master/examples/query-complexity) - -## 3rd party libs integration - -- [TypeORM (manual, synchronous) \*](https://github.com/MichalLytek/type-graphql/tree/master/examples/typeorm-basic-usage) -- [TypeORM (automatic, lazy relations) \*](https://github.com/MichalLytek/type-graphql/tree/master/examples/typeorm-lazy-relations) -- [Typegoose](https://github.com/MichalLytek/type-graphql/tree/master/examples/typegoose) -- [Apollo federation](https://github.com/MichalLytek/type-graphql/tree/master/examples/apollo-federation) -- [Apollo Engine (Apollo Cache Control) \*\*](https://github.com/MichalLytek/type-graphql/tree/master/examples/apollo-engine) -- [Apollo client state](https://github.com/MichalLytek/type-graphql/tree/master/examples/apollo-client) - -_\* Note that we need to edit the TypeORM example's `index.ts` with the credentials of our local database_ - -_\*\* Note that we need to provide an `APOLLO_ENGINE_API_KEY` env variable with our own API key_ diff --git a/website/versioned_docs/version-1.0.0-rc.3/bootstrap.md b/website/versioned_docs/version-1.0.0/bootstrap.md similarity index 99% rename from website/versioned_docs/version-1.0.0-rc.3/bootstrap.md rename to website/versioned_docs/version-1.0.0/bootstrap.md index 63de071ce..a561fbaea 100644 --- a/website/versioned_docs/version-1.0.0-rc.3/bootstrap.md +++ b/website/versioned_docs/version-1.0.0/bootstrap.md @@ -1,6 +1,6 @@ --- title: Bootstrapping -id: version-1.0.0-rc.3-bootstrap +id: version-1.0.0-bootstrap original_id: bootstrap --- diff --git a/website/versioned_docs/version-1.0.0-rc.3/browser-usage.md b/website/versioned_docs/version-1.0.0/browser-usage.md similarity index 98% rename from website/versioned_docs/version-1.0.0-rc.3/browser-usage.md rename to website/versioned_docs/version-1.0.0/browser-usage.md index d3b1025de..f43c26c0d 100644 --- a/website/versioned_docs/version-1.0.0-rc.3/browser-usage.md +++ b/website/versioned_docs/version-1.0.0/browser-usage.md @@ -1,6 +1,6 @@ --- title: Browser usage -id: version-1.0.0-rc.3-browser-usage +id: version-1.0.0-browser-usage original_id: browser-usage --- diff --git a/website/versioned_docs/version-1.0.0-rc.1/complexity.md b/website/versioned_docs/version-1.0.0/complexity.md similarity index 99% rename from website/versioned_docs/version-1.0.0-rc.1/complexity.md rename to website/versioned_docs/version-1.0.0/complexity.md index 3332434cf..bd0dd1a92 100644 --- a/website/versioned_docs/version-1.0.0-rc.1/complexity.md +++ b/website/versioned_docs/version-1.0.0/complexity.md @@ -1,6 +1,6 @@ --- title: Query complexity -id: version-1.0.0-rc.1-complexity +id: version-1.0.0-complexity original_id: complexity --- diff --git a/website/versioned_docs/version-1.0.0-rc.1/custom-decorators.md b/website/versioned_docs/version-1.0.0/custom-decorators.md similarity index 98% rename from website/versioned_docs/version-1.0.0-rc.1/custom-decorators.md rename to website/versioned_docs/version-1.0.0/custom-decorators.md index 1aff8e0f2..500c8ecaa 100644 --- a/website/versioned_docs/version-1.0.0-rc.1/custom-decorators.md +++ b/website/versioned_docs/version-1.0.0/custom-decorators.md @@ -1,6 +1,6 @@ --- title: Custom decorators -id: version-1.0.0-rc.1-custom-decorators +id: version-1.0.0-custom-decorators original_id: custom-decorators --- diff --git a/website/versioned_docs/version-1.0.0-rc.1/dependency-injection.md b/website/versioned_docs/version-1.0.0/dependency-injection.md similarity index 99% rename from website/versioned_docs/version-1.0.0-rc.1/dependency-injection.md rename to website/versioned_docs/version-1.0.0/dependency-injection.md index 529f8f4a4..75ca1a35d 100644 --- a/website/versioned_docs/version-1.0.0-rc.1/dependency-injection.md +++ b/website/versioned_docs/version-1.0.0/dependency-injection.md @@ -1,6 +1,6 @@ --- title: Dependency injection -id: version-1.0.0-rc.1-dependency-injection +id: version-1.0.0-dependency-injection original_id: dependency-injection --- diff --git a/website/versioned_docs/version-1.0.0-rc.1/directives.md b/website/versioned_docs/version-1.0.0/directives.md similarity index 99% rename from website/versioned_docs/version-1.0.0-rc.1/directives.md rename to website/versioned_docs/version-1.0.0/directives.md index e9b84c70e..6b065b30a 100644 --- a/website/versioned_docs/version-1.0.0-rc.1/directives.md +++ b/website/versioned_docs/version-1.0.0/directives.md @@ -1,6 +1,6 @@ --- title: Directives -id: version-1.0.0-rc.1-directives +id: version-1.0.0-directives original_id: directives --- diff --git a/website/versioned_docs/version-1.0.0-rc.1/emit-schema.md b/website/versioned_docs/version-1.0.0/emit-schema.md similarity index 98% rename from website/versioned_docs/version-1.0.0-rc.1/emit-schema.md rename to website/versioned_docs/version-1.0.0/emit-schema.md index fdf61bd25..bfde890fe 100644 --- a/website/versioned_docs/version-1.0.0-rc.1/emit-schema.md +++ b/website/versioned_docs/version-1.0.0/emit-schema.md @@ -1,6 +1,6 @@ --- title: Emitting the schema SDL -id: version-1.0.0-rc.1-emit-schema +id: version-1.0.0-emit-schema original_id: emit-schema --- diff --git a/website/versioned_docs/version-1.0.0-rc.2/examples.md b/website/versioned_docs/version-1.0.0/examples.md similarity index 63% rename from website/versioned_docs/version-1.0.0-rc.2/examples.md rename to website/versioned_docs/version-1.0.0/examples.md index 8b24d1bb3..bfa8ef9db 100644 --- a/website/versioned_docs/version-1.0.0-rc.2/examples.md +++ b/website/versioned_docs/version-1.0.0/examples.md @@ -1,7 +1,7 @@ --- title: Examples sidebar_label: List of examples -id: version-1.0.0-rc.2-examples +id: version-1.0.0-examples original_id: examples --- @@ -11,37 +11,38 @@ All examples have an `examples.gql` file with sample queries/mutations/subscript ## Basics -- [Simple usage of fields, basic types and resolvers](https://github.com/MichalLytek/type-graphql/tree/v1.0.0-rc.2/examples/simple-usage) +- [Simple usage of fields, basic types and resolvers](https://github.com/MichalLytek/type-graphql/tree/v1.0.0/examples/simple-usage) ## Advanced -- [Enums and unions](https://github.com/MichalLytek/type-graphql/tree/v1.0.0-rc.2/examples/enums-and-unions) -- [Subscriptions (simple)](https://github.com/MichalLytek/type-graphql/tree/v1.0.0-rc.2/examples/simple-subscriptions) -- [Subscriptions (using Redis)](https://github.com/MichalLytek/type-graphql/tree/v1.0.0-rc.2/examples/redis-subscriptions) -- [Interfaces](https://github.com/MichalLytek/type-graphql/tree/v1.0.0-rc.2/examples/interfaces-inheritance) -- [Extensions (metadata)](https://github.com/MichalLytek/type-graphql/tree/v1.0.0-rc.2/examples/extensions) +- [Enums and unions](https://github.com/MichalLytek/type-graphql/tree/v1.0.0/examples/enums-and-unions) +- [Subscriptions (simple)](https://github.com/MichalLytek/type-graphql/tree/v1.0.0/examples/simple-subscriptions) +- [Subscriptions (using Redis)](https://github.com/MichalLytek/type-graphql/tree/v1.0.0/examples/redis-subscriptions) +- [Interfaces](https://github.com/MichalLytek/type-graphql/tree/v1.0.0/examples/interfaces-inheritance) +- [Extensions (metadata)](https://github.com/MichalLytek/type-graphql/tree/v1.0.0/examples/extensions) ## Features usage -- [Dependency injection (IoC container)](https://github.com/MichalLytek/type-graphql/tree/v1.0.0-rc.2/examples/using-container) - - [Scoped containers](https://github.com/MichalLytek/type-graphql/tree/v1.0.0-rc.2/examples/using-scoped-container) -- [Authorization](https://github.com/MichalLytek/type-graphql/tree/v1.0.0-rc.2/examples/authorization) -- [Validation](https://github.com/MichalLytek/type-graphql/tree/v1.0.0-rc.2/examples/automatic-validation) -- [Types inheritance](https://github.com/MichalLytek/type-graphql/tree/v1.0.0-rc.2/examples/interfaces-inheritance) -- [Resolvers inheritance](https://github.com/MichalLytek/type-graphql/tree/v1.0.0-rc.2/examples/resolvers-inheritance) -- [Generic types](https://github.com/MichalLytek/type-graphql/tree/v1.0.0-rc.2/examples/generic-types) -- [Mixin classes](https://github.com/MichalLytek/type-graphql/tree/v1.0.0-rc.2/examples/mixin-classes) -- [Middlewares and Custom Decorators](https://github.com/MichalLytek/type-graphql/tree/v1.0.0-rc.2/examples/middlewares-custom-decorators) -- [Query complexity](https://github.com/MichalLytek/type-graphql/tree/v1.0.0-rc.2/examples/query-complexity) +- [Dependency injection (IoC container)](https://github.com/MichalLytek/type-graphql/tree/v1.0.0/examples/using-container) + - [Scoped containers](https://github.com/MichalLytek/type-graphql/tree/v1.0.0/examples/using-scoped-container) +- [Authorization](https://github.com/MichalLytek/type-graphql/tree/v1.0.0/examples/authorization) +- [Validation](https://github.com/MichalLytek/type-graphql/tree/v1.0.0/examples/automatic-validation) +- [Types inheritance](https://github.com/MichalLytek/type-graphql/tree/v1.0.0/examples/interfaces-inheritance) +- [Resolvers inheritance](https://github.com/MichalLytek/type-graphql/tree/v1.0.0/examples/resolvers-inheritance) +- [Generic types](https://github.com/MichalLytek/type-graphql/tree/v1.0.0/examples/generic-types) +- [Mixin classes](https://github.com/MichalLytek/type-graphql/tree/v1.0.0/examples/mixin-classes) +- [Middlewares and Custom Decorators](https://github.com/MichalLytek/type-graphql/tree/v1.0.0/examples/middlewares-custom-decorators) +- [Query complexity](https://github.com/MichalLytek/type-graphql/tree/v1.0.0/examples/query-complexity) ## 3rd party libs integration -- [TypeORM (manual, synchronous) \*](https://github.com/MichalLytek/type-graphql/tree/v1.0.0-rc.2/examples/typeorm-basic-usage) -- [TypeORM (automatic, lazy relations) \*](https://github.com/MichalLytek/type-graphql/tree/v1.0.0-rc.2/examples/typeorm-lazy-relations) -- [Typegoose](https://github.com/MichalLytek/type-graphql/tree/v1.0.0-rc.2/examples/typegoose) -- [Apollo federation](https://github.com/MichalLytek/type-graphql/tree/v1.0.0-rc.2/examples/apollo-federation) -- [Apollo Engine (Apollo Cache Control) \*\*](https://github.com/MichalLytek/type-graphql/tree/v1.0.0-rc.2/examples/apollo-engine) -- [Apollo client state](https://github.com/MichalLytek/type-graphql/tree/v1.0.0-rc.2/examples/apollo-client) +- [TypeORM (manual, synchronous) \*](https://github.com/MichalLytek/type-graphql/tree/v1.0.0/examples/typeorm-basic-usage) +- [TypeORM (automatic, lazy relations) \*](https://github.com/MichalLytek/type-graphql/tree/v1.0.0/examples/typeorm-lazy-relations) +- [Typegoose](https://github.com/MichalLytek/type-graphql/tree/v1.0.0/examples/typegoose) +- [Apollo federation](https://github.com/MichalLytek/type-graphql/tree/v1.0.0/examples/apollo-federation) +- [Apollo Engine (Apollo Cache Control) \*\*](https://github.com/MichalLytek/type-graphql/tree/v1.0.0/examples/apollo-engine) +- [Apollo client state](https://github.com/MichalLytek/type-graphql/tree/v1.0.0/examples/apollo-client) +- [GraphQL Modules](https://github.com/MichalLytek/type-graphql/tree/v1.0.0/examples/graphql-modules) _\* Note that we need to edit the TypeORM example's `index.ts` with the credentials of our local database_ diff --git a/website/versioned_docs/version-1.0.0-rc.1/extensions.md b/website/versioned_docs/version-1.0.0/extensions.md similarity index 99% rename from website/versioned_docs/version-1.0.0-rc.1/extensions.md rename to website/versioned_docs/version-1.0.0/extensions.md index e4348ac20..009c5b49d 100644 --- a/website/versioned_docs/version-1.0.0-rc.1/extensions.md +++ b/website/versioned_docs/version-1.0.0/extensions.md @@ -1,6 +1,6 @@ --- title: Extensions -id: version-1.0.0-rc.1-extensions +id: version-1.0.0-extensions original_id: extensions --- diff --git a/website/versioned_docs/version-1.0.0-rc.1/generic-types.md b/website/versioned_docs/version-1.0.0/generic-types.md similarity index 78% rename from website/versioned_docs/version-1.0.0-rc.1/generic-types.md rename to website/versioned_docs/version-1.0.0/generic-types.md index 354ad8dd0..d273a505a 100644 --- a/website/versioned_docs/version-1.0.0-rc.1/generic-types.md +++ b/website/versioned_docs/version-1.0.0/generic-types.md @@ -1,6 +1,6 @@ --- title: Generic Types -id: version-1.0.0-rc.1-generic-types +id: version-1.0.0-generic-types original_id: generic-types --- @@ -12,6 +12,8 @@ Hence TypeGraphQL also has support for describing generic GraphQL types. Unfortunately, the limited reflection capabilities of TypeScript don't allow for combining decorators with standard generic classes. To achieve behavior like that of generic types, we use the same class-creator pattern like the one described in the [Resolvers Inheritance](inheritance.md) docs. +### Basic usage + Start by defining a `PaginatedResponse` function that creates and returns a `PaginatedResponseClass`: ```typescript @@ -99,6 +101,39 @@ class UserResolver { } ``` +### Complex generic type values + +When we need to provide something different than a class (object type) for the field type, we need to enhance the parameter type signature and provide the needed types. + +Basically, the parameter that the `PaginatedResponse` function accepts is the value we can provide to `@Field` decorator. +So if we want to return an array of strings as the `items` field, we need to add proper types to the function signature, like `GraphQLScalarType` or `String`: + +```typescript +export default function PaginatedResponse( + itemsFieldValue: ClassType | GraphQLScalarType | String | Number | Boolean, +) { + @ObjectType({ isAbstract: true }) + abstract class PaginatedResponseClass { + @Field(type => [itemsFieldValue]) + items: TItemsFieldValue[]; + + // ...other fields + } + return PaginatedResponseClass; +} +``` + +And then provide a proper runtime value (like `String`) while creating a proper subtype of generic `PaginatedResponse` object type: + +```typescript +@ObjectType() +class PaginatedStringsResponse extends PaginatedResponse(String) { + // ... +} +``` + +### Types factory + We can also create a generic class without using the `isAbstract` option or the `abstract` keyword. But types created with this kind of factory will be registered in the schema, so this way is not recommended to extend the types for adding fields. diff --git a/website/versioned_docs/version-1.0.0-rc.1/getting-started.md b/website/versioned_docs/version-1.0.0/getting-started.md similarity index 99% rename from website/versioned_docs/version-1.0.0-rc.1/getting-started.md rename to website/versioned_docs/version-1.0.0/getting-started.md index fd86a984b..680a5d57f 100644 --- a/website/versioned_docs/version-1.0.0-rc.1/getting-started.md +++ b/website/versioned_docs/version-1.0.0/getting-started.md @@ -1,6 +1,6 @@ --- title: Getting started -id: version-1.0.0-rc.1-getting-started +id: version-1.0.0-getting-started original_id: getting-started --- diff --git a/website/versioned_docs/version-1.0.0-rc.1/installation.md b/website/versioned_docs/version-1.0.0/installation.md similarity index 98% rename from website/versioned_docs/version-1.0.0-rc.1/installation.md rename to website/versioned_docs/version-1.0.0/installation.md index 799cb6b8a..067b8ccbd 100644 --- a/website/versioned_docs/version-1.0.0-rc.1/installation.md +++ b/website/versioned_docs/version-1.0.0/installation.md @@ -1,6 +1,6 @@ --- title: Installation -id: version-1.0.0-rc.1-installation +id: version-1.0.0-installation original_id: installation --- diff --git a/website/versioned_docs/version-1.0.0-rc.1/interfaces.md b/website/versioned_docs/version-1.0.0/interfaces.md similarity index 99% rename from website/versioned_docs/version-1.0.0-rc.1/interfaces.md rename to website/versioned_docs/version-1.0.0/interfaces.md index bcd8e2876..2254cae14 100644 --- a/website/versioned_docs/version-1.0.0-rc.1/interfaces.md +++ b/website/versioned_docs/version-1.0.0/interfaces.md @@ -1,6 +1,6 @@ --- title: Interfaces -id: version-1.0.0-rc.1-interfaces +id: version-1.0.0-interfaces original_id: interfaces --- diff --git a/website/versioned_docs/version-1.0.0-rc.1/middlewares.md b/website/versioned_docs/version-1.0.0/middlewares.md similarity index 99% rename from website/versioned_docs/version-1.0.0-rc.1/middlewares.md rename to website/versioned_docs/version-1.0.0/middlewares.md index b736f7341..4fb96ce74 100644 --- a/website/versioned_docs/version-1.0.0-rc.1/middlewares.md +++ b/website/versioned_docs/version-1.0.0/middlewares.md @@ -1,6 +1,6 @@ --- title: Middleware and guards -id: version-1.0.0-rc.1-middlewares +id: version-1.0.0-middlewares original_id: middlewares --- diff --git a/website/versioned_docs/version-1.0.0/nestjs.md b/website/versioned_docs/version-1.0.0/nestjs.md new file mode 100644 index 000000000..4b870cded --- /dev/null +++ b/website/versioned_docs/version-1.0.0/nestjs.md @@ -0,0 +1,50 @@ +--- +title: NestJS Integration +sidebar_label: NestJS +id: version-1.0.0-nestjs +original_id: nestjs +--- + +TypeGraphQL provides some basic integration with NestJS by the [`typegraphql-nestjs` package](https://www.npmjs.com/package/typegraphql-nestjs). + +It allows to use TypeGraphQL features while integrating with NestJS modules system and its dependency injector. + +## Overview + +The usage is similar to the official `@nestjs/graphql` package. +First you need to register your resolver classes in `providers` of the `@Module` : + +```typescript +@Module({ + providers: [RecipeResolver, RecipeService], +}) +export default class RecipeModule {} +``` + +Then you need to register the TypeGraphQL module in your root module - you can pass there all standard `buildSchema` options: + +```typescript +@Module({ + imports: [ + TypeGraphQLModule.forRoot({ + emitSchemaFile: true, + authChecker, + dateScalarMode: "timestamp", + context: ({ req }) => ({ currentUser: req.user }), + }), + RecipeModule, + ], +}) +export default class AppModule {} +``` + +And your `AppModule` is ready to use like with a standard NestJS approach. + +### Caveats + +For now, this basic integration doesn't support other NestJS features like guards, interceptors, filters and pipes. +To achieve the same goals, you can use standard TypeGraphQL equivalents - middlewares, custom decorators, built-in authorization and validation. + +## Documentation and examples + +You can find some examples and more detailed info about the installation and the usage [in the separate GitHub repository](https://github.com/MichalLytek/typegraphql-nestjs/). diff --git a/website/versioned_docs/version-1.0.0-rc.1/performance.md b/website/versioned_docs/version-1.0.0/performance.md similarity index 99% rename from website/versioned_docs/version-1.0.0-rc.1/performance.md rename to website/versioned_docs/version-1.0.0/performance.md index c974093f2..adc55bf9a 100644 --- a/website/versioned_docs/version-1.0.0-rc.1/performance.md +++ b/website/versioned_docs/version-1.0.0/performance.md @@ -1,6 +1,6 @@ --- title: Performance -id: version-1.0.0-rc.1-performance +id: version-1.0.0-performance original_id: performance --- diff --git a/website/versioned_docs/version-1.0.0/prisma.md b/website/versioned_docs/version-1.0.0/prisma.md new file mode 100644 index 000000000..cec0379c5 --- /dev/null +++ b/website/versioned_docs/version-1.0.0/prisma.md @@ -0,0 +1,55 @@ +--- +title: Prisma 2 Integration +sidebar_label: Prisma 2 +id: version-1.0.0-prisma +original_id: prisma +--- + +TypeGraphQL provides an integration with Prisma 2 by the [`typegraphql-prisma` package](https://www.npmjs.com/package/typegraphql-prisma). + +It generates the type classes and CRUD resolvers based on the Prisma schema, so you can execute complex queries or mutations that corresponds to the Prisma actions, without having to write any code for that. + +## Overview + +To make use of the prisma integration, first you need to add a new generator to the `schema.prisma` file: + +```sh +generator typegraphql { + provider = "node node_modules/typegraphql-prisma/generator.js" + output = "../src/generated/typegraphql-prisma" +} +``` + +Then, after running `prisma generate` you can import the generated classes and use them to build your schema: + +```typescript +import { User, UserRelationsResolver, UserCrudResolver } from "./generated/typegraphql-prisma"; + +const schema = await buildSchema({ + resolvers: [CustomUserResolver, UserRelationsResolver, UserCrudResolver], + validate: false, +}); +``` + +So you will be able to execute such complex query that talks with the db in just a few minutes! + +```graphql +query GetSomeUsers { + users(where: { email: { contains: "prisma" } }, orderBy: { name: desc }) { + id + name + email + posts(take: 10, orderBy: { updatedAt: desc }) { + published + title + content + } + } +} +``` + +## Documentation and examples + +To read about all the `typegraphql-prisma` features, like exposing selected Prisma actions or changing exposed model type name, as well as how to write a custom query or how to add some fields to model type, please check the docs [on the separate branch of the main GitHub repository](https://github.com/MichalLytek/type-graphql/tree/prisma). + +You can find there also some examples and more detailed info about the installation and the configuration. diff --git a/website/versioned_docs/version-1.0.0-rc.3/resolvers.md b/website/versioned_docs/version-1.0.0/resolvers.md similarity index 99% rename from website/versioned_docs/version-1.0.0-rc.3/resolvers.md rename to website/versioned_docs/version-1.0.0/resolvers.md index 6b296bd36..474a7e00e 100644 --- a/website/versioned_docs/version-1.0.0-rc.3/resolvers.md +++ b/website/versioned_docs/version-1.0.0/resolvers.md @@ -1,6 +1,6 @@ --- title: Resolvers -id: version-1.0.0-rc.3-resolvers +id: version-1.0.0-resolvers original_id: resolvers --- diff --git a/website/versioned_docs/version-1.0.0/scalars.md b/website/versioned_docs/version-1.0.0/scalars.md new file mode 100644 index 000000000..0faa450cc --- /dev/null +++ b/website/versioned_docs/version-1.0.0/scalars.md @@ -0,0 +1,164 @@ +--- +title: Scalars +id: version-1.0.0-scalars +original_id: scalars +--- + +## Aliases + +TypeGraphQL provides aliases for 3 basic scalars: + +- Int --> GraphQLInt; +- Float --> GraphQLFloat; +- ID --> GraphQLID; + +This shorthand allows you to save keystrokes when declaring field types: + +```typescript +// import the aliases +import { ID, Float, Int } from "type-graphql"; + +@ObjectType() +class MysteryObject { + @Field(type => ID) + readonly id: string; + + @Field(type => Int) + notificationsCount: number; + + @Field(type => Float) + probability: number; +} +``` + +In the last case you can omit the `type => Float` since JavaScript `Number` will become `GraphQLFloat` in the schema automatically. + +Other scalars - i.e. `GraphQLString` and `GraphQLBoolean` - do not need aliases. When possible, they will be reflected automatically: + +```typescript +@ObjectType() +class User { + @Field() + name: string; + + @Field() + isOld: boolean; +} +``` + +However in some cases we must explicitly declare the string/bool scalar type. Use JS constructor functions (`String`, `Boolean`) then: + +```typescript +@ObjectType() +class SampleObject { + @Field(type => String, { nullable: true }) + // TS reflected type is `Object` :( + get optionalInfo(): string | undefined { + if (Math.random() > 0.5) { + return "Gotcha!"; + } + } +} +``` + +## Date Scalars + +TypeGraphQL provides built-in scalars for the `Date` type. There are two versions of this scalar: + +- timestamp based (`"timestamp"`) - `1518037458374` +- ISO format (`"isoDate"`) - `"2018-02-07T21:04:39.573Z"` + +They are exported from the `type-graphql` package as `GraphQLISODateTime` and `GraphQLTimestamp`. + +By default, TypeGraphQL uses the ISO date format, however you can change it in the `buildSchema` options: + +```typescript +import { buildSchema } from "type-graphql"; + +const schema = await buildSchema({ + resolvers, + dateScalarMode: "timestamp", // "timestamp" or "isoDate" +}); +``` + +There's no need then to explicitly declare the field type: + +```typescript +@ObjectType() +class User { + @Field() + registrationDate: Date; +} +``` + +## Custom Scalars + +TypeGraphQL also supports custom scalar types! + +First of all, we need to create our own `GraphQLScalarType` instance or import a scalar type from a 3rd-party npm library. For example, Mongo's ObjectId: + +```typescript +import { GraphQLScalarType, Kind } from "graphql"; +import { ObjectId } from "mongodb"; + +export const ObjectIdScalar = new GraphQLScalarType({ + name: "ObjectId", + description: "Mongo object id scalar type", + parseValue(value: string) { + return new ObjectId(value); // value from the client input variables + }, + serialize(value: ObjectId) { + return value.toHexString(); // value sent to the client + }, + parseLiteral(ast) { + if (ast.kind === Kind.STRING) { + return new ObjectId(ast.value); // value from the client query + } + return null; + }, +}); +``` + +Then we can just use it in our field decorators: + +```typescript +// import the earlier created const +import { ObjectIdScalar } from "../my-scalars/ObjectId"; + +@ObjectType() +class User { + @Field(type => ObjectIdScalar) // and explicitly use it + readonly id: ObjectId; + + @Field() + name: string; + + @Field() + isOld: boolean; +} +``` + +Optionally, we can declare the association between the reflected property type and our scalars to automatically map them (no need for explicit type annotation!): + +```typescript +@ObjectType() +class User { + @Field() // magic goes here - no type annotation for custom scalar + readonly id: ObjectId; +} +``` + +All we need to do is register the association map in the `buildSchema` options: + +```typescript +import { ObjectId } from "mongodb"; +import { ObjectIdScalar } from "../my-scalars/ObjectId"; +import { buildSchema } from "type-graphql"; + +const schema = await buildSchema({ + resolvers, + scalarsMap: [{ type: ObjectId, scalar: ObjectIdScalar }], +}); +``` + +However, we must be aware that this will only work when the TypeScript reflection mechanism can handle it. So our class property type must be a `class`, not an enum, union or interface. diff --git a/website/versioned_docs/version-1.0.0-rc.1/subscriptions.md b/website/versioned_docs/version-1.0.0/subscriptions.md similarity index 99% rename from website/versioned_docs/version-1.0.0-rc.1/subscriptions.md rename to website/versioned_docs/version-1.0.0/subscriptions.md index d7f6155d4..7a6aafb8c 100644 --- a/website/versioned_docs/version-1.0.0-rc.1/subscriptions.md +++ b/website/versioned_docs/version-1.0.0/subscriptions.md @@ -1,6 +1,6 @@ --- title: Subscriptions -id: version-1.0.0-rc.1-subscriptions +id: version-1.0.0-subscriptions original_id: subscriptions --- diff --git a/website/versioned_docs/version-1.0.0-rc.3/types-and-fields.md b/website/versioned_docs/version-1.0.0/types-and-fields.md similarity index 99% rename from website/versioned_docs/version-1.0.0-rc.3/types-and-fields.md rename to website/versioned_docs/version-1.0.0/types-and-fields.md index df038a123..9cdc48663 100644 --- a/website/versioned_docs/version-1.0.0-rc.3/types-and-fields.md +++ b/website/versioned_docs/version-1.0.0/types-and-fields.md @@ -1,6 +1,6 @@ --- title: Types and Fields -id: version-1.0.0-rc.3-types-and-fields +id: version-1.0.0-types-and-fields original_id: types-and-fields --- diff --git a/website/versioned_docs/version-1.0.0-rc.1/unions.md b/website/versioned_docs/version-1.0.0/unions.md similarity index 99% rename from website/versioned_docs/version-1.0.0-rc.1/unions.md rename to website/versioned_docs/version-1.0.0/unions.md index d2e5b7382..112a135a0 100644 --- a/website/versioned_docs/version-1.0.0-rc.1/unions.md +++ b/website/versioned_docs/version-1.0.0/unions.md @@ -1,6 +1,6 @@ --- title: Unions -id: version-1.0.0-rc.1-unions +id: version-1.0.0-unions original_id: unions --- diff --git a/website/versioned_docs/version-1.0.0-rc.1/validation.md b/website/versioned_docs/version-1.0.0/validation.md similarity index 99% rename from website/versioned_docs/version-1.0.0-rc.1/validation.md rename to website/versioned_docs/version-1.0.0/validation.md index 5d7b303fe..926cec37e 100644 --- a/website/versioned_docs/version-1.0.0-rc.1/validation.md +++ b/website/versioned_docs/version-1.0.0/validation.md @@ -1,7 +1,7 @@ --- title: Argument and Input validation sidebar_label: Validation -id: version-1.0.0-rc.1-validation +id: version-1.0.0-validation original_id: validation --- diff --git a/website/versioned_sidebars/version-1.0.0-rc.1-sidebars.json b/website/versioned_sidebars/version-1.0.0-rc.1-sidebars.json deleted file mode 100644 index 15af70a07..000000000 --- a/website/versioned_sidebars/version-1.0.0-rc.1-sidebars.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "version-1.0.0-rc.1-docs": { - "Introduction": [ - "version-1.0.0-rc.1-introduction" - ], - "Beginner guides": [ - "version-1.0.0-rc.1-installation", - "version-1.0.0-rc.1-getting-started", - "version-1.0.0-rc.1-types-and-fields", - "version-1.0.0-rc.1-resolvers", - "version-1.0.0-rc.1-bootstrap" - ], - "Advanced guides": [ - "version-1.0.0-rc.1-scalars", - "version-1.0.0-rc.1-enums", - "version-1.0.0-rc.1-unions", - "version-1.0.0-rc.1-interfaces", - "version-1.0.0-rc.1-subscriptions", - "version-1.0.0-rc.1-directives", - "version-1.0.0-rc.1-extensions" - ], - "Features": [ - "version-1.0.0-rc.1-dependency-injection", - "version-1.0.0-rc.1-authorization", - "version-1.0.0-rc.1-validation", - "version-1.0.0-rc.1-inheritance", - "version-1.0.0-rc.1-generic-types", - "version-1.0.0-rc.1-middlewares", - "version-1.0.0-rc.1-custom-decorators", - "version-1.0.0-rc.1-complexity" - ], - "Others": [ - "version-1.0.0-rc.1-emit-schema", - "version-1.0.0-rc.1-performance", - "version-1.0.0-rc.1-browser-usage" - ] - }, - "version-1.0.0-rc.1-examples": { - "Examples": [ - "version-1.0.0-rc.1-examples" - ] - }, - "version-1.0.0-rc.1-others": { - "Others": [ - "version-1.0.0-rc.1-faq" - ] - } -} diff --git a/website/versioned_sidebars/version-1.0.0-sidebars.json b/website/versioned_sidebars/version-1.0.0-sidebars.json new file mode 100644 index 000000000..0952e2858 --- /dev/null +++ b/website/versioned_sidebars/version-1.0.0-sidebars.json @@ -0,0 +1,52 @@ +{ + "version-1.0.0-docs": { + "Introduction": [ + "version-1.0.0-introduction" + ], + "Beginner guides": [ + "version-1.0.0-installation", + "version-1.0.0-getting-started", + "version-1.0.0-types-and-fields", + "version-1.0.0-resolvers", + "version-1.0.0-bootstrap" + ], + "Advanced guides": [ + "version-1.0.0-scalars", + "version-1.0.0-enums", + "version-1.0.0-unions", + "version-1.0.0-interfaces", + "version-1.0.0-subscriptions", + "version-1.0.0-directives", + "version-1.0.0-extensions" + ], + "Features": [ + "version-1.0.0-dependency-injection", + "version-1.0.0-authorization", + "version-1.0.0-validation", + "version-1.0.0-inheritance", + "version-1.0.0-generic-types", + "version-1.0.0-middlewares", + "version-1.0.0-custom-decorators", + "version-1.0.0-complexity" + ], + "Integrations": [ + "version-1.0.0-prisma", + "version-1.0.0-nestjs" + ], + "Others": [ + "version-1.0.0-emit-schema", + "version-1.0.0-performance", + "version-1.0.0-browser-usage" + ] + }, + "version-1.0.0-examples": { + "Examples": [ + "version-1.0.0-examples" + ] + }, + "version-1.0.0-others": { + "Others": [ + "version-1.0.0-faq" + ] + } +} diff --git a/website/versions.json b/website/versions.json index 1c76bcca7..0d2b0802a 100644 --- a/website/versions.json +++ b/website/versions.json @@ -1,7 +1,5 @@ [ - "1.0.0-rc.3", - "1.0.0-rc.2", - "1.0.0-rc.1", + "1.0.0", "0.17.6", "0.17.5", "0.17.4",