Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/custom directives #152

Merged
merged 8 commits into from
Feb 14, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions examples/custom-directives/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# custom-directive

This directory contains a simple GraphQL with custom directives example based on `graphql-yoga`.

## Get started

**Clone the repository:**

```sh
git clone https://github.com/graphcool/graphql-yoga/
cd graphql-yoga/examples/custom-directives
```

**Install dependencies and run the app:**

```sh
yarn install # or npm install
yarn start # or npm start
```

## Testing

Open your browser at [http://localhost:4000](http://localhost:4000) and start sending queries.

**Query `hello`:**

```graphql
query {
hello
}
```

The server returns the following response:

```json
{
"data": {
"hello": "HELLO WORD"
}
}
```

Note that the original `Hello Word` output from the resolver is now in upper case due to our custom `@upper` directive.

**Query `secret` (with role set as `admin`):**
```graphql
query {
secret
}
```

The server returns the following response:

```json
{
"data": {
"secret": "This is very secret"
}
}
```


**Query `secret` (with role set as `user`):**

Go to `index.js:45`, change `admin` by `user` and reload the server.

```graphql
query {
secret
}
```

The server returns the following response:

```json
{
"data": {
"secret": null
},
"errors": [
{
"message": "You are not authorized. Expected roles: admin",
"locations": [
{
"line": 1,
"column": 2
}
],
"path": [
"secret"
]
}
]
}
```
48 changes: 48 additions & 0 deletions examples/custom-directives/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
const { GraphQLServer } = require("graphql-yoga");

const typeDefs = `
directive @upper on FIELD_DEFINITION
directive @auth(roles: [String]) on FIELD_DEFINITION

type Query {
hello: String! @upper
secret: String @auth(roles: ["admin"])
}
`;

const directiveResolvers = {
upper(next, src, args, context) {
return next().then(str => str.toUpperCase());
},
auth(next, src, args, context) {
const { roles } = context; // We asume has roles of current user in context;
const expectedRoles = args.roles || [];
if (
expectedRoles.length === 0 || expectedRoles.some(r => roles.includes(r))
) {
// Call next to continues process resolver.
return next();
}

// We has two options here. throw an error or return null (if field is nullable).
throw new Error(
`You are not authorized. Expected roles: ${expectedRoles.join(", ")}`
);
}
};

const resolvers = {
Query: {
hello: () => `Hello World`,
secret: () => `This is very secret`
}
};

const server = new GraphQLServer({
typeDefs,
resolvers,
directiveResolvers,
context: () => ({ roles: ["admin"] })
});

server.start(() => console.log("Server is running on localhost:4000"));
8 changes: 8 additions & 0 deletions examples/custom-directives/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"scripts": {
"start": "node ."
},
"dependencies": {
"graphql-yoga": "1.2.0"
}
}
3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export class GraphQLServer {
if (props.schema) {
this.executableSchema = props.schema
} else if (props.typeDefs && props.resolvers) {
let { typeDefs, resolvers } = props
let { directiveResolvers, typeDefs, resolvers } = props

// read from .graphql file if path provided
if (typeDefs.endsWith('graphql')) {
Expand All @@ -69,6 +69,7 @@ export class GraphQLServer {
? { Upload: GraphQLUpload }
: {}
this.executableSchema = makeExecutableSchema({
directiveResolvers,
typeDefs,
resolvers: {
...uploadMixin,
Expand Down
3 changes: 2 additions & 1 deletion src/lambda.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export class GraphQLServerLambda {
if (props.schema) {
this.executableSchema = props.schema
} else if (props.typeDefs && props.resolvers) {
let { typeDefs, resolvers } = props
let { directiveResolvers, typeDefs, resolvers } = props

// read from .graphql file if path provided
if (typeDefs.endsWith('graphql')) {
Expand All @@ -42,6 +42,7 @@ export class GraphQLServerLambda {
}

this.executableSchema = makeExecutableSchema({
directiveResolvers,
typeDefs,
resolvers,
})
Expand Down
3 changes: 3 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
GraphQLTypeResolver,
ValidationContext,
} from 'graphql'
import { IDirectiveResolvers } from 'graphql-tools/dist/Interfaces'
import { SubscriptionOptions } from 'graphql-subscriptions/dist/subscriptions-manager'
import { LogFunction } from 'apollo-server-core'

Expand Down Expand Up @@ -68,13 +69,15 @@ export interface Options extends ApolloServerOptions {
}

export interface Props {
directiveResolvers?: IDirectiveResolvers<any, any>
typeDefs?: string
resolvers?: IResolvers
schema?: GraphQLSchema
context?: Context | ContextCallback
}

export interface LambdaProps {
directiveResolvers?: IDirectiveResolvers<any, any>
typeDefs?: string
resolvers?: IResolvers
schema?: GraphQLSchema
Expand Down