Skip to content
Permalink
Browse files

release: 0.17.4

  • Loading branch information...
19majkel94 committed May 12, 2019
1 parent da2f95d commit fdb7e57b8e08fddb0535dd601f1c5505b3a95fb3
@@ -1,7 +1,9 @@
# Changelog and release notes

## Unreleased
<!-- ## Unreleased -->
<!-- here goes all the unreleased changes descriptions -->

## v0.17.4
## Features
- add support for creating custom parameter decorators (#329)
- allow to provide custom `subscribe` function in `@Subscription` decorator (#328)

Some generated files are not rendered by default. Learn more.

@@ -1,6 +1,6 @@
{
"name": "type-graphql",
"version": "0.17.3",
"version": "0.17.4",
"author": {
"name": "Michał Lytek",
"url": "https://github.com/19majkel94"
@@ -297,6 +297,25 @@
},
"version-0.17.2-unions": {
"title": "Unions"
},
"version-0.17.4-custom-decorators": {
"title": "Custom decorators"
},
"version-0.17.4-examples": {
"title": "Examples",
"sidebar_label": "List of examples"
},
"version-0.17.4-interfaces": {
"title": "Interfaces"
},
"version-0.17.4-middlewares": {
"title": "Middleware and guards"
},
"version-0.17.4-subscriptions": {
"title": "Subscriptions"
},
"version-0.17.4-unions": {
"title": "Unions"
}
},
"links": {
@@ -0,0 +1,106 @@
---
title: Custom decorators
id: version-0.17.4-custom-decorators
original_id: custom-decorators
---

Custom decorators are a great way to reduce the boilerplate and reuse some common logic between different resolvers. TypeGraphQL supports two kinds of custom decorators - method and parameter.

## Method decorators

Using [middlewares](middlewares.md) allows to reuse some code between resolvers. To further reduce the boilerplate and have a nicer API, we can create our own custom method decorators.

They work in the same way as the [reusable middleware function](middlewares.md#reusable-middleware), however, in this case we need to call `createMethodDecorator` helper function with our middleware logic and return its value:

```typescript
export function ValidateArgs(schema: JoiSchema) {
return createMethodDecorator(async ({ args }, next) => {
// here place your middleware code that uses custom decorator arguments
// e.g. validation logic based on schema using joi
await joiValidate(schema, args);
return next();
});
}
```

The usage is then very simple, as we have a custom, descriptive decorator - we just place it above the resolver/field and pass the required arguments to it:

```typescript
@Resolver()
export class RecipeResolver {
@ValidateArgs(MyArgsSchema) // custom decorator
@UseMiddleware(ResolveTime) // explicit middleware
@Query()
randomValue(@Args() { scale }: MyArgs): number {
return Math.random() * scale;
}
}
```

## Parameter decorators

Parameter decorators are just like the custom method decorators or middlewares but with an ability to return some value that will be injected to the method as a parameter. Thanks to this, it reduces the pollution in `context` which was used as a workaround for the communication between reusable middlewares and resolvers.

They might be just a simple data extractor function, that makes our resolver more unit test friendly:

```typescript
function CurrentUser() {
return createParamDecorator<MyContextType>(({ context }) => context.currentUser);
}
```

Or might be a more advanced one that performs some calculations and encapsulates some logic. Compared to middlewares, they allows for a more granular control on executing the code, like calculating fields map based on GraphQL info only when it's really needed (requested by using the `@Fields()` decorator):

```typescript
function Fields(level = 1): ParameterDecorator {
return createParamDecorator(({ info }) => {
const fieldsMap: FieldsMap = {};
// calculate an object with info about requested fields
// based on GraphQL `info` parameter of the resolver and the level parameter
return fieldsMap;
}
}
```
Then we can use our custom param decorators in the resolvers just like the built-in decorators:
```typescript
@Resolver()
export class RecipeResolver {
constructor(private readonly recipesRepository: Repository<Recipe>) {}
@Authorized()
@Mutation(returns => Recipe)
async addRecipe(
@Args() recipeData: AddRecipeInput,
// here we place our custom decorator
// just like the built-in one
@CurrentUser() currentUser: User,
) {
const recipe: Recipe = {
...recipeData,
// and use the data returned from custom decorator in our resolver code
author: currentUser,
};
await this.recipesRepository.save(recipe);
return recipe;
}
@Query(returns => Recipe, { nullable: true })
async recipe(
@Arg("id") id: string,
// our custom decorator that parses the fields from graphql query info
@Fields() fields: FieldsMap,
) {
return await this.recipesRepository.find(id, {
// use the fields map as a select projection to optimize db queries
select: fields,
});
}
}
```
## Example
See how different kinds of custom decorators work in the [custom decorators and middlewares example](https://github.com/19majkel94/type-graphql/tree/v0.17.4/examples/middlewares-custom-decorators).
@@ -0,0 +1,44 @@
---
title: Examples
sidebar_label: List of examples
id: version-0.17.4-examples
original_id: examples
---

On the [GitHub repository](https://github.com/19majkel94/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/19majkel94/type-graphql/tree/v0.17.4/examples/simple-usage)

## Advanced

- [Enums and unions](https://github.com/19majkel94/type-graphql/tree/v0.17.4/examples/enums-and-unions)
- [Subscriptions (simple)](https://github.com/19majkel94/type-graphql/tree/v0.17.4/examples/simple-subscriptions)
- [Subscriptions (using Redis)](https://github.com/19majkel94/type-graphql/tree/v0.17.4/examples/redis-subscriptions)
- [Interfaces](https://github.com/19majkel94/type-graphql/tree/v0.17.4/examples/interfaces-inheritance)

## Features usage

- [Dependency injection (IoC container)](https://github.com/19majkel94/type-graphql/tree/v0.17.4/examples/using-container)
- [scoped container](https://github.com/19majkel94/type-graphql/tree/v0.17.4/examples/using-scoped-container)
- [Authorization](https://github.com/19majkel94/type-graphql/tree/v0.17.4/examples/authorization)
- [Validation](https://github.com/19majkel94/type-graphql/tree/v0.17.4/examples/automatic-validation)
- [Types inheritance](https://github.com/19majkel94/type-graphql/tree/v0.17.4/examples/interfaces-inheritance)
- [Resolvers inheritance](https://github.com/19majkel94/type-graphql/tree/v0.17.4/examples/resolvers-inheritance)
- [Generic types](https://github.com/19majkel94/type-graphql/tree/v0.17.4/examples/generic-types)
- [Middlewares and Custom Decorators](https://github.com/19majkel94/type-graphql/tree/v0.17.4/examples/middlewares-custom-decorators)

## 3rd party libs integration

- [TypeORM (manual, synchronous) \*](https://github.com/19majkel94/type-graphql/tree/v0.17.4/examples/typeorm-basic-usage)
- [TypeORM (automatic, lazy relations) \*](https://github.com/19majkel94/type-graphql/tree/v0.17.4/examples/typeorm-lazy-relations)
- [Typegoose](https://github.com/19majkel94/type-graphql/tree/v0.17.4/examples/typegoose)
- [Apollo Engine (Apollo Cache Control) \*\*](https://github.com/19majkel94/type-graphql/tree/v0.17.4/examples/apollo-engine)
- [Apollo client state](https://github.com/19majkel94/type-graphql/tree/v0.17.4/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_
@@ -0,0 +1,74 @@
---
title: Interfaces
id: version-0.17.4-interfaces
original_id: interfaces
---

The main idea of TypeGraphQL is to create GraphQL types based on TypeScript classes.

In object-oriented programming it is common to create interfaces which describe the contract that classes implementing them must adhere to. Hence, TypeGraphQL supports defining GraphQL interfaces.

Read more about the GraphQL Interface Type in the [official GraphQL docs](https://graphql.org/learn/schema/#interfaces).

## Usage

TypeScript has first class support for interfaces. Unfortunately, they only exist at compile-time, so we can't use them to build GraphQL schema at runtime by using decorators.

Luckily, we can use an abstract class for this purpose. It behaves almost like an interface - it can't be "newed" but it can be implemented by the class - and it just won't prevent developers from implementing a method or initializing a field. So, as long as we treat it like an interface, we can safely use it.

How do we create a GraphQL interface definition? We create an abstract class and decorate it with the `@InterfaceType()` decorator. The rest is exactly the same as with object types: we use the `@Field` decorator to declare the shape of the type:

```typescript
@InterfaceType()
abstract class IPerson {
@Field(type => ID)
id: string;
@Field()
name: string;
@Field(type => Int)
age: number;
}
```

We can then we use this "interface" in the object type class definition:

```typescript
@ObjectType({ implements: IPerson })
class Person implements IPerson {
id: string;
name: string;
age: number;
}
```

The only difference is that we have to let TypeGraphQL know that this `ObjectType` is implementing the `InterfaceType`. We do this by passing the param `({ implements: IPerson })` to the decorator. If we implement multiple interfaces, we pass an array of interfaces like so: `({ implements: [IPerson, IAnimal, IMachine] })`.

We can also omit the decorators since the GraphQL types will be copied from the interface definition - this way we won't have to maintain two definitions and solely rely on TypeScript type checking for correct interface implementation.

## Resolving Type

Be aware that when our object type is implementing a GraphQL interface type, **we have to return an instance of the type class** in our resolvers. Otherwise, `graphql-js` will not be able to detect the underlying GraphQL type correctly.

We can also provide our own `resolveType` function implementation to the `@InterfaceType` options. This way we can return plain objects in resolvers and then determine the returned object type by checking the shape of the data object, the same ways [like in unions](./unions.md), e.g.:

```typescript
@InterfaceType({
resolveType: value => {
if ("grades" in value) {
return "Student"; // schema name of the type as a string
}
return Person; // or the object type class
},
})
abstract class IPerson {
// ...
}
```

However in case of interfaces, it might be a little bit more tricky than with unions, as we might not remember all the object types that implements this particular interface.

## Examples

For more advanced usage examples of interfaces (and type inheritance), e.g. with query returning an interface type, go to [this examples folder](https://github.com/19majkel94/type-graphql/tree/v0.17.4/examples/interfaces-inheritance).

0 comments on commit fdb7e57

Please sign in to comment.
You can’t perform that action at this time.