1.1.0 • Published 2 years ago

express-graphql-router v1.1.0

Weekly downloads
-
License
ISC
Repository
github
Last release
2 years ago

express-graphql-router

Write express-like routers in your graphql project.

Installation

npm install express-graphql-router

or

yarn add express-graphql-router

Quick links

Key advantages of this package.

  • Easily use middleware thus ensuring seperation of concerns and avoiding code repetition.
  • Group common resolvers together.

See an example below.

Creating Graphql APIs using this package.

// middleware.ts
import { Middleware } from 'express-graphql-router';

export const authenticationMiddleware: Middleware = (next, parent, args, context, info) => {
  console.log('in the authentication middleware');
  args.path = args.path || '';
  args.path += ' --> authentication-middleware';
  const result = next();
  console.log('after the authentication middleware');
  return result;
};

export const authorizationMiddleware: Middleware = (next, parent, args, context, info) => {
  console.log('in the authorization middleware');

  args.path = args.path || '';
  args.path += ' --> authorization-middleware';

  const result = next();
  console.log('after the authorization middleware');
  return result;
};

export const validateUser: Middleware = (next, parent, args, context, info) => {
  console.log('in the validate user middleware');

  args.path = args.path || '';
  args.path += ' --> validate-user-middleware';

  if (!args.name) return next(new Error('user name is required'));

  const result = next();
  console.log('after the validate-user middleware');
  return result;
};

export const validateBook: Middleware = (next, parent, args, context, info) => {
  console.log('in the validate book middleware');

  args.path = args.path || '';
  args.path += ' --> validate-book-middleware';

  if (!args.name) return next(new Error('book name is required'));

  const result = next();
  console.log('after the validate-book middleware');
  return result;
};
// BookResolver.ts
import { GraphQLFieldConfig, GraphQLString } from 'graphql';
import { Router } from 'express-graphql-router';
import { authenticationMiddleware, authorizationMiddleware, validateBook } from './middlewares';

const getAllbooks: GraphQLFieldConfig<any, any, any> = {
  type: GraphQLString,
  args: {},
  async resolve(parent, args, context, info) {
    return 'getting all books' + args.path;
  },
};

const getSinglebook: GraphQLFieldConfig<any, any, any> = {
  type: GraphQLString,
  args: {
    id: { type: GraphQLString },
  },
  async resolve(parent, args, context, info) {
    return `getting book with id = ${args.id}` + args.path;
  },
};

const createbook: GraphQLFieldConfig<any, any, any> = {
  type: GraphQLString,
  args: {
    name: { type: GraphQLString },
  },
  async resolve(parent, args, context, info) {
    return `creating book with name = ${args.name}` + args.path;
  },
};

const updatebookdetails: GraphQLFieldConfig<any, any, any> = {
  type: GraphQLString,
  args: {
    id: { type: GraphQLString },
    name: { type: GraphQLString },
  },
  async resolve(parent, args, context, info) {
    return `update name to ${args.name} for book with id = ${args.id}` + args.path;
  },
};

const bookRouter = new Router();

bookRouter.use(authenticationMiddleware, authorizationMiddleware);

bookRouter.query('getAllbooks', getAllbooks);
bookRouter.query('getSinglebook', getSinglebook);
bookRouter.mutation('createbook', createbook, validateBook);
bookRouter.mutation('updatebook', updatebookdetails, validateBook);

export { bookRouter };
// UserResolver.ts
import { GraphQLFieldConfig, GraphQLString } from 'graphql';
import { Router } from 'express-graphql-router';
import { authenticationMiddleware, authorizationMiddleware, validateUser } from './middlewares';

const getAllUsers: GraphQLFieldConfig<any, any, any> = {
  type: GraphQLString,
  args: {},
  async resolve(parent, args, context, info) {
    return 'getting all users' + args.path;
  },
};

const getSingleUser: GraphQLFieldConfig<any, any, any> = {
  type: GraphQLString,
  args: {
    id: { type: GraphQLString },
  },
  async resolve(parent, args, context, info) {
    return `getting user with id = ${args.id}` + args.path;
  },
};

