• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    迪恩网络公众号

aichbauer/express-graphql-boilerplate: Express GraphQL API with JWT Authenticati ...

原作者: [db:作者] 来自: 网络 收藏 邀请

开源软件名称(OpenSource Name):

aichbauer/express-graphql-boilerplate

开源软件地址(OpenSource Url):

https://github.com/aichbauer/express-graphql-boilerplate

开源编程语言(OpenSource Language):

JavaScript 100.0%

开源软件介绍(OpenSource Introduction):

express-graphql-boilerplate

Express GraphQL API with JWT Authentication and support for sqlite, mysql, and postgresql

  • Authentication via JWT
  • Support for sqlite, mysql, and postgresql
  • Support for graphiql an easy way exploring a GraphQL API
  • Environments for development, testing, and production
  • Linting via eslint
  • Integration tests running with Jest
  • Built with npm scripts
  • Examples for User, Note, and nested GraphQL Queries

Quick Intro

GraphQL is a Query Language where your REST API can co-exist directly beside your GraphQL API in harmony. To demonstrate this we have two REST endpoints for register and login.

# clone repository
$ git clone https://github.com/aichbauer/express-graphql-boilerplate.git
# cd into project root
$ cd express-graphql-boilerplate
# install dependencies
$ npm i
# start application
$ npm start
# create a User via the REST API
$ curl -H "Content-Type: application/json" -X POST -d '{"email":"[email protected]","password":"pw","password2":"pw"}' http://localhost:2017/rest/register
# login a User via the REST API
# you will get a JSON with a token and this is your token to get access to the GraphQL API
$ curl -H "Content-Type: application/json" -X POST -d '{"email":"[email protected]","password":"pw"}' http://localhost:2017/rest/login
# requesting a User via the GraphQL API
$ curl -i -H "Content-Type:application/json" -H "Authorization: Bearer <token>" -X POST -d '{"query": "{user{id, username}}"}'  http://localhost:2017/graphql
# creating a Note for a user via the GraphQL API
$ curl -i -H "Content-Type:application/json" -H "Authorization: Bearer <token>" -X POST -d '{"query": "mutation{createNote(userId:1,note:\"this is a note\"){id,userId,note}}"}' http://localhost:2017/graphql
# requesting a User with its Notes via the GraphQL API (nested Query)
$ curl -i -H "Content-Type:application/json" -H "Authorization: Bearer <token>" -X POST -d '{"query": "{user{id, username, notes{id, note}}}"}'  http://localhost:2017/graphql

Table of Contents

Install and Use

Start by cloning this repository

# HTTPS
$ git clone https://github.com/aichbauer/express-graphql-boilerplate.git

then

# change directory to project root
$ cd express-graphql-boilerplate
# install dependencies
$ npm i
# to use mysql
$ npm i mysql2 -S
# to use postgresql
$ npm i pg pg-hstore -S

or

# change directory to project root
$ cd express-graphql-boilerplate
# install dependencies
$ yarn
# to use mysql
$ yarn add mysql2
# to use postgresql
$ yarn add pg pg-hstore

SQLite is supported out of the box as it is the default database.

Folder Structure

This boilerplate has four main directories:

  • api - for controllers, queries, mutations, models, types, services, etc.
  • config - for routes, database, etc.
  • db - this is only a directory for the sqlite database, the default for NODE_ENV=development
  • test - using Jest

Controllers

Controllers in this boilerplate have a naming convention: ModelnameController.js and uses an object factory pattern. To use a model inside of your controller you have to require it. We use Sequelize as ORM, if you want further information read the Docs.

Create a Controller

For an example with all CRUD operations visit the express-rest-api-boilerplate

GraphQL

This directory holds all files that are related to GraphQL (mutations, queries, types, ...).

Create a Query

Note: You need to have a Type, and an existing Model to use queries in combination with a database!

Example query for a User which lets you request all different fields which are defined in args.

// import the required GraphQL Types
const {
  GraphQLInt,
  GraphQLString,
  GraphQLList,
} = require('graphql');

