10 分钟搭建一个 GraphQL 后端项目

这次我们通过快速搭建一个 GraphQL 项目来学习如何使用它。

项目使用的技术栈:

项目依赖

项目初始化

  • 终端前往存放项目的文件夹,创建项目:
$ mkdir tinylearn && cd tinylearn
  • 项目初始化:
$ npm init -y
  • 用编辑器打开项目(笔者使用的是 VSCode):
  • 安装依赖包,终端输入:
$ npm install apollo-server@2.13.1 \
    class-validator@0.12.2 \
    graphql@14.6.0 \
    reflect-metadata@0.1.13  \
    type-graphql@0.17.6
$ npm install --save-dev @types/graphql@14.0.7 \
    @types/node@12.12.39 \
    ts-node@8.10.1 \
    typescript@3.9.2
  • 在项目根目录加入文件 tinylearn/tsconfig.json:
{
  "compilerOptions": {
    "target": "es2018",
    "module": "commonjs",
    "lib": [
      "es2018",
      "esnext.asynciterable"
    ],
    "strictFunctionTypes": true,
    "strictNullChecks": true,
    "noImplicitAny": true,
    "noImplicitReturns": true,
    "noImplicitThis": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
}

tsconfig.jsonTypescript 的配置文件,它指定编译项目所需的根文件和编译器选项。

Hello world

准备工作已经做完了,接下来我们写点代码。

  • 创建 tinylearn/src/index.ts 文件,并写入相关代码:

index.ts:

console.log('Hello world!');
  • 修改 package.json:
{
  "name": "tinylearn",

  ...

  "scripts": {
+    "start": "ts-node src"  
-    "test": "echo \"Error: no test specified\" && exit 1"
  },

  ...

}

  • 运行以下命令,启动项目:
$ npm start

结果如下:

恭喜 Hello world 跑起来了!

引入 GraphQL

接下来我们要写一些逻辑了。

  • 修改 src/index.ts 为:
import "reflect-metadata"
import { buildSchema, ObjectType, Field, ID, Resolver, Query } from "type-graphql";
import { ApolloServer } from "apollo-server";

@ObjectType()
class Post {

  @Field(type => ID)
  id: string;

  @Field()
  created: Date;

  @Field()
  content: string;
}

@Resolver(Post)
class PostResolver {

  @Query(returns => [Post])
  async posts(): Promise<Post[]> {
    return [
      {
        id: "0",
        created: new Date(),
        content: '提供基于GraphQL API的数据查询及访问,「Hasura」获990万美元A轮...'
      },
      {
        id: "1",
        created: new Date(),
        content: '为什么GraphQL是API的未来'
      },
      {
        id: "2",
        created: new Date(),
        content: 'Netflix:我们为什么要将 GraphQL 引入前端架构?'
      },
    ]
  }
}

async function main() {

  try {

    const schema = await buildSchema({
      resolvers: [PostResolver],
      dateScalarMode: 'timestamp'
    });


    const server = new ApolloServer({
      schema,
      playground: true
    });

    const { url } = await server.listen(4444);

    console.log(`GraphQL Playground available at ${url}`);

  } catch (error) {
    console.error(error);
  }
}


main();
  • 再次运行项目:
$ npm start

结果:


玩一玩

浏览器访问网址 http://localhost:4444/:

我们先点击右侧 SCHEMA 按钮:

然后在左侧输入:

它会有自动补全,所以输入应该会很轻松。

点击中间圆形的播放按钮:

其实这个页面就像是 Postman,左边可以看作是前端发请求的配置,中间是服务器返回的 json,右边可以看作是后端的 API 文档。

我们改变参数再发请求试试:

整个过程就像是后端定义了一个 json graph(也就是 schema),前端根据需求拉取这个 graph 的部分数据。

我们再来回顾一下代码 tinylearn/src/index.ts

@ObjectType()
class Post {

  @Field(type => ID)
  id: string;

  @Field()
  created: Date;

  @Field()
  content: string;
}

@Resolver(Post)
class PostResolver {

  @Query(returns => [Post])
  async posts(): Promise<Post[]> {
    return [
      {
        id: "0",
        created: new Date(),
        content: '提供基于GraphQL API的数据查询及访问,「Hasura」获990万美元A轮...'
      },
      {
        id: "1",
        created: new Date(),
        content: '为什么GraphQL是API的未来'
      },
      {
        id: "2",
        created: new Date(),
        content: 'Netflix:我们为什么要将 GraphQL 引入前端架构?'
      },
    ]
  }
}

后端定义 Schema:

前端根据需求,参考 Schema 后发起请求:

前端获取返回值:

另外,这个 json graph (schema) 是一个严格的类型系统:

  • posts: [Posts!]! - Post 数组
  • id: ID! - 字符串 ID
  • created: Timestamp! - 时间戳
  • content: String! - 字符串

我们检查一下返回值的类型:

确实如此,返回值的类型和 Schema 里面一样:

postsPost 数组。

id 是字符串 ID。
created 是时间戳。
content 是字符串。

大家对 GraphQL 是否有了新的认识?欢迎给我留言。

未完待续

大家可能还是有很多疑问,不用心急,我们慢慢来。

下一篇 《Flutter 如何接入 GraphQL》 会介绍如何使用 Flutter(移动端大热) 接入本章写好的接口。让前端真正的跑起来,这样我们就会对 GraphQL 有更完整的认知。

Flutter Demo:

源码

代码地址
tinylearn/src/index.ts:

import "reflect-metadata"
import { buildSchema, ObjectType, Field, ID, Resolver, Query } from "type-graphql";
import { ApolloServer } from "apollo-server";

@ObjectType()
class Post {

  @Field(type => ID)
  id: string;

  @Field()
  created: Date;

  @Field()
  content: string;
}

@Resolver(Post)
class PostResolver {

  @Query(returns => [Post])
  async posts(): Promise<Post[]> {
    return [
      {
        id: "0",
        created: new Date(),
        content: '提供基于GraphQL API的数据查询及访问,「Hasura」获990万美元A轮...'
      },
      {
        id: "1",
        created: new Date(),
        content: '为什么GraphQL是API的未来'
      },
      {
        id: "2",
        created: new Date(),
        content: 'Netflix:我们为什么要将 GraphQL 引入前端架构?'
      },
    ]
  }
}

async function main() {

  try {

    const schema = await buildSchema({
      resolvers: [PostResolver],
      dateScalarMode: 'timestamp'
    });


    const server = new ApolloServer({
      schema,
      playground: true
    });

    const { url } = await server.listen(4444);

    console.log(`GraphQL Playground available at ${url}`);

  } catch (error) {
    console.error(error);
  }
}


main();

参考

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335