const createUser: GraphQLFieldConfig<any, any, any> = {
  type: GraphQLString,
  args: {
    name: { type: GraphQLString },
  },
  async resolve(parent, args, context, info) {
    return `creating user with name = ${args.name}` + args.path;
  },
};

const updateUserdetails: GraphQLFieldConfig<any, any, any> = {
  type: GraphQLString,
  args: {
    id: { type: GraphQLString },
    name: { type: GraphQLString },
  },
  async resolve(parent, args, context, info) {
    return `update name to ${args.name} for user with id = ${args.id}` + args.path;
  },
};

const userRouter = new Router();

userRouter.use(authenticationMiddleware, authorizationMiddleware);

userRouter.query('getAllUsers', getAllUsers);
userRouter.query('getSingleUser', getSingleUser);
userRouter.mutation('createUser', createUser, validateUser);
userRouter.mutation('updateUser', updateUserdetails, validateUser);

export { userRouter };
// app.ts
import express, { Request, Response } from 'express';
import { GraphQLSchema, GraphQLObjectType } from 'graphql';
import { graphqlHTTP } from 'express-graphql';
import { Router } from 'express-graphql-router';

import { userRouter } from './UserResolver';
import { bookRouter } from './BookResolver';

const router = new Router();
router.use(userRouter);
router.use(bookRouter);

const app = express();

app.use(express.json());
app.use(express.urlencoded({ extended: true }));

const RootQuery = new GraphQLObjectType({
  name: 'RootQueryType',
  fields: router.getQueryFields(),
});

const Mutation = new GraphQLObjectType({
  name: 'Mutation',
  fields: router.getMutationFields(),
});

const schema: GraphQLSchema = new GraphQLSchema({
  query: RootQuery,
  mutation: Mutation,
});

app.use('/graphql', (req: Request, res: Response) => {
  return graphqlHTTP({
    schema,
    graphiql: true,
  })(req, res);
});

app.listen(3098, () => {
  console.log('graphql server running at http://localhost:3098/graphql');
});
export { app };

Using Decorators

An extra feature supported only in typescript is Decorators. You can add middleware and define queries and mutations using the decorators.

See an example below.

// middleware.ts
import { Middleware } from 'express-graphql-router';

export const authenticationMiddleware: Middleware = (next, parent, args, context, info) => {
  console.log('in the authentication middleware');
  args.path = args.path || '';
  args.path += ' --> authentication-middleware';
  const result = next();
  console.log('after the authentication middleware');
  return result;
};

export const authorizationMiddleware: Middleware = (next, parent, args, context, info) => {
  console.log('in the authorization middleware');

  args.path = args.path || '';
  args.path += ' --> authorization-middleware';

  const result = next();
  console.log('after the authorization middleware');
  return result;
};

export const validateUser: Middleware = (next, parent, args, context, info) => {
  console.log('in the validate user middleware');

  args.path = args.path || '';
  args.path += ' --> validate-user-middleware';

  if (!args.name) return next(new Error('user name is required'));

  const result = next();
  console.log('after the validate-user middleware');
  return result;
};

export const validateBook: Middleware = (next, parent, args, context, info) => {
  console.log('in the validate book middleware');

  args.path = args.path || '';
  args.path += ' --> validate-book-middleware';

  if (!args.name) return next(new Error('book name is required'));

  const result = next();
  console.log('after the validate-book middleware');
  return result;
};
// UserResolver.ts
import { GraphQLFieldConfig, GraphQLString } from 'graphql';
import { mutation, query, Router } from 'express-graphql-router';
import { authenticationMiddleware, authorizationMiddleware, validateUser } from './middlewares';

class UserResolver {
  @query('getAllUsers', authenticationMiddleware, authorizationMiddleware)
  getAllUsers: GraphQLFieldConfig<any, any, any> = {
    type: GraphQLString,
    args: {},
    async resolve(parent, args, context, info) {
      return 'getting all users' + args.path;
    },
  };

  @query('getSingleUser', authenticationMiddleware, authorizationMiddleware)
  getSingleUser: GraphQLFieldConfig<any, any, any> = {
    type: GraphQLString,
    args: {
      id: { type: GraphQLString },
    },
    async resolve(parent, args, context, info) {
      return `getting user with id = ${args.id}` + args.path;
    },
  };

