banner
Violet

Violet's Blog

A Web <Developer />. Code_ for Fun.
x
github
bilibili
email

初次使用GraphQL

本文首次发布于我的博客,原文地址vio.vin/article/use-puppeteer

GraphQL API 和 Restful API 類似,是一種查詢數據的語言,相比於 Restful API,GraphQL API 在查詢方面更加靈活,前後端代碼編寫也更加方便。本文將介紹如何構建 GraphQL API 應用程序,基於 SpringBoot3.0。如何測試 GraphQL API 接口是否可用,以及前端如何調用 GraphQL API 接口(以 Nuxt 項目為例)。

What is GraphQL#

以下內容摘自GraphQL Wiki

GraphQL 是一個開源的,面向 API 而創造出來的數據查詢操作語言以及相應的運行環境。於 2012 年仍處於 Facebook 內部開發階段,直到 2015 年才公開發布。 2018 年 11 月 7 日,Facebook 將 GraphQL 項目轉移到新成立的 GraphQL 基金會(隸屬於非營利性的 Linux 基金會)。

GraphQL 相較於 REST 以及其他 web service 架構提供了一種更加高效、強大和靈活的開發 web APIs 的方式。它通過由客戶端根據所需定義數據結構,同時由服務端負責返回相同數據結構的對應數據的方式避免了服務端大量冗餘數據的返回,但與此同時也意味著這種方式不能有效利用起查詢結果的 web 緩存。GraphQL 這種查詢語言所帶來的靈活性和豐富性的同時也增加了複雜性,導致簡單的 APIs 有可能並不適合這種方式。

Why use GraphQL#

  • 由前端決定返回哪些數據。這對於後端需要複雜格式的 JSON 特別有用,前端可以自行決定需要哪些數據,不需要哪些數據,減少冗餘數據同時也減少後端壓力。
  • 不僅可以查詢,還可以用於更新。雖然名稱為 Graph Query Language,但是 GraphQL 同樣可以用於創建、更新、刪除操作。

How to use#

通過調用 GraphQL 接口,向後端發送這樣的數據,注意這並不是 JSON

這裡傳入參數為 article id,用於查詢一篇文章

query Article {
  article(id: "2") {
    id
    title
    content
    author {
      id
      firstName
      lastName
    }
    tags {
      id
      name
      description
    }
  }
}

後端將根據請求中的數據結構返回對應數據

{
  "data": {
    "article": {
      "id": "2",
      "title": "title2",
      "content": "content2",
      "author": {
        "id": "author-2",
        "firstName": "Douglas",
        "lastName": "Adams"
      },
      "tags": [
        {
          "id": "2",
          "name": "Python",
          "description": "Python related articles"
        },
        {
          "id": "3",
          "name": "JavaScript",
          "description": "JavaScript related articles"
        }
      ]
    }
  }
}

也可以一次查詢多條數據

query Article {
  articles {
    id
    title
    content
    author {
      id
      firstName
      lastName
    }
    tags {
      id
      name
      description
    }
  }
}

Reponse

{
  "data": {
    "articles": [
      {
        "id": "1",
        "title": "title1",
        "content": "content1",
        "author": {
          "id": "author-1",
          "firstName": "Joshua",
          "lastName": "Bloch"
        },
        "tags": [
          {
            "id": "1",
            "name": "Java",
            "description": "Java related articles"
          },
          {
            "id": "2",
            "name": "Python",
            "description": "Python related articles"
          },
          {
            "id": "1",
            "name": "Java",
            "description": "Java related articles"
          }
        ]
      },
      {
        "id": "2",
        "title": "title2",
        "content": "content2",
        "author": {
          "id": "author-2",
          "firstName": "Douglas",
          "lastName": "Adams"
        },
        "tags": [
          {
            "id": "2",
            "name": "Python",
            "description": "Python related articles"
          },
          {
            "id": "3",
            "name": "JavaScript",
            "description": "JavaScript related articles"
          }
        ]
      }
    ]
  }
}

Build SpringBoot Application#

推薦 IDEA 先安裝 GraphQL 的相關插件,GraphQL,該插件提供了對schema.graphqls文件的語法高亮支持。

創建項目#

