Skip to content

Commit b815600

Browse files
committed
release: 1.0.0-rc.1
1 parent b2822d8 commit b815600

24 files changed

+2839
-3
lines changed

CHANGELOG.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
# Changelog and release notes
22

3-
## Unreleased
3+
<!-- ## Unreleased -->
44
<!-- here goes all the unreleased changes descriptions -->
5+
6+
## v1.0.0-rc.1
57
### Features
68
- **Breaking Change**: emit in schema only types actually used by provided resolvers classes (#415)
79
- **Breaking Change**: update `graphql-js` peer dependency to `^15.0.0`
810
- **Breaking Change**: update `graphql-query-complexity` dependency to `^0.5.0` and drop support for `fieldConfigEstimator` (use `fieldExtensionsEstimator` instead)
911
- **Breaking Change**: introduce `sortedSchema` option in `PrintSchemaOptions` and emit sorted schema file by default
1012
- **Breaking Change**: make `class-validator` an optional, peer dependency of version `>=0.12.0` (#366)
11-
- **Breaking Change**: remove deprecated direct array syntax for declaring union types
1213
- **Breaking Change**: remove `CannotDetermineTypeError` and make other error messages more detailed and specific
1314
- update `TypeResolver` interface to match with `GraphQLTypeResolver` from `graphql-js`
1415
- add basic support for directives with `@Directive()` decorator (#369)
@@ -19,10 +20,10 @@
1920
- add `{ autoRegisterImplementations: false }` option to prevent automatic emitting in schema all the object types that implements used interface type (#595)
2021
- allow interfaces to implement other interfaces (#602)
2122
### Fixes
23+
- **Breaking Change**: stop returning null for `GraphQLTimestamp` and `GraphQLISODateTime` scalars when returned value is not a `Date` instance - now it throws explicit error instead
2224
- refactor union types function syntax handling to prevent possible errors with circular refs
2325
- fix transforming and validating nested inputs and arrays (#462)
2426
- remove duplicated entries for resolver classes that use inheritance (#499)
25-
- **Breaking Change**: stop returning null for `GraphQLTimestamp` and `GraphQLISODateTime` scalars when returned value is not a `Date` instance - now it throws explicit error instead
2627
- fix using `name` option on interface fields (#567)
2728
- fix not calling `authChecker` during subscribe phase for subscriptions (#578)
2829
- fix using shared union type in multiple schemas
@@ -33,6 +34,7 @@
3334
### Others
3435
- **Breaking Change**: change build config to ES2018 - drop support for Node.js < 10.3
3536
- **Breaking Change**: remove deprecated `DepreciationOptions` interface
37+
- **Breaking Change**: remove deprecated direct array syntax for declaring union types
3638

3739
## v0.17.6
3840
### Fixes

website/i18n/en.json

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,68 @@
365365
},
366366
"version-0.17.6/version-0.17.6-custom-decorators": {
367367
"title": "Custom decorators"
368+
},
369+
"version-1.0.0-rc.1/version-1.0.0-rc.1-bootstrap": {
370+
"title": "Bootstrapping"
371+
},
372+
"version-1.0.0-rc.1/version-1.0.0-rc.1-browser-usage": {
373+
"title": "Browser usage"
374+
},
375+
"version-1.0.0-rc.1/version-1.0.0-rc.1-complexity": {
376+
"title": "Query complexity"
377+
},
378+
"version-1.0.0-rc.1/version-1.0.0-rc.1-custom-decorators": {
379+
"title": "Custom decorators"
380+
},
381+
"version-1.0.0-rc.1/version-1.0.0-rc.1-dependency-injection": {
382+
"title": "Dependency injection"
383+
},
384+
"version-1.0.0-rc.1/version-1.0.0-rc.1-directives": {
385+
"title": "Directives"
386+
},
387+
"version-1.0.0-rc.1/version-1.0.0-rc.1-emit-schema": {
388+
"title": "Emitting the schema SDL"
389+
},
390+
"version-1.0.0-rc.1/version-1.0.0-rc.1-examples": {
391+
"title": "Examples",
392+
"sidebar_label": "List of examples"
393+
},
394+
"version-1.0.0-rc.1/version-1.0.0-rc.1-extensions": {
395+
"title": "Extensions"
396+
},
397+
"version-1.0.0-rc.1/version-1.0.0-rc.1-generic-types": {
398+
"title": "Generic Types"
399+
},
400+
"version-1.0.0-rc.1/version-1.0.0-rc.1-getting-started": {
401+
"title": "Getting started"
402+
},
403+
"version-1.0.0-rc.1/version-1.0.0-rc.1-installation": {
404+
"title": "Installation"
405+
},
406+
"version-1.0.0-rc.1/version-1.0.0-rc.1-interfaces": {
407+
"title": "Interfaces"
408+
},
409+
"version-1.0.0-rc.1/version-1.0.0-rc.1-middlewares": {
410+
"title": "Middleware and guards"
411+
},
412+
"version-1.0.0-rc.1/version-1.0.0-rc.1-performance": {
413+
"title": "Performance"
414+
},
415+
"version-1.0.0-rc.1/version-1.0.0-rc.1-resolvers": {
416+
"title": "Resolvers"
417+
},
418+
"version-1.0.0-rc.1/version-1.0.0-rc.1-subscriptions": {
419+
"title": "Subscriptions"
420+
},
421+
"version-1.0.0-rc.1/version-1.0.0-rc.1-types-and-fields": {
422+
"title": "Types and Fields"
423+
},
424+
"version-1.0.0-rc.1/version-1.0.0-rc.1-unions": {
425+
"title": "Unions"
426+
},
427+
"version-1.0.0-rc.1/version-1.0.0-rc.1-validation": {
428+
"title": "Argument and Input validation",
429+
"sidebar_label": "Validation"
368430
}
369431
},
370432
"links": {
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
---
2+
title: Bootstrapping
3+
id: version-1.0.0-rc.1-bootstrap
4+
original_id: bootstrap
5+
---
6+
7+
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.
8+
9+
## Create Executable Schema
10+
11+
To create an executable schema from type and resolver definitions, we need to use the `buildSchema` function.
12+
It takes a configuration object as a parameter and returns a promise of a `GraphQLSchema` object.
13+
14+
In the configuration object we must provide a `resolvers` property, which can be an array of resolver classes:
15+
16+
```typescript
17+
import { FirstResolver, SecondResolver } from "../app/src/resolvers";
18+
// ...
19+
const schema = await buildSchema({
20+
resolvers: [FirstResolver, SecondResolver],
21+
});
22+
```
23+
24+
Be aware that only operations (queries, mutation, etc.) defined in the resolvers classes (and types directly connected to them) will be emitted in schema.
25+
26+
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`:
27+
28+
```typescript
29+
import { FirstResolver, SecondResolver } from "../app/src/resolvers";
30+
import { FirstObject } from "../app/src/types";
31+
// ...
32+
const schema = await buildSchema({
33+
resolvers: [FirstResolver, SecondResolver],
34+
// here provide all the types that are missing in schema
35+
orphanedTypes: [FirstObject],
36+
});
37+
```
38+
39+
However, when there are several resolver classes, manual imports can be cumbersome.
40+
So we can also provide an array of paths to resolver module files instead, which can include globs:
41+
42+
```typescript
43+
const schema = await buildSchema({
44+
resolvers: [__dirname + "/modules/**/*.resolver.{ts,js}", __dirname + "/resolvers/**/*.{ts,js}"],
45+
});
46+
```
47+
48+
> 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.
49+
50+
There are also other options related to advanced features like [authorization](authorization.md) or [validation](validation.md) - you can read about them in docs.
51+
52+
To make `await` work, we need to declare it as an async function. Example of `main.ts` file:
53+
54+
```typescript
55+
import { buildSchema } from "type-graphql";
56+
57+
async function bootstrap() {
58+
const schema = await buildSchema({
59+
resolvers: [__dirname + "/**/*.resolver.{ts,js}"],
60+
});
61+
62+
// other initialization code, like creating http server
63+
}
64+
65+
bootstrap(); // actually run the async function
66+
```
67+
68+
## Create an HTTP GraphQL endpoint
69+
70+
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):
71+
72+
```typescript
73+
import { ApolloServer } from "apollo-server";
74+
75+
const PORT = process.env.PORT || 4000;
76+
77+
async function bootstrap() {
78+
// ... Building schema here
79+
80+
// Create the GraphQL server
81+
const server = new ApolloServer({
82+
schema,
83+
playground: true,
84+
});
85+
86+
// Start the server
87+
const { url } = await server.listen(PORT);
88+
console.log(`Server is running, GraphQL Playground available at ${url}`);
89+
}
90+
91+
bootstrap();
92+
```
93+
94+
Remember to install the `apollo-server` package from npm - it's not bundled with TypeGraphQL.
95+
96+
Of course you can use the `express-graphql` middleware, `graphql-yoga` or whatever you want 😉
97+
98+
## Create typeDefs and resolvers map
99+
100+
TypeGraphQL provides a second way to generate the GraphQL schema - the `buildTypeDefsAndResolvers` function.
101+
102+
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):
103+
104+
```typescript
105+
import { makeExecutableSchema } from "graphql-tools";
106+
107+
const { typeDefs, resolvers } = await buildTypeDefsAndResolvers({
108+
resolvers: [FirstResolver, SecondResolver],
109+
});
110+
111+
const schema = makeExecutableSchema({ typeDefs, resolvers });
112+
```
113+
114+
Or even with other libraries that expect the schema info in that shape, like [`apollo-link-state`](https://github.com/apollographql/apollo-link-state):
115+
116+
```typescript
117+
import { withClientState } from "apollo-link-state";
118+
119+
const { typeDefs, resolvers } = await buildTypeDefsAndResolvers({
120+
resolvers: [FirstResolver, SecondResolver],
121+
});
122+
123+
const stateLink = withClientState({
124+
// ...other options like `cache`
125+
typeDefs,
126+
resolvers,
127+
});
128+
129+
// ...the rest of `ApolloClient` initialization code
130+
```
131+
132+
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.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
---
2+
title: Browser usage
3+
id: version-1.0.0-rc.1-browser-usage
4+
original_id: browser-usage
5+
---
6+
7+
## Using classes in a client app
8+
9+
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.
10+
11+
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:
12+
13+
```js
14+
module.exports = {
15+
// ... the rest of the webpack config
16+
plugins: [
17+
// ... here are any other existing plugins that we already have
18+
new webpack.NormalModuleReplacementPlugin(/type-graphql$/, resource => {
19+
resource.request = resource.request.replace(/type-graphql/, "type-graphql/dist/browser-shim.js");
20+
}),
21+
];
22+
}
23+
```
24+
25+
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:
26+
27+
```json
28+
{
29+
"compilerOptions": {
30+
"baseUrl": ".",
31+
"paths": {
32+
"type-graphql": ["./node_modules/type-graphql/dist/browser-shim.ts"]
33+
}
34+
}
35+
}
36+
```
37+
38+
Thanks to this, our bundle will be much lighter as we don't need to embed the whole TypeGraphQL library code in our app.
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
---
2+
title: Query complexity
3+
id: version-1.0.0-rc.1-complexity
4+
original_id: complexity
5+
---
6+
7+
A single GraphQL query can potentially generate a huge workload for a server, like thousands of database operations which can be used to cause DDoS attacks. In order to limit and keep track of what each GraphQL operation can do, `TypeGraphQL` provides the option of integrating with Query Complexity tools like [graphql-query-complexity](https://github.com/ivome/graphql-query-complexity).
8+
9+
This cost analysis-based solution is very promising, since we can define a “cost” per field and then analyze the AST to estimate the total cost of the GraphQL query. Of course all the analysis is handled by `graphql-query-complexity`.
10+
11+
All we must do is define our complexity cost for the fields, mutations or subscriptions in `TypeGraphQL` and implement `graphql-query-complexity` in whatever GraphQL server that is being used.
12+
13+
## How to use
14+
15+
First, we need to pass `complexity` as an option to the decorator on a field, query or mutation.
16+
17+
Example of complexity
18+
19+
```typescript
20+
@ObjectType()
21+
class MyObject {
22+
@Field({ complexity: 2 })
23+
publicField: string;
24+
25+
@Field({ complexity: ({ args, childComplexity }) => childComplexity + 1 })
26+
complexField: string;
27+
}
28+
```
29+
30+
The `complexity` option may be omitted if the complexity value is 1.
31+
Complexity can be passed as an option to any `@Field`, `@FieldResolver`, `@Mutation` or `@Subscription` decorator. If both `@FieldResolver` and `@Field` decorators of the same property have complexity defined, then the complexity passed to the field resolver decorator takes precedence.
32+
33+
In the next step, we will integrate `graphql-query-complexity` with the server that expose our GraphQL schema over HTTP.
34+
You can use it with `express-graphql` like [in the lib examples](https://github.com/slicknode/graphql-query-complexity/blob/b6a000c0984f7391f3b4e886e3df6a7ed1093b07/README.md#usage-with-express-graphql), however we will use Apollo Server like in our other examples:
35+
36+
```typescript
37+
async function bootstrap() {
38+
// ...build TypeGraphQL schema as always
39+
40+
// Create GraphQL server
41+
const server = new ApolloServer({
42+
schema,
43+
// Create a plugin that will allow for query complexity calculation for every request
44+
plugins: [
45+
{
46+
requestDidStart: () => ({
47+
didResolveOperation({ request, document }) {
48+
/**
49+
* This provides GraphQL query analysis to be able to react on complex queries to your GraphQL server.
50+
* This can be used to protect your GraphQL servers against resource exhaustion and DoS attacks.
51+
* More documentation can be found at https://github.com/ivome/graphql-query-complexity.
52+
*/
53+
const complexity = getComplexity({
54+
// Our built schema
55+
schema,
56+
// To calculate query complexity properly,
57+
// we have to check only the requested operation
58+
// not the whole document that may contains multiple operations
59+
operationName: request.operationName,
60+
// The GraphQL query document
61+
query: document,
62+
// The variables for our GraphQL query
63+
variables: request.variables,
64+
// Add any number of estimators. The estimators are invoked in order, the first
65+
// numeric value that is being returned by an estimator is used as the field complexity.
66+
// If no estimator returns a value, an exception is raised.
67+
estimators: [
68+
// Using fieldExtensionsEstimator is mandatory to make it work with type-graphql.
69+
fieldExtensionsEstimator(),
70+
// Add more estimators here...
71+
// This will assign each field a complexity of 1
72+
// if no other estimator returned a value.
73+
simpleEstimator({ defaultComplexity: 1 }),
74+
],
75+
});
76+
// Here we can react to the calculated complexity,
77+
// like compare it with max and throw error when the threshold is reached.
78+
if (complexity >= 20) {
79+
throw new Error(
80+
`Sorry, too complicated query! ${complexity} is over 20 that is the max allowed complexity.`,
81+
);
82+
}
83+
// And here we can e.g. subtract the complexity point from hourly API calls limit.
84+
console.log("Used query complexity points:", complexity);
85+
},
86+
}),
87+
},
88+
],
89+
});
90+
91+
// ...start the server as always
92+
}
93+
```
94+
95+
And it's done! 😉
96+
97+
For more info about how query complexity is computed, please visit [graphql-query-complexity](https://github.com/ivome/graphql-query-complexity).
98+
99+
## Example
100+
101+
See how this works in the [simple query complexity example](https://github.com/MichalLytek/type-graphql/tree/master/examples/query-complexity).

0 commit comments

Comments
 (0)