  @mutation('createUser', authenticationMiddleware, authorizationMiddleware, validateUser)
  createUser: GraphQLFieldConfig<any, any, any> = {
    type: GraphQLString,
    args: {
      name: { type: GraphQLString },
    },
    async resolve(parent, args, context, info) {
      return `creating user with name = ${args.name}` + args.path;
    },
  };

  @mutation('updateUser', authenticationMiddleware, authorizationMiddleware, validateUser)
  updateUserdetails: GraphQLFieldConfig<any, any, any> = {
    type: GraphQLString,
    args: {
      id: { type: GraphQLString },
      name: { type: GraphQLString },
    },
    async resolve(parent, args, context, info) {
      return `update name to ${args.name} for user with id = ${args.id}` + args.path;
    },
  };
}

new UserResolver();
const userRouter = Router.getRouter(UserResolver);

export { userRouter };
// BookResolver.ts
import { GraphQLFieldConfig, GraphQLString } from 'graphql';
import { classMiddleware, mutation, query, Router } from 'express-graphql-router';
import { authenticationMiddleware, authorizationMiddleware, validateBook } from './middlewares';

@classMiddleware(authenticationMiddleware, authorizationMiddleware)
class BoolResolver {
  @query('getAllBooks')
  getAllbooks: GraphQLFieldConfig<any, any, any> = {
    type: GraphQLString,
    args: {},
    async resolve(parent, args, context, info) {
      return 'getting all books' + args.path;
    },
  };

  @query('getSingleBook')
  getSinglebook: GraphQLFieldConfig<any, any, any> = {
    type: GraphQLString,
    args: {
      id: { type: GraphQLString },
    },
    async resolve(parent, args, context, info) {
      return `getting book with id = ${args.id}` + args.path;
    },
  };

  @mutation('createBook', validateBook)
  createbook: GraphQLFieldConfig<any, any, any> = {
    type: GraphQLString,
    args: {
      name: { type: GraphQLString },
    },
    async resolve(parent, args, context, info) {
      return `creating book with name = ${args.name}` + args.path;
    },
  };

  @mutation('updateBook', validateBook)
  updatebookdetails: GraphQLFieldConfig<any, any, any> = {
    type: GraphQLString,
    args: {
      id: { type: GraphQLString },
      name: { type: GraphQLString },
    },
    async resolve(parent, args, context, info) {
      return `update name to ${args.name} for book with id = ${args.id}` + args.path;
    },
  };
}

new BoolResolver();
const bookRouter = Router.getRouter(BoolResolver);

export { bookRouter };
// app.ts
import express, { Request, Response } from 'express';
import { GraphQLSchema, GraphQLObjectType } from 'graphql';
import { graphqlHTTP } from 'express-graphql';
import { Router } from 'express-graphql-router';

import { userRouter } from './UserResolver';
import { bookRouter } from './BookResolver';

const router = new Router();
router.use(userRouter);
router.use(bookRouter);

const app = express();

app.use(express.json());
app.use(express.urlencoded({ extended: true }));

const RootQuery = new GraphQLObjectType({
  name: 'RootQueryType',
  fields: router.getQueryFields(),
});

const Mutation = new GraphQLObjectType({
  name: 'Mutation',
  fields: router.getMutationFields(),
});

const schema: GraphQLSchema = new GraphQLSchema({
  query: RootQuery,
  mutation: Mutation,
});

app.use('/graphql', (req: Request, res: Response) => {
  return graphqlHTTP({
    schema,
    graphiql: true,
  })(req, res);
});

app.listen(3098, () => {
  console.log('graphql server running at http://localhost:3098/graphql');
});
export { app };

As seen above, there are 3 decorators that are used

  • @query( name: string, ...middleware: Middleware[] ): creates a new query resolver with specified middleware

  • @mutation( name: string, ...middleware: Middleware[] ): creates a new mutation resolver with specified middleware

  • @classMiddleware(...middleware: Middleware[] ): adds specified middleware before all middleware in each of the resolvers in the class.

NOTE

  • When working with decorators, It is very important to create an instance of the resolver class to register the queries and mutation.