// import the Model and the Type
const { UserType } = require('../types');
const { User } = require('../../models');

// create the query
const userQuery = {
  type: new GraphQLList(UserType), // the Type which it returns (an array of Users)
  args: {
    // arguments you are able to Query
    // notice no password field
    // so the password will not be send as respond
    // neither can you Query for it
    id: {
      name: 'id',
      type: GraphQLInt,
    },
    username: {
      name: 'username',
      type: GraphQLString,
    },
    email: {
      name: 'email',
      type: GraphQLString,
    },
    notes: {
      name: 'notes',
      type: GraphQLString,
    },
    createdAt: {
      name: 'createdAt',
      type: GraphQLString,
    },
    updatedAt: {
      name: 'updatedAt',
      type: GraphQLString,
    },
  },
  // how to get the respond
  // DB call
  resolve: (user, args) => User.findAll({ where: args }),
};

module.exports = { userQuery };

Do not forget to require and export the query in ./api/graphql/queries/index.js.

Create a Mutation

Note: You need to have a Type, and an existing Model to use mutations in combination with a database!

// import the rqeuired GraphQL Types
const {
  GraphQLString,
  GraphQLInt,
  GraphQLNonNull,
} = require('graphql');
const merge = require('lodash.merge');

// import the Model and the Type
const { UserType } = require('../types');
const { User } = require('../../models');

// create the mutation
const updateUser = {
  // the Type which it returns (one user)
  type: UserType,
  description: 'The mutation that allows you to update an existing User by Id',
  // arguments you can use
  // have to be fields that are
  // resolvable by the UserType
  args: {
    id: {
      name: 'id',
      type: new GraphQLNonNull(GraphQLInt),
    },
    username: {
      name: 'username',
      type: GraphQLString,
    },
    email: {
      name: 'email',
      type: GraphQLString,
    },
  },
  // find the User in the DB
  // update the fields for this user
  resolve: async (user, { id, username, email }) => {
    const foundUser = await User.findByPk(id);

    if (!foundUser) {
      throw new Error(`User with id: ${id} not found!`);
    }

    const updatedUser = merge(foundUser, {
      username,
      email,
    });

    return foundUser.update(updatedUser);
  },
};

// the delete mutation
const deleteUser = {
  // the Type which it returns (one user)
  type: UserType,
  description: 'The mutation that allows you to delete a existing User by Id',
  // arguments you can use
  args: {
    id: {
      name: 'id',
      type: new GraphQLNonNull(GraphQLInt),
    },
  },
  resolve: async (user, { id }) => {
    const foundUser = await User.findByPk(id);

    if (!foundUser) {
      throw new Error(`User with id: ${id} not found!`);
    }

    await User.destroy({
      where: {
        id,
      },
    });

    return foundUser;
  },
};

module.exports = {
  updateUser,
  deleteUser,
};

Do not forget to require and export the mutation in ./api/graphql/mutations/index.js.

Create a Type

Types are necessary to let GraphQL know, how to resolve the different fields you provide in your queries and mutations.

// import the required GraphQL Types
const {
  GraphQLObjectType,
  GraphQLInt,
  GraphQLString,
  GraphQLList,
} = require('graphql');

// our UserType definition
const UserType = new GraphQLObjectType({
  name: 'User',
  description: 'This represents a User',
  // all the fields a User can have
  fields: () => ({
    id: {
      type: GraphQLInt,
      resolve: (user) => user.id,
    },
    username: {
      type: GraphQLString,
      resolve: (user) => user.username,
    },
    email: {
      type: GraphQLString,
      resolve: (user) => user.email,
    },
    createdAt: {
      type: GraphQLString,
      resolve: (user) => user.createdAt,
    },
    updatedAt: {
      type: GraphQLString,
      resolve: (user) => user.updatedAt,
    },
  }),
});

module.exports = { UserType };

Do not forget to require and export the type in ./api/graphql/types/index.js.

Create an InputType

InputTypes are a way to simplify your arguments on a mutation or a query. You may not need to implement InputTypes, but it is common that some kind of mutations or queries accept the same input types, what makes them pretty handy.

