使用 ExpressJs 和 Docker 简单介绍 GraphQL

简介: 使用 ExpressJs 和 Docker 简单介绍 GraphQL

什么是 GraphQL

GraphQL 是一种用于编写 API 查询的规范,也是一种用于对现有数据执行这些查询的运行时。它围绕 HTTP 协议构建,并定义了如何从服务器发送和接收资源。

使用 GraphQL,您不需要像创建 REST 端点时通常那样创建多个端点来请求数据。您将获得一个单一的端点,它会根据您发送到端点的查询返回所需的数据。

REST 与 GraphQL

假设您正在构建一个 CMS(一个内容管理系统)并且您的数据库中有两个表;userposts。下面,您将看到 REST 和 GraphQL 如何处理您需要获取应用程序中每个用户的所有帖子的情况。

REST

使用 REST 时,您首先需要向第一个端点(例如/users)发出请求,该端点查询user模型以获取数据库中所有用户的列表。这样做之后,您可以提取id每个用户的 ,然后再向第二个端点(例如 )发出另一个请求,该端点/users/posts返回该用户创建的所有帖子。这意味着您必须对端点进行n多次调用,即应用程序中的用户数。/users/posts``n

另请注意,来自/users端点的响应可能包含其他用户信息,除了id姓名、出生日期、位置、年龄等您在此上下文中并不真正需要的信息。这也适用于/users/posts端点。这最终意味着您可以向端点发出更多您需要的请求,这可能会降低应用程序的速度。

GraphQL

使用 GraphQL,您只需要创建一个包含您需要的确切数据的查询。在这种情况下,您通过创建如下所示的查询告诉它您需要所有用户(仅他们的名字)和每个用户创建的所有帖子(仅标题)

query{
  users{
    name
    posts{
      title
    }
  }
}
复制代码

创建查询后,将其发送到 GraphQL 服务器,该服务器解析查询并返回查询中定义的适当数据。在这里,您可以从一个端点获得所需的一切,仅此而已。这在许多方面极大地改进了您的应用程序。

简而言之

简而言之,GraphQL 为我们提供了 REST API 可以做的一切,例如 CRUD 功能、错误处理等。然而,它通过减少发出的请求数量、基于简单有组织的查询嵌套数据来改进它,从而使整个服务器运行得更快并且更加优化。

Express + GraphQL

让我们制作一个方便的模板来开始使用 GraphQL 应用程序。您首先需要使用 GraphQL 设置一个简单的 Express 应用程序。然后向应用程序添加一些功能,这样您就可以看到 GraphQL 必须自己提供的一些功能。

首先,您可以通过运行以下命令创建一个基本的快速服务器;

% npm init --yes
复制代码

这会生成一个package.json文件,其中包含有关您的应用程序的信息,以及其他信息,例如应用程序依赖项以及如何运行它。

您需要通过运行以下命令来安装您的应用程序依赖项,并将它们添加到您的package.json文件中;

% npm i express express-graphql graphql
复制代码

这些依赖项允许您启动一个 express 服务器,将您的 express 服务器连接到 graphql ( express-graphql) 并且还提供您需要的 GraphQL 功能 ( graphql)。

您还将安装nodemon以帮助在任何文件更改时重新启动服务器,dev并向您的文件添加脚本package.json

% npm i --save-dev nodemon
复制代码

package.js

{ "scripts":
  {  "dev": "nodemon index.js"}
}
复制代码

PostgreSQL + docker

完成此操作后,您将使用 docker-compose 设置一个 postgreSQL 数据库,并用一些用户和帖子填充它,以便您可以测试您的应用程序。为此,您将创建一个docker-compose.yml文件,其中包含您将使用的 postgres 服务的配置。您可以通过将以下行添加到 docker-compose 文件来进行设置

version: '3.8'
services:
  postgres:
    image: postgres:13-alpine
    ports:
      - 5432:5432
    env_file:
      - .env
    volumes:
      - postgres:/var/lib/postgresql/data
    networks:
      - graphql-express
volumes:
  postgres:
    name: graphql-express-docker-db
networks:
  graphql-express:
复制代码

然后继续到您的终端并运行docker compose up -dsudo docker-compose up -d(对于 linux)。这将启动 postgres 服务并使其可用于您的应用程序。您还需要安装sequelizeORM用于连接和查询您的数据库。为此,请安装以下内容

% npm install sequelize pg pg-hstore
复制代码

Sequelize 还提供了一个cli通过提供 cli 命令使使用 ORM 更容易的工具。在此示例中,您将使用sequelize-cli来设置数据库连接和架构。在终端中输入以下命令

% npm install --save-dev sequelize-cli
...
% npx sequelize-cli init
复制代码

