实践2.使用 Next.js 构建 GraphQL 服务器

1,055 阅读9分钟

我们的最终目标是构建一个全栈应用程序,使我们能够以交互式图形可视化的形式探索数据。在这篇文章中,我们将继续朝着这个全栈目标迈进,专注于使用 Next.js API 路由、Neo4j GraphQL 库、Vercel 和 Neo4j Aura 启动和运行我们的 API 层。

Neo4j Bloom 的交互式图形可视化

我们的目标是构建一个 Web 应用程序,在使用社交网络数据时展示数据可视化的力量,在这种情况下,帮助我们发现有趣和相关的内容。在我们深入构建我们的全栈应用程序之前,有必要探索一下 Neo4j 开箱即用的图形数据可视化工具。

也许我们可能考虑的最相关的工具是Neo4j Bloom。Bloom 包含在 Neo4j Aura 和 Neo4j Desktop 中,允许用户在不编写 Cypher 的情况下直观地探索图形。Bloom 是一个独立的应用程序,使用户能够搜索模式、过滤、探索和共享图形可视化。

11.png

虽然 Bloom 是与 Neo4j 一起使用的强大工具,但它并不是我们想要的这个项目,因为我们想要构建一个可定制的可视化体验。

Next.js

Next.js是由Vercel构建和维护的全栈 React 框架。Next.js 包含许多开箱即用的特性,我们通常需要在 React 应用程序中设置这些特性——比如文件系统路由、服务器端渲染、API 路由等——这意味着我们可以专注于构建我们的应用程序,而不是样板设置和配置。

create-next-app

开始使用 Next.js 的最简单方法是使用create-next-appCLI。这是一个命令行工具,使我们能够快速开始构建新的 Next.js 应用程序。我们可以使用它来创建一个新的骨架 Next.js 项目或从许多示例 Next.js 项目中进行选择。

让我们使用它在 Lobsters Graph 存储库中启动一个新的 Next.js 应用程序:

npx create-next-app next

我们现在可以导航到该next目录,并运行yarn dev以启动为我们的 Next.js 应用程序提供服务的本地 Web 服务器。

在这篇文章中,我们将专注于为我们的应用程序构建 GraphQL API,而不是前端,因此我们今天不会讨论任何特定于 React 的内容。相反,我们将使用 Next.js 的 API 路由功能来构建我们的 GraphQL API。

Next.js 路由

Next.js 支持创建 API 端点以向我们的 Next.js 应用程序添加后端功能 - 毕竟它确实是一个全栈框架。要创建新的 API 路由,我们只需在其中创建一个新文件,pages/api该文件将映射到新的 API 端点。

我们创建的框架 Next.js 应用程序create-next-app包含一个 API 路由示例pages/api/hello.js

// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
​
export default function handler(req, res) {
  res.status(200).json({ name: 'John Doe' })
}

如果我们发出请求,localhost:3000/api/hello我们将返回一个简单的 JSON 对象:

{
    name: "John Doe"
}

让我们使用这个 API 路由功能将 GraphQL 服务添加到我们的 Next.js 应用程序。

在 Next.js API 路由中创建 GraphQL 服务器

按照Next.js 文档中链接的 GraphQL 示例,我们将使用该micro包并将apollo-server-micro简单的 GraphQL 服务器设置为 API 路由。

首先,我们将安装必要的依赖项:

yarn add apollo-server-micro micro graphql

Micro 是一个与 Next.js 配合得很好的 HTTP 服务器,更重要的是,它有一个 Apollo Server 实现。要使用 Apollo Server 创建 GraphQL 服务器,我们需要创建两件事:定义 API 中可用数据的 GraphQL 类型定义,以及包含实际解析 GraphQL 操作的逻辑的 GraphQL 解析器函数。Apollo Server 接受这两个输入,将它们组合成一个可执行的 GraphQL 模式,并处理服务于 GraphQL API 所涉及的 HTTP 网络层。

让我们创建简单的 GraphQL 类型定义和单个解析器函数来启动我们的 API 并作为 API 路由运行:

import { gql, ApolloServer } from "apollo-server-micro";
​
const typeDefs = gql`
  type User {
    id: ID
  }
​
  type Query {
    getUser: User
  }
`;
​
const resolvers = {
  Query: {
    getUser: () => {
      return {
        id: "Foo",
      };
    },
  },
};
​
const apolloServer = new ApolloServer({
  typeDefs,
  resolvers,
});
​
const startServer = apolloServer.start();
​
export default async function handler(req, res) {
​
  await startServer;
  await apolloServer.createHandler({
    path: "/api/graphql",
  })(req, res);
}
​
export const config = {
  api: {
    bodyParser: false,
  },
};

