翻譯|《JavaScript Everywhere》第4章咱們的第一個GraphQL API(^_^)

翻譯 | JavaScript Everywhere》第4 咱們的第一個GraphQL API(^_^)前端

寫在最前面

你們好呀,我是毛小悠,是一位前端開發工程師。正在翻譯一本英文技術書籍。node

爲了提升你們的閱讀體驗,對語句的結構和內容略有調整。若是發現本文中有存在瑕疵的地方,或者你有任何意見或者建議,能夠在評論區留言,或者加個人微信:code_maomao,歡迎相互溝通交流學習。數據庫

(σ゚∀゚)σ..:*☆哎喲不錯哦express

第4章 咱們的第一個GraphQL API

根據推測,若是你正在閱讀本文,那麼你就是一我的。做爲人類,你有不少興趣和愛好,你也有家人、朋友、熟人、同窗和同事。這些人也有本身的社會關係、興趣和愛好。這些關係和興趣中有些重疊,有些沒有。總而言之,咱們每一個人都有咱們生活中人的關係圖。這些類型的互連數據正是GraphQL最初提出要在API開發中解決的挑戰。npm

經過編寫GraphQL API,咱們可以高效地鏈接數據,從而下降了複雜性和請求數量,同時使咱們可以準確地爲客戶提供所需的數據。聽起來對Notes應用程序來講有點過了,是嗎?也許是吧,可是正如你將看到的,GraphQL JavaScript生態系統提供的工具和技術均可以啓用和簡化全部類型的API開發。json

在本章中,咱們將使用apollo-server-express包來構建GraphQL API。爲此,咱們將探究GraphQL的基本主題,編寫GraphQL模式,編寫代碼以解析模式功能,並使用GraphQL Playground用戶界面訪問APIapi

將咱們的服務器轉換爲API(排序)

讓咱們經過使用如下命令將Express服務器變成GraphQL服務器來開始API開發。數組

apollo-server-express軟件包。瀏覽器

Apollo Server是一個開源GraphQL服務器庫,可與大量Node.js服務器框架一塊兒使用,包括ExpressConnectHapiKoa服務器

GraphQL API,可從Node.js應用程序中獲取數據,還提供了有用的工具,例如GraphQL Playground,一個可視化的輔助器,幫助咱們在開發中查看API

要編寫咱們的API,咱們將修改上一章中編寫的Web應用程序的代碼。讓咱們首先開始於apollo-server-express軟件包。

將如下內容添加到src/index.js文件的頂部:

const { ApolloServer, gql } = require('apollo-server-express');

如今咱們已經導入了apollo服務器,咱們將創建一個基本的GraphQL應用程序。

GraphQL應用程序由兩個主要組件組成:類型定義和解析器,用於解析針對數據執行的查詢和修改。

這聽起來像是胡話,但不要緊。

咱們將實現「 Hello World API響應,並將在咱們API的整個開發過程當中進一步探討這些GraphQL主題。

首先,讓咱們構建一個基本模式,將其存儲在一個名爲typeDefs的變量中。

該模式將描述一個名爲hello的查詢,該查詢將返回一個字符串:

// Construct a schema, using GraphQL schema language
 const typeDefs = gql`
   type Query {
     hello: String
   }
 `;

如今咱們已經設置告終構,咱們能夠添加一個解析器,該解析器將向用戶返回一個值。這只是一個簡單的函數,返回字符串「 Hello world!」:

// Provide resolver functions for our schema fields
 const resolvers = {
   Query: {
     hello: () => 'Hello world!'
   }
 };

最後,咱們將集成Apollo Server以提供GraphQL API

爲此,咱們將添加一些特定於Apollo Server的設置和中間件,並更新咱們的應用程序。

監聽代碼:

// Apollo Server setup
 const server = new ApolloServer({ typeDefs, resolvers });
 
 // Apply the Apollo GraphQL middleware and set the path to /api
 server.applyMiddleware({ app, path: '/api' });
 
 app.listen({ port }, () =>
   console.log(
     `GraphQL Server running at http://localhost:${port}${server.graphqlPath}`
   )
 );

綜上,咱們的src/index.js文件如今應以下所示:

const express = require('express');
 const { ApolloServer, gql } = require('apollo-server-express');
 
 // Run the server on a port specified in our .env file or port 4000
 const port = process.env.PORT || 4000;
 
 // Construct a schema, using GraphQL's schema language
 const typeDefs = gql`
   type Query {
     hello: String
   }
 `;
 
 // Provide resolver functions for our schema fields
 const resolvers = {
   Query: {
     hello: () => 'Hello world!'
   }
 };
 
 const app = express();
 
 // Apollo Server setup
 const server = new ApolloServer({ typeDefs, resolvers });
 
 // Apply the Apollo GraphQL middleware and set the path to /api
 server.applyMiddleware({ app, path: '/api' });
 
 app.listen({ port }, () =>
   console.log(
     `GraphQL Server running at http://localhost:${port}${server.graphqlPath}`
   )
 );