这将创建许多文件夹

  • config,其中包含配置文件,它告诉 CLI 如何连接数据库
  • models, 包含您项目的所有模型
  • migrations, 包含所有迁移文件

创建这些之后,您需要创建用户和帖子模型。为此,您可以使用model:generate带有模型名称和属性的命令

% npx sequelize-cli model:generate --name User --attributes name:string
% npx sequelize-cli model:generate --name Post --attributes title:string
复制代码

这会生成用户和帖子模型,您可以编辑这些模型以添加关系。在模型中,您可以将以下行添加到文件user中的静态associate函数user.js

static associate({ Post }) {
    this.hasMany(Post, { foreignKey: "userId", onDelete: "CASCADE" })
}
复制代码

post.js文件中,您在模型中添加belongsTo关联Post

static associate({ Post }) {
    this.hasMany(Post, { foreignKey: "userId", onDelete: "CASCADE" })
}
复制代码

db:migrate然后,您可以运行迁移以使用以下命令将模式持久保存在数据库中

npx sequelize-cli db:migrate
复制代码

在此之后,您可以创建一个种子文件以默认插入一些原始数据。这将帮助您稍后测试服务器。您可以创建一个种子文件来创建演示用户。

% npx sequelize-cli seed:generate --name demo-user
...
复制代码

然后您可以继续将演示用户添加到种子文件。添加这些用户后,您可以使用db:seed:all命令创建它们。

xxxxxxxxxxxxx-demo-user.js

'use strict';
/** @type {import('sequelize-cli').Migration} */
module.exports = {
  async up (queryInterface, Sequelize) {
    return queryInterface.bulkInsert('Users', [{
        name: 'Naruto Uzumaki',
        createdAt: new Date(),
        updatedAt: new Date()
    }]);
  },
  async down (queryInterface, Sequelize) {
    return queryInterface.bulkDelete('Users', null, {});
  }
};
复制代码

然后

% npx sequelize-cli db:seed:all
复制代码

Express Server

index.js然后,您可以通过将以下内容添加到文件来设置一个简单的服务器。这会启动服务器并添加GraphQL到它。它使用带有graphqlHTTP两个参数的函数schema,这将在稍后定义,graphiql并且提供用于与我们的服务器交互的 GUI。

const express = require('express');
const { graphqlHTTP } = require('express-graphql');
const { sequelize, User, Post} = require('./models');
const app = express();
app.use('/graphql', graphqlHTTP({
    schema: schema,
    graphiql: true
}))
app.listen(5000, () => (
    await sequelize.authenticate();
    console.log("Running")
))
复制代码

不过,要使其正常工作,您需要定义一个模式,告诉 GraphQL 您的数据如何相互交互。该模式有两个参数;query参数是您从服务器获取数据的一种方式,参数是为mutation服务器提供其他CRUD功能的参数。

const schema = new GraphQLSchema({
    query: rootQuery,
    mutation: rootMutation
})
复制代码

要定义您的rootQuery,请将以下行添加到您的服务器文件中。rootQueryGraphQLObjectType定义对象的名称和描述的。它作为数据访问层 (DAL) 服务,定义对象字段,对象字段本身包含可用于从服务器获取数据的各种查询字段的定义。它还通过对数据库进行查询来解析这些字段并返回适当的数据。

// root query scope
const rootQuery = new GraphQLObjectType({
    name: "Query",
    description: "Root Query",
    fields: () => ({
        posts: {
            type: new GraphQLList(PostType),
            description: 'List of posts',
            resolve: () => Post.findAll()
        },
        users: {
            type: new GraphQLList(UserType),
            description: 'List of users',
            resolve: () => User.findAll()
        },
        post: {
            type: PostType,
            description: 'A single post',
            args: {
                id: { type: GraphQLInt }
            },
            resolve: (parent, args) => Post.findOne({
                where: {
                    id: args.id
                }
            })
        },
        user: {
            type: UserType,
            description: 'A single user',
            args: {
                id: { type: GraphQLInt }
            },
            resolve: (parent, args) => User.findOne({
                where: {
                    id: args.id
                }
            })
        }
    })
})
复制代码

您还可以定义您的与对象rootMutation非常相似的rootQuery对象。但是它有所不同,因为它用于改变或更改服务器上的数据。

const rootMutation = new GraphQLObjectType({
    name: "Mutation",
    description: "Root Mutation",
    fields: () => ({
        addUser: {
            type: UserType,
            description: 'Add a user',
            args: {
                name: { type: GraphQLNonNull(GraphQLString) }
            },
            resolve: (parent, args) => {
                const user = User.create({
                    name: args.name
                })
                return user
            } 
        },
        addPost: {
            type: PostType,
            description: 'Add a post',
            args: {
                title: { type: GraphQLNonNull(GraphQLString) },
                userId: { type: GraphQLNonNull(GraphQLInt) }
            },
            resolve: (parent, args) => {
                const post = Post.create({
                    title: args.title,
                    userId: args.userId
                })
                return post
            }
        }
    })
})
复制代码

