grahql极简入门教程(基于react+graphql-yoga+urql)②

950 阅读7分钟

本文是graphql极简入门教程的第二篇,基于prisma及sqlite,让大家通过graphql的playground创建及查询数据

graphql极简入门教程目录:

👉🏻点击进入本教程github仓库

创建/展示数据

在完成了初步的了解后,现在让我们开始实现一个简单版本的hackernews,下面是hackernews的截图: image-20230204003223534

在hackernews的列表展示中,每个链接包含以下的基础内容:

  • 链接描述(description
  • 链接地址(url
  • 创建时间(createdAt

基于上面的分析,可以创建下面的Graphql Scheme:

// ①
type Link {
    id: ID!
    url: String!
    description: String!
    createdAt: DateTime!
}

// ②
scalar DateTime

// ③
type Feed {
    links: [Link!]!
}

①中创建了一个Link(链接)类型,这个类型中有:

  • id:链接id,使用graphql中的ID类型标记,不为空
  • url:链接地址,不为空的字符串
  • description:链接描述:不为空的字符串
  • createdAt: 创建时间,使用graphql中的DateTime类型标记,不为空。

注意:DateTime类型标记需要额外添加②中的scalar DateTime声明才可以使用

③创建了一个Feed(数据流,类似于列表、瀑布流)类型,[Link!]!代表这个数据要么是一个空数组,要么是一个包含Link对象的数组

具体的数据类似于下面👇🏻这种形式

let link = {
  id: 1,
  url: 'www.howtographql.com',
  description: 'Fullstack tutorial for GraphQL',
  createAt: '2023-02-03T16:59:46.674Z'
}

let feed = [link];

现在把这个已经完成的Graphql Schema写到后端文件中,在src/server目录下,创建schema.graphql文件,写入下面的内容:

type Link {
    id: ID!
    url: String!
    description: String!
    createdAt: DateTime!
}

type Feed {
    links: [Link!]!
}

scalar DateTime

目前已经定义好了数据的形式,接下来需要实现具体获取数据的方法。

大家回想一下第一章最开头demo例子中,是直接在resolvers里写死了返回值:

resolvers: {        
    Query: {            
        hello: () => 'world'       
    }
}

但是在实际的生产环境中,通常需要从数据库中获取数据。这里为了降低复杂度,采用sqlite作为数据库,并且搭配prisma(无需编写SQL,直接通过prisma里提供的api即可增删改查数据)来使用。

安装prisma

在项目根目录下,输入以下指令安装:

npm install prisma --save-dev
npm install @prisma/client

src/server下,创建prisma文件夹,新建schema.prisma

// ①
datasource db {
  provider = "sqlite"
  url      = "file:./dev.db"
}

// ②
generator client {
  provider = "prisma-client-js"
}

// ③
model Link {
  id          Int      @id @default(autoincrement())
  createdAt   DateTime @default(now())
  description String
  url         String
}

① 设置使用SQLite作为数据库,并且指定数据库位置。

② 设置你想生成的Prisma客户端,这里我们生成js类型的客户端prisma-client-js

③ 设置数据模型,在这里设置一个Link模型,与上面我们定义的Link类型一一对应

从项目根目录进入src/server目录下,执行第一次迁移:

cd src/server
npx prisma migrate dev

你会收到提示,此时输入"init",并回车即可

第一次初始化数据库迁移

执行下面的指令,生成prisma客户端

npx prisma generate

接着需要将prisma挂载在graphql的上下文中,你可以把它理解为类似于react中的全局上下文,让我们方便在后续的操作中从数据库里增删改查

src/server/index.js文件中,添加下面的内容👇🏻:

const createServer = require('node:http').createServer;
const createYoga = require('graphql-yoga').createYoga;
const createSchema = require('graphql-yoga').createSchema;

// ①
+ const {PrismaClient} = require("@prisma/client");

const schema = createSchema({
    typeDefs: `
        type Query {
          hello: String
        }
    `,
    resolvers: {
        Query: {
            hello: () => 'world'
        }
    }
})

// ②
+ const prisma = new PrismaClient();

// 基于graphql的scheme,创建一个graphql-yoga的实例
const yoga = createYoga({
    schema,
    // ③
+    context: req => ({
+        ...req,
+        prisma
+    })
})

// 基于实例化后的yoga,创建一个server
const server = createServer(yoga)

// 启动server
server.listen(4000, () => {
    console.info('Server is running on http://localhost:4000/graphql')
})

在①中我们引入了prisma的客户端,并在②中进行了实例化。

创建graphql-yoga实例时,将prisma挂在了context对象上,方便后面调用。

恭喜你(^▽^)🎉🎉🎉已经完成了数据库的初步设置。

写入数据

在日常的开发过程中,你一定接触过通过post请求来创建数据、put请求修改数据。在graphql中,我们将这些改变原有数据内容的操作统一称作Mutation(突变)

因此,假如想要创建一个链接,可以有如下的定义

type Mutation {
    post(url: String!, description: String!): Link!
}

上面定义的含义:这是一个Mutation类型,接着我们在这个里面定义了一个post,post一定会传入不为空的url和description字符串参数。创建成功后的返回值是一个Link类型的数据。

将上面的内容添加到:src/server/schema.graphql中:

type Link {
    id: ID!
    url: String!
    description: String!
    createdAt: DateTime!
}

type Feed {
    links: [Link!]!
}

scalar DateTime

+ type Mutation {
+    post(url: String!, description: String!): Link!
+ }

定义好数据类型,接下来就需要编写相关的resolvers来具体实现创建链接的功能。

src/server/中创建resolvers文件夹统一存放所有的resolver,并新增Mutation.js

// 文件路径:src/server/resolvers/Mutation.js

// ①
async function post(parent, args, context) {
    // ②
    const newLink = await context.prisma.link.create({
        data: {
            url: args.url,
            description: args.description,
        }
    });

    // ③
    return newLink;
}

module.exports = {
    post,
}

在①中创建了一个post方法,传参中第一个参数parent暂时不做了解,第二个参数代表用户调用graphql时传入的参数,第三个参数代表上下文。

②中通过上下文,拿到了之前挂载的prisma,并且通过提供的create方法,创建了一个链接

最后在③中,返回了新建的链接数据。

到这里,就完成了创建新链接数据的的准备工作,接下来开始读取数据的准备

读取数据

请同学们先不要看下面的内容,自己先想一想该如何写读取数据的数据定义。

在读取数据时,只要定义好返回数据的数据结构即可。因此在src/server/schema.graphql中添加下面的内容:

+ type Query {
+     feed: Feed!
+ }

type Link {
    id: ID!
    url: String!
    description: String!
    createdAt: DateTime!
}

type Feed {
    links: [Link!]!
}

scalar DateTime

type Mutation {
    post(url: String!, description: String!): Link!
}

并且在src/server/resolvers文件夹下新建Query.js文件,添加下面的内容

// 文件路径:src/server/resolvers/Query.js
async function feed(parent, args, context) {
    // ①
    const links = await context.prisma.link.findMany();

    return {
        links,
    };
}

module.exports = {
    feed
};

在①中,我们使用prisma提供的findMany方法,获取所有的链接。并将查询的数据,按照之前定义的数据结构返回。

使用playground创建及获取数据

此时已经定义好了如何创建及获取链接的数据类型以及具体的实现方法。现在需要修改src/server/index.js文件,引入这些内容就可以了,具体如下:

const createServer = require('node:http').createServer;
const createYoga = require('graphql-yoga').createYoga;
const createSchema = require('graphql-yoga').createSchema;

const {PrismaClient} = require("@prisma/client");

+ const fs = require("fs");
+ const path = require("path");

+ const Mutation = require("./resolvers/Mutation");
+ const Query = require("./resolvers/Query");

+ const resolvers = {
+     Mutation,
+     Query
+ }

+ const schema = createSchema({
+     typeDefs: fs.readFileSync(path.join(__dirname, 'schema.graphql'), 'utf-8'),
+     resolvers: resolvers
+ })

const prisma = new PrismaClient();

// 基于graphql的scheme,创建一个graphql-yoga的实例
const yoga = createYoga({
    schema,
    context: req => ({
        ...req,
        prisma
    })
})

// 基于实例化后的yoga,创建一个server
const server = createServer(yoga)

// 启动server
server.listen(4000, () => {
    console.info('Server is running on http://localhost:4000/graphql')
})

此时,已经传入之前定义好的graphql schema、resolver

运行下面指令,启动服务

npm run start:server

在浏览器中,打开链接:http://localhost:4000/graphql,并在左侧输入创建新链接的语句

mutation {
  post(url: "juejin.cn", description: "稀土掘金") {
    id
  }
}

创建数据

由于定义了创建成功后,只返回id,因此返回的结构体中只有id字段,如果你还希望返回更多字段信息,也可以这么写:

mutation {
  post(url: "juejin.cn", description: "稀土掘金") {
    id
    url
    description
    createdAt
  }
}

此时就可以得到更多的返回值

更多返回值示例

接下来,输入下面的内容,我们来查找刚刚添加进去的内容

query {
  feed {
    links {
      id
      url
      description
      createdAt
    }
  }
}

查找内容

此时就完成了数据的添加和查找。接下来将在react项目中,完成上面在playground中的操作,串联起来前后端的流程。

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 2 天,点击查看活动详情