若是你沒有運行nodemon進程,則能夠直接進入瀏覽器。不然,你必須在終端應用程序中輸入npm運行dev以啓動服務器。而後訪問http://localhost:4000/api,在這裏你會看到GraphQL Playground(圖4-1)。

Web應用程序與Apollo Server捆綁在一塊兒,是使用GraphQL的巨大好處之一。從這裏,你能夠運行GraphQL查詢和修改並查看結果。

你也能夠單擊「Schema」選項卡來訪問爲API自動建立的文檔。

image-20201120062343873

4-1GraphQL Playground

注意

GraphQL Playground具備深色的默認語法主題。在整本書中,我將使用「淺色」主題以提升對比度。

這能夠在GraphQL Playground的設置中進行配置,能夠經過單擊齒輪圖標進行訪問。如今,咱們能夠針對咱們的GraphQL API編寫查詢。爲此,在GraphQL Playground中鍵入如下內容:

query {
   hello
 }

當你單擊「播放」按鈕時,查詢應返回如下內容(圖4-2):

{
   "data": {
     "hello": "Hello world!"
   }
 }

image-20201120062638814

4-2。你好查詢

就是這樣!如今,咱們已經能夠經過GraphQL Playground訪問有效的GraphQL API。咱們的API會查詢hello,並返回字符串Hello world!。

更重要的是,咱們如今具備了功能構建齊全的API的結構。

😙

GraphQL基礎

在上一節中,咱們深刻探討並開發了咱們的第一個API,先讓咱們花一些時間後退一步來看看GraphQL API的不一樣部分。

GraphQL API的兩個主要構建模塊是模式和解析器。

經過理解這兩個組件,能夠更有效地將它們應用於API設計和開發。

Schemas模式是咱們的數據和交互的書面表示。

經過請求一個模式,GraphQL規範了咱們的API請求。

這是由於你的API只能返回數據並執行架構中定義的交互。

GraphQL模式的基本組成部分是對象類型。

在前面的示例中,咱們建立了一個GraphQL對象類型的Query,帶有一個hello字段,該對象返回了標準的String類型。

GraphQL包含五種內置標量類型:

  • :String

    具備UTF-8字符編碼的字符串

  • 布爾型Boolean

    正確或錯誤的值

  • 整數Int

    32位整數

  • 浮點型Flaot

    浮點值

  • ID

    惟一標識符

使用這些基本組件,咱們能夠爲API構建一個模式。

咱們首先定義類型。假設咱們正在爲披薩菜單建立一個API

這時,咱們能夠定義披薩的GraphQL模式類型,以下所示:

type Pizza {
 }

如今,每一個披薩餅都有惟一的ID,大小(例如小,中或大),切片數和可選的澆頭。

Pizza模式可能看起來像這樣:

type Pizza {
  id: ID
  size: String
  slices: Int
  toppings: [String]
}

在此架構中,某些字段值是必需的(例如ID,大小和切片),而其餘字段值則是可選的(例如配料)。咱們可使用感嘆號來表示字段必須包含一個值。

讓咱們更新結構以表示所需的值:

type Pizza {
  id: ID!
  size: String!
  slices: Int!
  toppings: [String]
}

在本書中,咱們將編寫一個基本模式,這將使咱們可以執行常見API中的絕大多數操做。若是你想探索全部的GraphQL模式選項,建議你閱讀GraphQL模式文檔。

解析器Resolvers

GraphQL API的第二部分是解析器。

解析程序徹底執行其名稱所暗示的操做;他們解析API而後獲取用戶已請求的數據。

咱們將首先在結構中定義這些解析器,而後在JavaScript代碼中實現邏輯,以編寫這些解析器。

咱們的API將包含兩種類型的解析器:查詢和修改。

查詢

查詢從API請求中獲取所需格式的特定數據。

在咱們假設的披薩API中,咱們能夠編寫一個查詢,該查詢將返回菜單上披薩的完整列表,而另外一個查詢將返回有關單個披薩的詳細信息。而後查詢將返回一個對象,其中包含API中用戶請求的數據。查詢從不修改數據,僅訪問數據。

修改

當咱們想要修改API中的數據時,咱們使用一個修改。

在咱們的披薩示例中,咱們可能編寫了一個修改,該修改能夠更改給定披薩的配料,而另外一個修改則能夠調整切片數。

與查詢相似,也指望修改以對象的形式返回結果,一般是所執行操做的最終結果。