我们定义了一个 Query 字段getUser和一个User只有一个字段的类型,id以及一个返回单个硬编码User对象的解析器函数。

在 Apollo Server v3 中使用 GraphQL Playground

在以前版本的 Apollo Server 中,默认情况下,可以使用 GraphQL Playground 浏览器内工具来探索 GraphQL API。但是,GraphQL Playground 已经被弃用了一段时间,最新版本的 Apollo Server v3 改为链接到 GraphQL API 的“登陆页面”上托管的 Apollo Studio 工具(加载 GraphQL 端点时加载的页面)网络浏览器)。

我们可以通过以下更改将 GraphQL Playground 作为Apollo Server 3 的插件启用:

import { ApolloServerPluginLandingPageGraphQLPlayground } from "apollo-server-core";
​
...
​
const apolloServer = new ApolloServer({
  typeDefs,
  resolvers,
  playground: true,
  plugins: [ApolloServerPluginLandingPageGraphQLPlayground()],
});

现在,当我们localhost:3000/graphql/api在 Web 浏览器中加载时,我们应该会看到熟悉的 GraphQL Playground 工具。要验证我们的 GraphQL 服务器是否正常工作,我们可以运行以下查询:

{
    getUser {
        id
    }
}

我们应该看到getUser解析器函数返回的简单结果:

{
    data: {
        getUser: {
            id: "Foo"
        }
    }
}

现在让我们将 GraphQL 模式从占位符更新为对 Lobster 图形数据进行建模并与 Neo4j 一起使用的模式。

使用 Neo4j-GraphQL 库

Neo4j GraphQL 库允许我们构建由 Neo4j 支持的Node.js GraphQL API,而无需编写任何解析器。我们需要做的就是编写 GraphQL 类型定义来定义我们数据库的数据模型,然后 Neo4j GraphQL 库负责其余的工作——生成完整的 CRUD GraphQL API 和解析器,并将任意 GraphQL 操作转换为数据库查询。

12.png

首先,让我们安装几个额外的依赖项,Neo4j GraphQL 库和 Neo4j JavaScript 驱动程序:

yarn add @neo4j/graphql neo4j-driver

Neo4j GraphQL 库使用以下约定将 GraphQL 类型定义映射到属性图模型:

  • GraphQL 类型映射到属性图模型中的节点标签
  • GraphQL 标量字段映射到属性图模型中的节点属性
  • GraphQL 对象和对象数组字段映射到属性图模型中的关系
  • @relationship指令在 GraphQL 类型定义中用于对属性图模型中的关系类型和方向进行编码

应用这些约定,我们最终得到以下 GraphQL 类型定义,这些定义映射到 Neo4j 中的 Lobsters 属性图:

type User {
    username: String
    created: DateTime
    karma: Int
    about: String
    avatar: String
    articles: [Article] @relationship(type: "SUBMITTED", direction: OUT)
    invited: [User] @relationship(type: "INVITED_BY", direction: IN)
    invited_by: [User] @relationship(type: "INVITED_BY", direction: OUT)
  }
​
  type Article {
      id: ID
      url: String
      score: Int
      title: String
      comments: String
      created: DateTime
      user: User @relationship(type: "SUBMITTED", direction: IN)
      tags: [Tag] @relationship(type: "HAS_TAG", direction: OUT)
  }
​
  type Tag {
      name: String
      articles: [Article] @relationship(type: "HAS_TAG", direction: IN)
  }

现在我们将从占位符 GraphQL 模式中删除解析器函数,因为在使用 Neo4j GraphQL 库时我们不需要编写手动解析器,并将我们的 GraphQL 类型定义替换为我们上面编写的定义。

我们还将创建一个 Neo4j JavaScript 驱动程序实例来连接到我们的 Neo4j Aura 数据库,使用环境变量作为连接凭据,并将我们的 GraphQL 类型定义传递给Neo4jGraphQL类构造函数以生成我们的 GraphQL API。

我们还在 GraphQL 类型定义中使用该@exclude指令来防止将任何突变添加到模式中——我们希望这是一个只读 API,至少目前是这样。