pom.xml 中需要加入以下依賴項

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-graphql</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

定義實體類#

實體類既可以和數據庫字段對應,可以是自定義的 VO。這裡使用了 record,也可以選擇使用傳統 class

public record Article(String id, String title, String content, String authorId, String tagId) {}

public record Author (String id, String firstName, String lastName) {}

public record Tag(String id, String name, String description) {}

查詢數據#

定義schema.graphqls#

schema.graphqls文件將用於定義 GraphQL 模式、描述數據結構和數據之間的關係、以及客戶端可以進行的操作。
文件將定義以下內容

  • 類型
  • 查詢
  • 關係

以下是一個具體的schema.graphqls文件

# 查詢的接口,客戶端可以訪問的內容
type Query {
    # id表示查詢的參數,Article為下面type中定義的返回的數據結構
    article(id: String): Article
    # 使用[Article]表示返回的是多條數據,以數組形式返回
    articles: [Article]
    tag(id: String): Tag
    tags: [Tag]
    author(id: String): Author
}

# 定義數據結構
type Author {
    id: String
    firstName: String
    lastName: String
}

# tags是多條,用[Tag]表示
type Article {
    id: String
    title: String
    content: String
    author: Author
    tags: [Tag]
}

type Tag {
    id: String
    name: String
    description: String
}

編寫 Controller#

GraphQL 同樣需要通過 Controller 來訪問。相比與 MVC 中使用@RequsetMapping,在 GraphQL 中應該使用@QueryMapping。並且該@QueryMapping不需要定義請求路徑,以下是一個具體示例

@QueryMapping#

@QueryMapping
public Collection<Article> articles()
{
    return queryService.listArticles();
}

此處方法名為articles(),需要和schema.graphqls文件中type Query中定義的名稱相同。

這樣當前端發送
query Article { articles { id } }時,會匹配到這一個接口。該方法返回type Article中所包含的所有屬性,之後GraphQL會根據前端傳入的參數,自動保留需要的數據。

例如在這個示例中,前端只請求了 id 和 title,所以只有 id 和 title 會被返回,其他的數據會被隱藏 / 刪除。

query Article {
  articles {
    id
    title
  }
}

查詢的參數需要添加@Argument注解,否則會報錯

@QueryMapping
public Article article(@Argument String id) {
    return queryService.getArticleById(id);
}

@SchemaMapping#

@QueryMapping@SchemaMapping是 GraphQL 最核心的兩個注解。@QueryMapping用於匹配請求地址,而@SchemaMapping將用於構建數據關係。

以一個具體的例子來說

@QueryMapping
public Article article(@Argument String id) {
    return queryService.getArticleById(id);
}

@SchemaMapping
public Collection<Tag> tags(Article article)
{
    return queryService.getTagsByArticleId(article.id());
}

@SchemaMapping
public Author author(Article article)
{
    return queryService.getAuthorByAuthorId(article.authorId());
}

tags()方法將用於查詢 Article 關聯的 Tag,所以該方法的參數為Article article,返回數據為多條 Tag。同理,author 用於查詢 Article 關聯的 Author,所以返回數據為 Author 類型。

保存數據#

定義schema.graphqls#

type Mutation {
    # !表示該參數為必須,:Article表示插入之後返回的數據類型
    createArticle(article: ArticleInput!): Article
}

input ArticleInput {
    title: String
    content: String
    authorId: String
    tagId: String
}

Controller#

@MutationMapping
public Article createArticle(@Argument("article") ArticleInput article) {
    return createService.createArticle(article);
}

調用該接口#

mutation {
  createArticle(
    article: {title: "Sample Title", content: "Sample Content", authorId: "author-3", tagId: "2"}
  ) {
    id
    title
    content
    author {
      id
      firstName
      lastName
    }
    tags {
      id
      name
      description
    }
  }
}

刪除 / 更新數據#

刪除數據同樣需要在Mutation中定義操作,: 後面為操作完成之後返回的類型,這裡不再詳細描述

type Mutation {
    deleteArticle(id: String!): Boolean
    updateArticle(id: String!, article: ArticleInput!): Article
}

後端項目地址https://github.com/lnbiuc/graphql-start

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。