調整咱們的API

如今你已經對GraphQL的組件有了很好的瞭解,讓咱們爲Notes應用程序調整咱們的初始API代碼。

首先,咱們將編寫一些代碼來閱讀和建立筆記。咱們須要作的第一件事是爲API提供一些數據。讓咱們建立一個「筆記」對象數組,將其用做API提供的基本數據。隨着項目的發展,咱們將用數據庫替換這個數據。

如今,咱們將數據存儲在一個名爲notes的變量中。數組中的每一個筆記將是一個具備三個屬性(idcontentauthor)的對象:

let notes = [
  { id: '1', content: 'This is a note', author: 'Adam Scott' },
  { id: '2', content: 'This is another note', author: 'Harlow Everly' },
  { id: '3', content: 'Oh hey look, another note!', author: 'Riley Harrison' }
];

如今咱們有了一些數據,咱們將調整咱們的GraphQL API來使用它。

讓咱們從關注咱們的模式開始。咱們的模式是GraphQL對咱們的數據以及如何與之交互的表示。已知咱們會有筆記,這些筆記將被查詢和修改。這些筆記目前將包含ID、內容和做者3個字段。

讓咱們在typeDefs GraphQL模式中建立一個相應的筆記類型。

如下表示咱們API中筆記的屬性:

type Note {
  id: ID!
  content: String!
  author: String!
}

如今,讓咱們添加一個查詢,該查詢將容許咱們檢索全部筆記的列表。

讓咱們更新一個筆記查詢,這將返回筆記對象的數組:

type Query {
  hello: String!
  notes: [Note!]!
}

如今,咱們能夠更新解析器代碼以執行返回數據數組的工做。

讓咱們更新包括如下筆記解析器的查詢代碼,該解析器返回原始數據對象:

Query: {
    hello: () => 'Hello world!',
    notes: () => notes
  },

若是如今切換到運行在http://localhost:4000/apiGraphQL Playground,咱們能夠測試筆記查詢。

爲此,請鍵入如下查詢:

query {
  notes {
    id
    content
    author
  }
}

而後,當你單擊「播放」按鈕時,應該看到返回的數據對象,其中包含數據數組(圖4-3)。

image-20201120062650769

4-3。筆記查詢。

在嘗試GraphQL最酷的方面之一是咱們能夠刪除任何請求的字段,例如idauthor。當咱們這樣作時,API精確地返回咱們所請求的數據。

這樣,使用數據的客戶端能夠控制每一個請求中發送的數據量,並將該數據限制在所需的範圍內(圖4-4)。

4-4。筆記查詢,僅請求內容數據。

如今咱們能夠查詢完整的筆記列表,讓咱們編寫一些代碼,使咱們能夠查詢單個筆記。

從用戶界面的角度,你能夠想象這樣作的用處,能夠顯示包含單個特定筆記的視圖。爲此,咱們但願請求帶有特定id值的筆記。這要求咱們使用GraphQL模式中的參數。參數容許API使用者將特定的值傳遞給解析器函數,從而提供解析所需的信息。讓咱們添加一個筆記查詢,該查詢將使用id類型的ID做爲參數。

咱們將typeDefs中的Query對象更新爲如下內容,其中包括新的筆記查詢:

type Query {
  hello: String
  notes: [Note!]!
  note(id: ID!): Note!
}

更新結構後,咱們已經能夠編寫在查詢解析器後返回的筆記了。如今咱們須要可以讀取API用戶的參數值。

有用信息

Apollo Server將如下有用的參數傳遞給咱們的解析器功能:

  • parent

    父查詢的結果,在嵌套查詢時頗有用。

  • args

    這些是用戶在查詢中傳遞的參數。

  • context

    信息從服務器應用程序傳遞到解析器功能。

    這可能包括諸如當前用戶或數據庫信息之類的信息。

  • info

    有關查詢自己的信息。

咱們將根據須要在代碼中進一步探索這些內容。若是你感到好奇,能夠在Apollo服務器的文檔中瞭解有關這些參數的更多信息。

如今,咱們僅須要第二個參數args中包含的信息。

筆記查詢將筆記ID做爲參數,在咱們的筆記對象數組中找到它。將如下內容添加到查詢解析器代碼中:

note: (parent, args) => {
  return notes.find(note => note.id === args.id);
}

解析器代碼如今應以下所示:

const resolvers = {
  Query: {
    hello: () => 'Hello world!',
    notes: () => notes,
    note: (parent, args) => {
      return notes.find(note => note.id === args.id);
    }
  }
};

運行咱們的查詢,讓咱們返回到Web瀏覽器並訪問位於http://localhost:4000/apiGraphQL Playground

如今,咱們能夠查詢具備特定ID的筆記,以下所示:

query {
  note(id: "1") {
    id
    content
    author
  }
}

運行此查詢時,你應該收到帶有請求的id值的筆記的結果。若是你嘗試查詢不存在的筆記,則應收到結果爲null的結果。要對此進行測試,請嘗試更改id值以返回不一樣的結果。

讓咱們經過使用GraphQL修改引入建立新筆記的功能來強化咱們的初始API代碼。在這種修改中,用戶將傳遞筆記的內容。

如今,咱們將對筆記的做者進行編碼。

讓咱們首先使用Mutation類型更新咱們的typeDefs模式,咱們將其稱爲newNote

type Mutation {
  newNote(content: String!): Note!
}

如今,咱們將編寫一個修改解析器,它將筆記內容做爲參數,將筆記存儲爲對象,而後將其添加到notes數組中。咱們將Mutation對象添加到解析器。

Mutation對象中,咱們將添加一個名爲newNote的函數,該函數具備parentargs參數。在此函數中,咱們將使用參數content並建立一個具備idcontentauthor鍵的對象。

你可能已經注意到,這與筆記的當前模式匹配。而後,咱們將該對象加入到notes數組並返回該對象。返回的對象容許GraphQL修改接收預期格式的響應。

繼續並編寫如下代碼:

Mutation: {
  newNote: (parent, args) => {
    let noteValue = {
      id: String(notes.length + 1),
      content: args.content,
      author: 'Adam Scott'
    };
    notes.push(noteValue);
    return noteValue;
  }
}

咱們的src/index.js文件如今將以下所示:

const express = require('express');
const { ApolloServer, gql } = require('apollo-server-express');

// Run our server on a port specified in our .env file or port 4000
const port = process.env.PORT || 4000;

let notes = [
  { id: '1', content: 'This is a note', author: 'Adam Scott' },
  { id: '2', content: 'This is another note', author: 'Harlow Everly' },
  { id: '3', content: 'Oh hey look, another note!', author: 'Riley Harrison' }
];

// Construct a schema, using GraphQL's schema language
const typeDefs = gql`
  type Note {
    id: ID!
    content: String!
    author: String!
  }

  type Query {
    hello: String
    notes: [Note!]!
    note(id: ID!): Note!
  }

  type Mutation {
    newNote(content: String!): Note!
  }
`;

// Provide resolver functions for our schema fields
const resolvers = {
  Query: {
    hello: () => 'Hello world!',
    notes: () => notes,
    note: (parent, args) => {
      return notes.find(note => note.id === args.id);
    }
  },
  Mutation: {
    newNote: (parent, args) => {
      let noteValue = {
        id: String(notes.length + 1),
        content: args.content,
        author: 'Adam Scott'
      };
      notes.push(noteValue);
      return noteValue;
    }
  }
};

const app = express();

// Apollo Server setup
const server = new ApolloServer({ typeDefs, resolvers });

// Apply the Apollo GraphQL middleware and set the path to /api
server.applyMiddleware({ app, path: '/api' });

app.listen({ port }, () =>
  console.log(
    `GraphQL Server running at http://localhost:${port}${server.graphqlPath}`
  )
);

更新結構和解析器以接受修改後,讓咱們在GraphQL Playgroundhttp://localhost:4000/api上進行嘗試。

Playground上,單擊+號以建立一個新選項卡,並按以下所示編寫變量:

mutation {
  newNote (content: "This is a mutant note!") {
   content
   id
   author
  }
}

當你單擊「播放」按鈕時,你應該會收到包含新筆記的內容、ID和做者的結果。

你還能夠經過從新運行notes查詢來查看該修改是否起做用。

爲此,請切換回包含該查詢的GraphQL Playground選項卡,或鍵入如下內容:

query {
  notes {
    content
    id
    author
  }
}

運行此查詢時,你如今應該看到四個筆記,包括最近添加的筆記。

數據存儲

咱們將數據存儲在內存中。這意味着,只要咱們從新啓動服務器,就會丟失該數據。在下一章中,咱們將使用數據庫來持久化數據。

如今,咱們已經成功實現了查詢和修改解析器,並在GraphQL Playground用戶界面中對其進行了測試。

結論

在本章中,咱們已經使用apollo-server-express模塊成功構建了GraphQL API。如今,咱們能夠對內存中的數據對象運行查詢和修改。此設置爲咱們提供了構建任何API的堅實基礎。在下一章中,咱們將探討使用數據庫持久化數據的能力。

若是有理解不到位的地方,歡迎你們糾錯。若是以爲還能夠,麻煩您點贊收藏或者分享一下,但願能夠幫到更多人。

相關文章
相關標籤/搜索