Skip to content

Commit 37a2d10

Browse files
committed
release: 0.17.0
1 parent efe5371 commit 37a2d10

27 files changed

+2751
-6
lines changed

CHANGELOG.md

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

3-
## Unreleased
3+
<!-- ## Unreleased -->
44
<!-- here goes all the unreleased changes descriptions -->
5+
6+
## v0.17.0
57
### Features
68
- **Breaking Change**: make `graphql-js` packages a peer dependencies, bump `graphql` to `^14.1.1` and `@types/graphql` to `^14.0.7` (#239)
79
- **Breaking Change**: remove `useContainer` function and allow to register container by `buildSchema` options (#241)
810
- **Breaking Change**: change the default `PrintSchemaOptions` option `commentDescriptions` to false (no more `#` comments in SDL)
911
- add support for passing `PrintSchemaOptions` in `buildSchema.emitSchemaFile` (e.g. `commentDescriptions: true` to restore previous behavior)
1012
- add `buildTypeDefsAndResolvers` utils function for generating apollo-like `typeDefs` and `resolvers` pair (#233)
1113
- add support for generic types (#255)
12-
13-
## Fixes
14+
### Fixes
1415
- **Breaking Change**: remove the `formatArgumentValidationError` helper as it's not compatible and not needed in new Apollo Server (#258)
1516
- fix calling return type getter function `@Field(type => Foo)` before finishing module evaluation (allow for extending circular classes using `require`)
1617
- fix nullifying other custom method decorators - call the method on target instance, not the stored reference to original function (#247)

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "type-graphql",
3-
"version": "0.16.0",
3+
"version": "0.17.0",
44
"author": {
55
"name": "Michał Lytek",
66
"url": "https://github.com/19majkel94"
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
---
2+
title: Authorization
3+
id: version-0.17.0-authorization
4+
original_id: authorization
5+
---
6+
7+
Authorization is a core feature used in almost all APIs. Sometimes we want to restrict access to some actions or reading some data only for specific group of users.
8+
9+
In express.js (and other Node.js framework) we use middlewares for this, like `passport.js` or the custom ones. However in GraphQL's resolvers architecture we don't have middlewares so we have to imperatively call the auth checking function and manually passing context data in each resolver, which might be quite tedious work.
10+
11+
And that's why authorization is a first-class feature in `TypeGraphQL`!
12+
13+
## How to use?
14+
15+
At first, you need to use `@Authorized` decorator as a guard on a field or a query/mutation.
16+
Example object type's fields guards:
17+
18+
```typescript
19+
@ObjectType()
20+
class MyObject {
21+
@Field()
22+
publicField: string;
23+
24+
@Authorized()
25+
@Field()
26+
authorizedField: string;
27+
28+
@Authorized("ADMIN")
29+
@Field()
30+
adminField: string;
31+
32+
@Authorized(["ADMIN", "MODERATOR"])
33+
@Field({ nullable: true })
34+
hiddenField?: string;
35+
}
36+
```
37+
38+
You can leave the `@Authorized` decorator brackets empty or you can specify the roles that the user needs to have to get access to the field, query or mutation.
39+
By default the roles are `string` but you can change it easily as the decorator is generic - `@Authorized<number>(1, 7, 22)`.
40+
41+
This way authed users (regardless of theirs roles) could read only `publicField` or `authorizedField` from `MyObject` object. They will receive `null` when accessing `hiddenField` field and will receive error (that will propagate through the whole query tree looking for nullable field) for `adminField` when they don't satisfy roles constraints.
42+
43+
Sample query and mutations guards:
44+
45+
```typescript
46+
@Resolver()
47+
class MyResolver {
48+
@Query()
49+
publicQuery(): MyObject {
50+
return {
51+
publicField: "Some public data",
52+
authorizedField: "Data only for logged users",
53+
adminField: "Top secret info for admin",
54+
};
55+
}
56+
57+
@Authorized()
58+
@Query()
59+
authedQuery(): string {
60+
return "Only for authed users!";
61+
}
62+
63+
@Authorized("ADMIN", "MODERATOR")
64+
@Mutation()
65+
adminMutation(): string {
66+
return "You are an admin/moderator, you can safely drop database ;)";
67+
}
68+
}
69+
```
70+
71+
Authed users (regardless of theirs roles) will be able to read data from `publicQuery` and `authedQuery` but will receive error trying to perform `adminMutation` when their roles doesn't include `ADMIN` or `MODERATOR`.
72+
73+
In next step, you need to create your auth checker function. Its implementation may depends on your business logic:
74+
75+
```typescript
76+
export const customAuthChecker: AuthChecker<ContextType> = (
77+
{ root, args, context, info },
78+
roles,
79+
) => {
80+
// here you can read user from context
81+
// and check his permission in db against `roles` argument
82+
// that comes from `@Authorized`, eg. ["ADMIN", "MODERATOR"]
83+
84+
return true; // or false if access denied
85+
};
86+
```
87+
88+
The second argument of `AuthChecker` generic type is `RoleType` - use it together with `@Authorized` decorator generic type.
89+
90+
The last step is to register the function while building the schema:
91+
92+
```typescript
93+
import { customAuthChecker } from "../auth/custom-auth-checker.ts";
94+
95+
const schema = await buildSchema({
96+
resolvers: [MyResolver],
97+
// here we register the auth checking function
98+
// or defining it inline
99+
authChecker: customAuthChecker,
100+
});
101+
```
102+
103+
And it's done! 😉
104+
105+
If you need silent auth guards and you don't want to return auth errors to users, you can set `authMode` property of `buildSchema` config object to `"null"`:
106+
107+
```typescript
108+
const schema = await buildSchema({
109+
resolvers: ["./**/*.resolver.ts"],
110+
authChecker: customAuthChecker,
111+
authMode: "null",
112+
});
113+
```
114+
115+
It will then return `null` instead of throwing authorization error.
116+
117+
## Recipes
118+
119+
You can also use `TypeGraphQL` with JWT authentication. Example using `apollo-server-express`:
120+
121+
```typescript
122+
import express from "express";
123+
import { ApolloServer, gql } from "apollo-server-express";
124+
import * as jwt from "express-jwt";
125+
126+
import { schema } from "../example/above";
127+
128+
const app = express();
129+
const path = "/graphql";
130+
131+
// Create a GraphQL server
132+
const server = new ApolloServer({
133+
schema,
134+
context: ({ req }) => {
135+
const context = {
136+
req,
137+
user: req.user, // `req.user` comes from `express-jwt`
138+
};
139+
return context;
140+
},
141+
});
142+
143+
// Mount a jwt or other authentication middleware that is run before the GraphQL execution
144+
app.use(
145+
path,
146+
jwt({
147+
secret: "TypeGraphQL",
148+
credentialsRequired: false,
149+
}),
150+
);
151+
152+
// Apply the GraphQL server middleware
153+
server.applyMiddleware({ app, path });
154+
155+
// Launch the express server
156+
app.listen({ port: 4000 }, () =>
157+
console.log(`🚀 Server ready at http://localhost:4000${server.graphqlPath}`),
158+
);
159+
```
160+
161+
Then you can use standard, token based authorization in HTTP header like in classic REST API and take advantages of `TypeGraphQL` authorization mechanism.
162+
163+
## Example
164+
165+
You can see how this works together in the [simple real life example](https://github.com/19majkel94/type-graphql/tree/master/examples/authorization).
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
---
2+
title: Bootstrapping
3+
id: version-0.17.0-bootstrap
4+
original_id: bootstrap
5+
---
6+
7+
After creating our resolvers, types 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 by HTTP server, WebSockets or even MQTT.
8+
9+
## Create Executable Schema
10+
11+
To create an executable schema from type and resolver definitions, you 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 you 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, SampleResolver],
21+
});
22+
```
23+
24+
However, when there are several dozen of resolver classes, manual imports can be tedious.
25+
So you can also provide an array of paths to resolver module files instead, which can include globs:
26+
27+
```typescript
28+
const schema = await buildSchema({
29+
resolvers: [__dirname + "/modules/**/*.resolver.ts", __dirname + "/resolvers/**/*.ts"],
30+
});
31+
```
32+
33+
There are also other options related to advanced features like [authorization](authorization.md) or [validation](validation.md) - you can read about them in docs.
34+
35+
To make `await` work, we need to declare it as an async function. Example of `main.ts` file:
36+
37+
```typescript
38+
import { buildSchema } from "type-graphql";
39+
40+
async function bootstrap() {
41+
const schema = await buildSchema({
42+
resolvers: [__dirname + "/**/*.resolver.ts"],
43+
});
44+
45+
// other initialization code, like creating http server
46+
}
47+
48+
bootstrap(); // actually run the async function
49+
```
50+
51+
## Create HTTP GraphQL endpoint
52+
53+
In most cases, the GraphQL app is served by a 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):
54+
55+
```typescript
56+
import { ApolloServer } from "apollo-server";
57+
58+
const PORT = process.env.PORT || 4000;
59+
60+
async function bootstrap() {
61+
// ... Building schema here
62+
63+
// Create GraphQL server
64+
const server = new ApolloServer({
65+
schema,
66+
playground: true,
67+
});
68+
69+
// Start the server
70+
const { url } = await server.listen(PORT);
71+
console.log(`Server is running, GraphQL Playground available at ${url}`);
72+
}
73+
74+
bootstrap();
75+
```
76+
77+
Remember to install `apollo-server` package from npm - it's not bundled with TypeGraphQL.
78+
79+
Of course you can use `express-graphql` middleware, `graphql-yoga` or whatever you want 😉
80+
81+
## Create typeDefs and resolvers map
82+
83+
TypeGraphQL also provides a second way to generate the GraphQL schema - the `buildTypeDefsAndResolvers` function.
84+
85+
It accepts the same `BuildSchemaOptions` like 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):
86+
87+
```typescript
88+
import { makeExecutableSchema } from "graphql-tools";
89+
90+
const { typeDefs, resolvers } = await buildTypeDefsAndResolvers({
91+
resolvers: [FirstResolver, SecondResolver],
92+
});
93+
94+
const schema = makeExecutableSchema({ typeDefs, resolvers });
95+
```
96+
97+
Or even with other libraries that expect the schema info in that shape, like [`apollo-link-state`](https://github.com/apollographql/apollo-link-state):
98+
99+
```typescript
100+
import { withClientState } from "apollo-link-state";
101+
102+
const { typeDefs, resolvers } = await buildTypeDefsAndResolvers({
103+
resolvers: [FirstResolver, SecondResolver],
104+
});
105+
106+
const stateLink = withClientState({
107+
// ...other options like `cache`
108+
typeDefs,
109+
resolvers,
110+
});
111+
112+
// ...the rest of `ApolloClient` initialization code
113+
```
114+
115+
Be aware that some of the TypeGraphQL features (i.a. [query complexity](complexity.md)) might not work with `buildTypeDefsAndResolvers` approach because they use some low-level `graphql-js` features.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
title: Browser usage
3+
id: version-0.17.0-browser-usage
4+
original_id: browser-usage
5+
---
6+
7+
## Using classes in client app
8+
9+
Sometimes you might want to use the classes, that you've created and annotated with TypeGraphQL decorators, in your client app that works in browser. For example, you may want to reuse the args or input classes with `class-validator` decorators or the object type classes with some helpful custom methods.
10+
11+
As TypeGraphQL is a Node.js framework, it doesn't work in browser environment, so you may quickly got an error, e.g. `ERROR in ./node_modules/fs.realpath/index.js`, while trying to build your app with Webpack. To fix that, you have to configure Webpack to use the decorators shim instead of normal module. All you need is to add this plugin code to your webpack config:
12+
13+
```js
14+
plugins: [
15+
// ...here are any other existing plugins that you already have
16+
new webpack.NormalModuleReplacementPlugin(/type-graphql$/, resource => {
17+
resource.request = resource.request.replace(/type-graphql/, "type-graphql/dist/browser-shim");
18+
}),
19+
];
20+
```
21+
22+
Also, thanks to this your bundle will be much lighter as you don't embedded the whole TypeGraphQL library code in your app.

0 commit comments

Comments
 (0)