The simpliest version would of a InputType could look like this:

// import the GraphQL types
// that you need for your
// input type
const {
  GraphQLInputObjectType,
  GraphQLInt,
  GraphQLNonNull,
  GraphQLString,
} = require('graphql');

// create a InputType
const UserInputType = new GraphQLInputObjectType({
  name: 'UserInputType',
  description: 'This represents a UserInputType',
  fields: {
    id: {
      type: new GraphQLNonNull(GraphQLInt),
    },
    username: {
      type: GraphQLString,
    },
    email: {
      type: GraphQLString,
    },
  },
});


module.exports = { UserInputType };

But you can reuse the same implementation for multiple purposes of the same InputType, e.g. update and delete. To delete a user you only need to send the Id and nothing else, this could help front end developers.

// import the GraphQL types
// that you need for your
// input type
const {
  GraphQLInputObjectType,
  GraphQLInt,
  GraphQLNonNull,
  GraphQLString,
} = require('graphql');

// the function that accepts a
// string, specifying the type
// e.g. 'create', 'update', or 'delete'
const UserInputType = (type) => {
  let allGraphFields = {};
  const standardGraphFields = {
    id: {
      type: new GraphQLNonNull(GraphQLInt),
    },
  };

  // add args for different mutations
  switch (type) {
    case 'delete':
      allGraphFields = {
        ...standardGraphFields,
      };
      break;
    case 'update':
      allGraphFields = {
        ...standardGraphFields,
        username: {
          type: GraphQLString,
        },
        email: {
          type: GraphQLString,
        },
      };
      break;
    default:
      allGraphFields = {
        ...standardGraphFields,
      };
  }

  // create a InputType
  // keep in mind that
  // one name can only
  // exist once in a schema
  const userInputType = new GraphQLInputObjectType({
    name: `UserInputType${type[0].toUpperCase() + type.slice(1, type.length - 1)}`,
    description: 'This represents a UserInputType',
    fields: allGraphFields,
  });

  return userInputType;
};

module.exports = { UserInputType };

Now you can use this on a mutation or a query like:

const updateUser = {
  type: UserType,
  description: 'The mutation that allows you to update an existing User by Id',
  args: {
    user: {
      name: 'user',
      // use the InputType
      type: UserInputType('update'),
    },
  },
  resolve: async (_, { user }) => {
    const foundUser = await User.findByPk(user.id);

    if (!foundUser) {
      throw new Error(`User with id: ${user.id} not found!`);
    }

    const updatedUser = merge(foundUser, {
      username: user.username,
      email: user.email,
    });

    return foundUser.update(updatedUser);
  },
};

const deleteUser = {
  type: UserType,
  description: 'The mutation that allows you to delete a existing User by Id',
  args: {
    user: {
      name: 'user',
      // use the InputType
      type: UserInputType('delete'),
    },
  },
  resolve: async (_, { user }) => {
    const foundUser = await User.findByPk(user.id);

    if (!foundUser) {
      throw new Error(`User with id: ${user.id} not found!`);
    }

    await User.destroy({
      where: {
        id: user.id,
      },
    });

    return foundUser;
  },
};

RootQuery, RootMutation and Schema

The schema holds the RootQuery and the RootMutation which holds all the other Queries and Mutations, that is applied to one route which is your entrypoint for your GraphQL API. The schema has to be exported and used in the ./api/api.js file.

// import required GraphQL Types
const {
  GraphQLSchema,
  GraphQLObjectType,
} = require('graphql');

// import Query and Mutations
const { userQuery } = require('./queries');
const {
  updateUser,
  deleteUser,
} = require('./mutations');

// add Queries to RootQuery
const RootQuery = new GraphQLObjectType({
  name: 'rootQuery',
  description: 'This is the RootQuery which holds all possible READ entrypoints for the GraphQL API',
  fields: () => ({
    user: userQuery,
  }),
});

// add Mutations to RootMutations
const RootMutation = new GraphQLObjectType
                      

鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap