使

使用 Amazon Neptune 图形数据库和 CDK 在 AWS 上构建 GraphQL API

2025-06-08

使用 Amazon Neptune 图形数据库和 CDK 在 AWS 上构建 GraphQL API

本教程的视频也可在 YouTube 上观看

在GraphQL 和图形数据库Paul Wilton概述了将图形数据库与 GraphQL 相结合的好处,以及如何将 GraphQL 模式与描述图形数据库所表示的图形中的数据的相同本体模型对齐。

Willian Lyon也倡导Grand Stack的理念,它将 React 和 Apollo 等技术与 GraphQL 和 Neo4j 图形数据库相结合。

作为一名 AWS 用户,我对如何利用由图数据库支持的 AppSync GraphQL API 非常感兴趣。市面上有很多不错的选择,包括Neo4jArangoDB等,我希望很快也能尝试一下,但这次构建我选择了Amazon Neptune

Neptune 是一项完全托管的图形数据库服务,可让您轻松构建和运行处理高度互联数据集的应用程序。Amazon Neptune 的核心是一个专门构建的高性能图形数据库引擎,经过优化,可存储数十亿个关系并以毫秒级延迟进行图形查询。

在本教程中,我将介绍如何使用 AWS CDK、AWS AppSync 和 AWS Lambda 构建由 Neptune 支持的 AppSync GraphQL API。

我们将使用 AWS Lambda 直接解析器来编写 API 的业务逻辑,该 API 将使用gremlin通过 Websockets 与 Amazon Neptune 进行交互

该项目的代码位于此处

先决条件

入门

首先,我们将使用 CDK CLI 初始化一个新项目。为此,请创建一个新的空文件夹,并使用 TypeScript 初始化一个新的 CDK 项目:

cdk init --language=typescript
Enter fullscreen mode Exit fullscreen mode

接下来,打开tsconfig.json并设置noImplicitAny为 false:

"noImplicitAny": false,
Enter fullscreen mode Exit fullscreen mode

现在让我们使用 npm 或 yarn 安装创建基础设施所需的依赖项:

yarn add @aws-cdk/aws-appsync @aws-cdk/aws-lambda @aws-cdk/aws-ec2 @aws-cdk/aws-neptune
Enter fullscreen mode Exit fullscreen mode

接下来,我们将在lib/your-project-name-stack.ts文件中创建我们的堆栈。

首先导入我们将要使用的 CDK 类和构造:

// lib/your-project-name-stack.ts
import * as cdk from '@aws-cdk/core'
import * as appsync from '@aws-cdk/aws-appsync'
import * as lambda from '@aws-cdk/aws-lambda'
import * as ec2 from '@aws-cdk/aws-ec2'
import * as neptune from '@aws-cdk/aws-neptune'
Enter fullscreen mode Exit fullscreen mode

注意 - 我们仅导入ec2以创建一个 VPC,我们将在其中放置我们的功能以及我们的 Neptune 实例。

创建 GraphQL API

现在让我们创建 GraphQL API。为此,请将以下代码行添加到堆栈中,位于 调用下方super

const api = new appsync.GraphqlApi(this, 'Api', {
  name: 'NeptuneAPI',
  schema: appsync.Schema.fromAsset('graphql/schema.graphql'),
  authorizationConfig: {
    defaultAuthorization: {
      authorizationType: appsync.AuthorizationType.API_KEY
    },
  },
})
Enter fullscreen mode Exit fullscreen mode

在这里,我们创建了一个名为的 API并设置了一些基本配置,包括位于graphql/schema.graphqlNeptuneAPI的 GraphQL 模式的位置

接下来,继续在根目录创建一个名为graphql的新文件夹,并在其中添加一个名为schema.graphql的文件。在这里,添加以下架构:

type Post {
  id: ID!
  title: String!
  content: String!
}

input PostInput {
  title: String!
  content: String!
}

type Query {
  listPosts: [Post]
}

type Mutation {
  createPost(post: PostInput!): Post
}

type Subscription {
  onCreatePost: Post
    @aws_subscribe(mutations: ["createPost"])
}
Enter fullscreen mode Exit fullscreen mode

该 API 将是一个非常基本的博客 API,允许我们创建和查询帖子

Lambda 和 VPC

接下来,在 API 代码下方创建 VPC 和 Lambda 函数:

const vpc = new ec2.Vpc(this, 'NeptuneVPC')

const lambdaFn = new lambda.Function(this, 'Lambda Function', {
  runtime: lambda.Runtime.NODEJS_14_X,
  handler: 'main.handler',
  code: lambda.Code.fromAsset('lambda-fns'),
  memorySize: 1024,
  vpc
})
Enter fullscreen mode Exit fullscreen mode

我们围绕 Lambda 设置了一些基本配置,包括运行时、内存大小以及入口代码(lambda-fns)和处理程序(main)的位置。

接下来,我们将 Lambda 函数作为数据源添加到 AppSync API,并为我们在 GraphQL 架构中定义的查询和变异创建解析器。在 Lambda 函数定义下方添加以下代码:

const lambdaDs = api.addLambdaDataSource('lambdaDatasource', lambdaFn);

lambdaDs.createResolver({
  typeName: "Query",
  fieldName: "listPosts"
})
lambdaDs.createResolver({
  typeName: "Mutation",
  fieldName: "createPost"
})
Enter fullscreen mode Exit fullscreen mode