therootQuery和 the都rootMutation定义了一些类型,例如 thePostTypeUserType分别代表您的帖子和用户的定义。rootQuery要定义这些与and有点相似的类型rootMutation,您应该输入以下内容并根据需要进行编辑

const UserType = new GraphQLObjectType({
    name: "User",
    description: "A User in our application",
    fields: () => ({
        id: { type: GraphQLNonNull(GraphQLInt) },
        name: { type: GraphQLNonNull(GraphQLString) },
        posts: {
            type: new GraphQLList(PostType),
            resolve: (user) => Post.findAll({
                where: {
                    userId: user.id
                }
            })
        }
    })
})
const PostType = new GraphQLObjectType({
    name: "Post",
    description: "A Post created by a user",
    fields: () => ({
        id: { type: GraphQLNonNull(GraphQLInt) },
        title: { type: GraphQLNonNull(GraphQLString) },
        userId: { type: GraphQLNonNull(GraphQLInt) },
        user: {
            type: UserType,
            resolve: (post) => User.findOne({
                where: {
                    id: post.userId
                }
            })
        }
    })
})
复制代码

测试服务器

当你的数据库正在运行并且上面的一切都按照你的需要准备就绪时,你可以继续npm run dev在你的终端会话中运行以启动服务器。您的 express 服务器应该可用,localhost:5000并且您可以与提供良好用户界面的GraphQL服务器进行交互。localhost:5000/graphql


相关文章
|
6月前
|
存储 监控 安全
Docker插件和扩展:深入Docker功能的完整指南
Docker作为一种流行的容器化技术,不仅令应用程序的部署更为便捷,同时也提供了丰富的插件和扩展机制,以满足更多复杂场景下的需求。本文将深入研究Docker的插件和扩展,提供更为详实和全面的示例代码,助力读者更好地理解和运用这些增强功能。
|
6月前
|
Java Docker 微服务
如何使用Docker和Docker Compose部署微服务
【2月更文挑战第12天】
730 0
|
1月前
|
缓存 Kubernetes 应用服务中间件
1分钟了解什么是docker和docker-compose?前后端必知必会技能GET啦
1分钟了解什么是docker和docker-compose?前后端必知必会技能GET啦
|
NoSQL API 数据库
YAPI介绍及Docker Compose部署指南
YApi 是高效、易用、功能强大的 api 管理平台,旨在为开发、产品、测试人员提供更优雅的接口管理服务。可以帮助开发者轻松创建、发布、维护 API,YApi 还为用户提供了优秀的交互体验,开发人员只需利用平台提供的接口数据写入工具以及简单的点击操作就可以实现接口的管理。
513 0
 YAPI介绍及Docker Compose部署指南
|
NoSQL 中间件 Java
Docker-Compose 搭建各种中间件
Docker-Compose 搭建各种中间件
264 3
|
应用服务中间件 Shell Linux
【Docker】微服务学习笔记五:Docker常用命令解析
【Docker】微服务学习笔记五:Docker常用命令解析
313 0
【Docker】微服务学习笔记五:Docker常用命令解析
|
存储 Kubernetes Ubuntu
面向WEB开发的Docker(三):安装Docker
Docker可以安装在Linux,macOS或Windows 10上。Docker Engine可通过Docker Desktop在各种Linux平台安装Docker, macOS安装Docker和Windows 10上安装Docker以静态二进制安装的形式使用。更多Docker安装指南可以参照官方文档介绍【Docker官方网站】,下面简单总结一下安装过程。
267 0
面向WEB开发的Docker(三):安装Docker
|
存储 NoSQL JavaScript
面向WEB开发的Docker(二):什么是Docker、镜像、编排?
什么是Docker容器?有什么的特点:轻量,在一台机器上运行的多个Docker容器可以共享这台机器的操作系统内核;它们能够迅速启动,只需占用很少的计算和内存资源。镜像是通过文件系统层进行构造的,并共享一些公共文件。这样就能尽量降低磁盘用量,并能更快地下载镜像。
266 0
面向WEB开发的Docker(二):什么是Docker、镜像、编排?
|
存储 Ubuntu Unix
Docker入门(2)-- Docker架构
docker 入门 docker 来源以及核心理念
166 0
|
关系型数据库 MySQL Java
【Docker】(一)Docker简介与核心概念
【Docker】(一)Docker简介与核心概念
145 0
【Docker】(一)Docker简介与核心概念