// pages/api/graphql.js
import { gql, ApolloServer } from "apollo-server-micro";
import { ApolloServerPluginLandingPageGraphQLPlayground } from "apollo-server-core";
import {Neo4jGraphQL} from "@neo4j/graphql"
import neo4j from "neo4j-driver"
import 'ts-tiny-invariant' // importing this module as a workaround for issue described here: https://github.com/vercel/vercel/discussions/5846
​
​
const typeDefs = gql`
  type User @exclude(operations: [CREATE, UPDATE, DELETE]) {
    username: String
    created: DateTime
    karma: Int
    about: String
    avatar: String
    articles: [Article] @relationship(type: "SUBMITTED", direction: OUT)
    invited: [User] @relationship(type: "INVITED_BY", direction: IN)
    invited_by: [User] @relationship(type: "INVITED_BY", direction: OUT)
  }
​
  type Article @exclude(operations: [CREATE, UPDATE, DELETE]) {
      id: ID
      url: String
      score: Int
      title: String
      comments: String
      created: DateTime
      user: User @relationship(type: "SUBMITTED", direction: IN)
      tags: [Tag] @relationship(type: "HAS_TAG", direction: OUT)
  }
​
  type Tag @exclude(operations: [CREATE, UPDATE, DELETE]) {
      name: String
      articles: [Article] @relationship(type: "HAS_TAG", direction: IN)
  }
`;
​
const driver = neo4j.driver(
    process.env.NEO4J_URI,
    neo4j.auth.basic(process.env.NEO4J_USER, process.env.NEO4J_PASSWORD)
)
​
const neoSchema = new Neo4jGraphQL({typeDefs, driver})
​
const apolloServer = new ApolloServer({
  schema: neoSchema.schema,
  playground: true,
  introspection: true,
  plugins: [ApolloServerPluginLandingPageGraphQLPlayground()],
});
​
const startServer = apolloServer.start();
​
export default async function handler(req, res) {
  await startServer;
  await apolloServer.createHandler({
    path: "/api/graphql",
  })(req, res);
}
​
export const config = {
  api: {
    bodyParser: false,
  },
};

Next.js 支持使用文件设置环境变量.env,所以让我们创建一个.env.local文件,我们将在其中添加 Neo4j Aura 实例的凭据。我们还将设置DEBUG环境变量以启用 Neo4j GraphQL 库的调试日志记录。这将记录生成的 Cypher 查询以及其他内容,这有助于了解数据库查询是如何从 GraphQL 操作生成的。

// .env.local
NEO4J_USER=neo4j
NEO4J_URI=neo4j+s://YOUR NEO4J AURA URI HERE
NEO4J_PASSWORD=YOUR NEO4J AURA PASSWORD HERE
DEBUG=@neo4j/graphql:*

我们现在有一个本地运行的 GraphQL 端点localhost:3000/api/graphql,我们可以使用 GraphQL 从我们的 Neo4j Aura 数据库中获取数据。接下来,我们将在Vercel上部署 Next.js 应用程序,以便它可以公开访问。

部署到 Vercel

Vercel 是一个云平台,我们将使用它来构建和部署 Next.js 应用程序。前端 React 应用程序(一旦我们构建它!)将构建并托管在 CDN 上,我们的 GraphQL API 路由将自动部署为无服务器功能。

Vercel 与 GitHub 集成,因此一旦我们提交更改,我们就可以通过在 Vercel 中选择 GitHub 存储库来添加 Next.js 应用程序。因为我们使用了一些 monorepo 设置并且我们的 Next.js 应用程序不在存储库的根目录中,所以我们只需要告诉 Vercel 我们的 Next.js 应用程序的根目录就是该next目录。我们还将在 Vercel 项目配置中添加 Neo4j Aura 连接凭据作为环境变量、设置NEO4J_PASSWORDNEO4J_URI和的值。NEO4J_USER

一旦我们添加了我们的项目,Vercel 构建服务将从 GitHub 拉取我们的代码,构建项目并部署我们的 Next.js 应用程序(静态内容到 CDN,我们的 GraphQL API 到无服务器函数)。我们的项目会自动分配一个域和 SSL 证书!因为我们通过 GitHub 连接了我们的项目,所以任何提交和拉取请求都会触发另一个构建。每个构建都分配有自己唯一的 URL,这意味着拉取请求将被构建和部署为“预览构建”,我们可以在部署到我们的主域之前对其进行测试和共享。

image-20220707003142358.png

自动生成URL链接:flat-data-visualization.vercel.app/

我们已经在 Vercel 上启动、运行和部署了 GraphQL API,我们将开始构建前端应用程序,看看 React 中的图形数据可视化与 GraphQL。 查看项目源代码:github.com/Utopia-xxl/…