创建 Neptune 数据库

最后一步是创建 Neptune 数据库。我们还将获取对读写端点引用,使其可用作环境变量,以便我们在 Lambda 函数中引用它们:

const cluster = new neptune.DatabaseCluster(this, 'NeptuneCluster', {
  vpc,
  instanceType: neptune.InstanceType.R5_LARGE
})

cluster.connections.allowDefaultPortFromAnyIpv4('Open to the world')

const writeAddress = cluster.clusterEndpoint.socketAddress;
const readAddress = cluster.clusterReadEndpoint.socketAddress

lambdaFn.addEnvironment('WRITER', writeAddress)
lambdaFn.addEnvironment('READER', readAddress)

// The next two lines are not required, they just log out the endpoints to your terminal for reference
new cdk.CfnOutput(this, 'readaddress', {
  value: readAddress
})
new cdk.CfnOutput(this, 'writeaddress', {
  value: writeAddress
})
Enter fullscreen mode Exit fullscreen mode

添加 Lambda 函数代码

当我们创建 Lambda 函数时,我们引用了位于lambda-fns目录中的代码,但我们还没有编写该代码。

首先,创建目录,初始化新的package.json文件,然后安装gremlin

mkdir lambda-fns

cd lambda-fns

npm init --y

yarn add gremlin

cd ..
Enter fullscreen mode Exit fullscreen mode

接下来在lambda-fns目录中创建以下 4 个文件:

  • Post.ts
  • 主目录
  • createPost.ts
  • 列表帖子.ts

让我们为每个文件创建代码:

Post.ts

type Post = {
  id: string;
  title: string;
  content: string;
}
export default Post
Enter fullscreen mode Exit fullscreen mode

createPost.ts

const gremlin = require('gremlin')
import Post from './Post'

const DriverRemoteConnection = gremlin.driver.DriverRemoteConnection
const Graph = gremlin.structure.Graph
const uri = process.env.WRITER

async function createPost(post: Post) {
    let dc = new DriverRemoteConnection(`wss://${uri}/gremlin`, {})
    const graph = new Graph()
    const g = graph.traversal().withRemote(dc)

    const data = await g.addV('posts').property('title',post.title).property('content', post.content).next()
    post.id = data.value.id
    dc.close()
    return post
}
export default createPost
Enter fullscreen mode Exit fullscreen mode

列表帖子.ts

const gremlin = require('gremlin')

const DriverRemoteConnection = gremlin.driver.DriverRemoteConnection
const Graph = gremlin.structure.Graph
const uri = process.env.READER

const listPosts = async () => {
    let dc = new DriverRemoteConnection(`wss://${uri}/gremlin`, {})
    const graph = new Graph()
    const g = graph.traversal().withRemote(dc)
    try {
      let data = await g.V().hasLabel('posts').toList()
      let posts = Array()

      for (const v of data) {
        const _properties = await g.V(v.id).properties().toList()
        let post = _properties.reduce((acc, next) => {
          acc[next.label] = next.value
          return acc
        }, {})
        post.id = v.id
        posts.push(post)
      }

      dc.close()
      return posts
    } catch (err) {
        console.log('ERROR', err)
        return null
    }
}

export default listPosts
Enter fullscreen mode Exit fullscreen mode

主目录

import createPost from './createPost';
import listPosts from './listPosts';
import Post from './Post';

type AppSyncEvent = {
  info: {
    fieldName: string
  },
  arguments: {
    post: Post
  }
}

exports.handler = async (event:AppSyncEvent) => {
  switch (event.info.fieldName) {
    case "createPost":
      return await createPost(event.arguments.post);
    case "listPosts":
      return await listPosts();
    default:
      return null;
  }
}
Enter fullscreen mode Exit fullscreen mode

main.ts中,我们切换event.info.fieldname触发函数的 GraphQL 查询或变异,并根据字段名称调用函数。

部署和测试

现在我们已经完成了代码编写,可以部署并测试了。为此,请运行构建,然后部署:

npm run build && cdk deploy
Enter fullscreen mode Exit fullscreen mode

部署完成后,您应该可以进行测试。为此,请访问AppSync 控制台,然后单击左侧菜单中的“查询” 。

执行以下查询来创建并查询来自 Neptune 的数据:

query listPosts {
  listPosts {
    id
    title
    content
  }
}

mutation createPost {
  createPost(post: {
    content:"Hello world"
    title: "My first post!!"
  }) {
    id
    title
    content
  }
}
Enter fullscreen mode Exit fullscreen mode

结论

本介绍绝不是深入探讨或指导如何查询和遍历 Neptune 或如何正确处理图形数据库中的数据,而是向您展示如何将所有部分组合在一起以设置基础设施和 API,这样您就可以开始使用这个堆栈。

为了了解更多信息,我将深入研究 gremlin、Neptune 的文档以及如何正确使用图形数据库的一般指南。

该项目的代码位于此处

鏂囩珷鏉ユ簮锛�https://dev.to/dabit3/building-a-graphql-api-on-aws-with-amazon-neptune-graph-database-and-cdk-428a
PREV
代码审查应礼貌还是直率?一劳永逸地解决这个问题
NEXT
再见 ReactNative,你好 Ionic。