GraphQL 是 Facebook 开发的一种数据查询语言,它在客户端和服务器之间提供了一个公共 API 来获取和操作数据。 它处理单个 URL 端点上的所有请求。 它是 REST 风格、更快的应用程序和改善开发者体验的替代品。下面将介绍如何创建 GraphQL API 服务器并在 Angular5 中使用 GraphQL
在创建 GraphQL API 服务器之前,我们需要了解 GraphQL 的一些重要术语。
类型定义对象的形状,客户机期望和字段是一个类型的基本单位,可以有值的字符串,整数,布尔,浮点数,id,或引用系统中的其他类型,甚至一个自定义标量。
查询指定如何允许客户端查询和检索已定义类型。
函数用于操作数据。 它用于创建、更新和删除操作。
模式将一切联系在一起。 它的类型,查询和函数的收集。
解析器是一组特定于应用程序的函数,它们根据模式中描述的查询和函数操作与底层数据存储进行交互
以下是这个项目的先决条件:
- Node.js
- 节点包管理器(NPM)
通过在终端 / 控制台窗口中键入以下命令,确保系统上安装了 Node 和 NPM
node –v
npm – v
设置 GraphQL API 服务器
创建一个类似于 GraphQLServer 的目录,并将其更改为工作目录。
mkdir GraphQLServer
cd GraphQLServer
创建 package.json
npm init
此命令提示您一些信息,例如应用程序的名称和版本。 现在,你可以简单的点击 RETURN 来接受大多数选项的默认值。
安装 Express
npm install express --save
安装 graphql、 graphql-tools、 express-graphql 和 cors
npm install graphql graphql-tools express-graphql cors --save
让我们创建一个新的文件 schema.js,在这里我们将定义类型、查询和函数。
声明自定义 Date 标量,因为它不是 GraphQL 中的默认标量。
# declare custom scalars for date as GQDate
scalar GQDate
定义注册类型
# registration type
type Registration {
id: ID!
firstName: String
lastName: String
dob: GQDate
email: String
password: String
country: String
}
定义查询
type Query {
# Return a registration by id
Registration(id: ID!): Registration
# Return all registrations
Registrations(limit: Int): [Registration]
}
定义创建、更新和删除的函数
type Mutation {
# Create a registration
createRegistration (firstName: String,lastName: String, dob: GQDate, email: String, password: String, country: String): Registration
# Update a registration
updateRegistration (id: ID!, firstName: String,lastName: String, dob: GQDate, email: String, password: String, country: String): Registration
# Delete a registration
deleteRegistration(id: ID!): Registration
}
最终的 schema.js 文件将如下所示
// schema.js
const schema = `
# declare custom scalars for date as GQDate
scalar GQDate
# registration type
type Registration {
id: ID!
firstName: String
lastName: String
dob: GQDate
email: String
password: String
country: String
}
type Query {
# Return a registration by id
Registration(id: ID!): Registration
# Return all registrations
Registrations(limit: Int): [Registration]
}
type Mutation {
# Create a registration
createRegistration (firstName: String,lastName: String, dob: GQDate, email: String, password: String, country: String): Registration
# Update a registration
updateRegistration (id: ID!, firstName: String,lastName: String, dob: GQDate, email: String, password: String, country: String): Registration
# Delete a registration
deleteRegistration(id: ID!): Registration
}
`;
module.exports.Schema = schema;
让我们创建一个 resolver.js 文件,我们将根据模式中描述的查询和函数操作来定义 CRUD 功能。
// resolvers.js
const { GraphQLScalarType } = require("graphql");
function convertDate(inputFormat) {
function pad(s) {
return s < 10 ? "0" + s : s;
}
var d = new Date(inputFormat);
return [pad(d.getDate()), pad(d.getMonth()), d.getFullYear()].join("/");
}
// Define Date scalar type.
const GQDate = new GraphQLScalarType({
name: "GQDate",
description: "Date type",
parseValue(value) {
// value comes from the client
return value; // sent to resolvers
},
serialize(value) {
// value comes from resolvers
return value; // sent to the client
},
parseLiteral(ast) {
// value comes from the client
return new Date(ast.value); // sent to resolvers
}
});
// data store with default data
const registrations = [
{
id: 1,
firstName: "Johan",
lastName: "Peter",
dob: new Date("2014-08-31"),
email: "johan@gmail.com",
password: "johan123",
country: "UK"
},
{
id: 2,
firstName: "Mohamed",
lastName: "Tariq",
dob: new Date("1981-11-24"),
email: "tariq@gmail.com",
password: "tariq123",
country: "UAE"
},
{
id: 3,
firstName: "Nirmal",
lastName: "Kumar",
dob: new Date("1991-09-02"),
email: "nirmal@gmail.com",
password: "nirmal123",
country: "India"
}
];
const resolvers = {
Query: {
Registrations: () => registrations, // return all registrations
Registration: (_, { id }) =>
registrations.find(registration => registration.id == id) // return registration by id
},
Mutation: {
// create a new registration
createRegistration: (root, args) => {
// get next registration id
const nextId =
registrations.reduce((id, registration) => {
return Math.max(id, registration.id);
}, -1) + 1;
const newRegistration = {
id: nextId,
firstName: args.firstName,
lastName: args.lastName,
dob: args.dob,
email: args.email,
password: args.password,
country: args.country
};
// add registration to collection
registrations.push(newRegistration);
return newRegistration;
}, // delete registration by id
deleteRegistration: (root, args) => {
// find index by id
const index = registrations.findIndex(
registration => registration.id == args.id
);
// remove registration by index
registrations.splice(index, 1);
}, // update registration
updateRegistration: (root, args) => {
// find index by id
const index = registrations.findIndex(
registration => registration.id == args.id
);
registrations[index].firstName = args.firstName;
registrations[index].lastName = args.lastName;
registrations[index].dob = args.dob;
registrations[index].email = args.email;
registrations[index].password = args.password;
registrations[index].country = args.country;
return registrations[index];
}
},
GQDate
};
module.exports.Resolvers = resolvers;
最后,让我们创建一个 server.js 文件,在这个文件中我们将传递 typedef 和解析器来创建可执行模式。
// server.js
const express = require("express");
const cors = require("cors");
const graphqlHTTP = require("express-graphql");
const { makeExecutableSchema } = require("graphql-tools");
const typeDefs = require("./schema").Schema;
const resolvers = require("./resolvers").Resolvers;
const schema = makeExecutableSchema({
typeDefs,
resolvers,
logger: {
log: e => console.log(e)
}
});
var app = express();
app.use(cors());
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header(
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept"
);
next();
});
app.use(
"/graphql",
graphqlHTTP(request => ({
schema: schema,
graphiql: true
}))
);
app.listen(4000);
console.log("Running a GraphQL API server at http://localhost:4000/graphql");
运行 GraphQL API 服务器
npm start
现在服务器已经启动,可以在 http://localhost:4200/graphql 上访问应用程序。 让我们在 graphhiql 上查询数据,我们应该得到下面的结果。
在客户端应用程序中使用 GraphQL API。
在父目录下创建一个作为 AngularClient 的目录,并将其更改为工作目录。
cd ..
mkdir AngularClient
cd AngularClient
克隆 github 中的项目到该目录
git clone https://github.com/bahurudeen/ng5bootstrap4.git
安装该项目
npm install
安装 Angular 命令行工具
npm install --save-dev @angular/cli@latest
为 Angular 安装 Apollo 客户端
npm install apollo-angular apollo-angular-link-http apollo-client apollo-cache-inmemory graphql-tag graphql --save
让我们创建一个 GraphQL.module.ts 文件,在该文件中,我们将创建到 GraphQL API 服务器的连接。
// src/app/graphql.module.ts
import { NgModule } from "@angular/core";
import { HttpClientModule } from "@angular/common/http";
// Apollo
import { ApolloModule, Apollo } from "apollo-angular";
import { HttpLinkModule, HttpLink } from "apollo-angular-link-http";
import { InMemoryCache } from "apollo-cache-inmemory";
@NgModule({
exports: [HttpClientModule, ApolloModule, HttpLinkModule]
})
export class GraphQLModule {
constructor(apollo: Apollo, httpLink: HttpLink) {
apollo.create({
link: httpLink.create({ uri: "http://localhost:4000/graphql" }),
cache: new InMemoryCache()
});
}
}
在 app.module.ts 中导入 GraphQL 模块
// src/app/app.module.ts
import { BrowserModule } from "@angular/platform-browser";
import { NgModule } from "@angular/core";
import { AppRoutingModule } from "./app-routing.module";
import { AppComponent } from "./app.component";
import { NgbModule } from "@ng-bootstrap/ng-bootstrap";
import { NavbarComponent } from "./navbar/navbar.component";
import { HomeComponent } from "./home/home.component";
import { RegistrationComponent } from "./registration/registration.component";
import { FormsModule } from "@angular/forms";
// Apollo
import { GraphQLModule } from "./graphql.module";
@NgModule({
declarations: [
AppComponent,
NavbarComponent,
HomeComponent,
RegistrationComponent
],
imports: [
BrowserModule,
AppRoutingModule,
NgbModule.forRoot(),
FormsModule,
GraphQLModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {}
在 registration.component.html 文件中将以下代码:
{{ registration.dob.day + '/' + registration.dob.month + '/' + registration.dob.year}}
替换为:
{{ registration.dob | date:”dd/MM/yyyy”}}
替换后的文件 registration.component.html 代码如下:
#
First Name
Last Name
DOB
Email
Country
{{ i + 1 }}
{{ registration.firstName }}
{{ registration.lastName }}
{{ registration.dob | date:"dd/MM/yyyy"}}
{{ registration.email }}
{{ registration.country }}
Edit
Delete
New
First Name
Last Name
DOB
Email
Password
Country
{{regModel.country}}
{{country}}
{{submitType}}
Cancel
让我们通过在 registration.com ponent.ts 中使用 GraphQL API 来实现新的、编辑、删除和显示注册信息。
更新 registration.component.ts 文件:
// src/app/registration/registration.component.ts
import { Component, OnInit } from "@angular/core";
import { NgbDateStruct } from "@ng-bootstrap/ng-bootstrap";
import { Apollo } from "apollo-angular";
import gql from "graphql-tag";
import { Observable } from "rxjs/Observable";
import "rxjs/add/operator/map";
class Registration {
constructor(
public firstName: string = "",
public lastName: string = "",
public dob: NgbDateStruct = null,
public email: string = "",
public password: string = "",
public country: string = "Select country"
) {}
}
@Component({
selector: "app-registration",
templateUrl: "./registration.component.html",
styleUrls: ["./registration.component.css"]
})
export class RegistrationComponent implements OnInit {
// It maintains list of Registrations
registrations: Array = [];
// It maintains registration Model
regModel: Registration;
// It maintains registration form display status. By default it will be false.
showNew: Boolean = false;
// It will be either 'Save' or 'Update' based on operation.
submitType: string = "Save";
// It maintains table row index based on selection.
selectedRow: number;
// It maintains Array of countries.
countries: string[] = ["US", "UK", "India", "UAE"];
registrationList: Array = []; // List of Users
comments: Observable;
constructor(private apollo: Apollo) {}
ngOnInit() {
this.displayRegistrations();
}
// Get all registrations
displayRegistrations() {
const getRegistrations = gql`
{
Registrations {
id
firstName
lastName
dob
email
country
}
}
`;
this.apollo
.watchQuery({
query: getRegistrations,
fetchPolicy: "network-only"
})
.valueChanges.map((result: any) => result.data.Registrations)
.subscribe(data => {
this.registrations = data;
});
}
// This method associate to New Button.
onNew() {
// Initiate new registration.
this.regModel = new Registration();
// Change submitType to 'Save'.
this.submitType = "Save";
// display registration entry section.
this.showNew = true;
}
// This method associate to Save Button.
onSave() {
var dateVal =
this.regModel.dob.year.toString() +
"-" +
this.regModel.dob.month.toString() +
"-" +
this.regModel.dob.day.toString();
if (this.submitType === "Save") {
const saveRegistration = gql`
mutation createRegistration(
$firstName: String!
$lastName: String!
$dob: GQDate!
$email: String!
$password: String!
$country: String!
) {
createRegistration(
firstName: $firstName
lastName: $lastName
dob: $dob
email: $email
password: $password
country: $country
) {
id
dob
}
}
`;
this.apollo
.mutate({
mutation: saveRegistration,
variables: {
firstName: this.regModel.firstName,
lastName: this.regModel.lastName,
dob: new Date(dateVal),
email: this.regModel.email,
password: this.regModel.password,
country: this.regModel.country
}
})
.subscribe(
({ data }) => {
this.displayRegistrations();
},
error => {
console.log("there was an error sending the query", error);
}
);
// Push registration model object into registration list.
// this.registrations.push(this.regModel);
} else {
const updateRegistration = gql`
mutation updateRegistration(
$id: ID!
$firstName: String!
$lastName: String!
$dob: GQDate!
$email: String!
$password: String!
$country: String!
) {
updateRegistration(
id: $id
firstName: $firstName
lastName: $lastName
dob: $dob
email: $email
password: $password
country: $country
) {
id
country
}
}
`;
this.apollo
.mutate({
mutation: updateRegistration,
variables: {
id: this.selectedRow + 1,
firstName: this.regModel.firstName,
lastName: this.regModel.lastName,
dob: new Date(dateVal),
email: this.regModel.email,
password: this.regModel.password,
country: this.regModel.country
}
})
.subscribe(
({ data }) => {
console.log("got editdata", data);
this.displayRegistrations();
},
error => {
console.log("there was an error sending the query", error);
}
);
}
// Hide registration entry section.
this.showNew = false;
}
// This method associate to Edit Button.
onEdit(index: number) {
// Assign selected table row index.
this.selectedRow = index;
// Initiate new registration.
this.regModel = new Registration();
// Retrieve selected registration from list and assign to model.
this.regModel = Object.assign({}, this.registrations[this.selectedRow]);
const dob = new Date(this.registrations[this.selectedRow].dob);
this.regModel.dob = {
day: dob.getDate(),
month: dob.getMonth() + 1,
year: dob.getFullYear()
};
// Change submitType to Update.
this.submitType = "Update";
// Display registration entry section.
this.showNew = true;
}
// This method associate to Delete Button.
onDelete(index: number) {
const deleteRegistration = gql`
mutation deleteRegistration($id: ID!) {
deleteRegistration(id: $id) {
id
}
}
`;
this.apollo
.mutate({
mutation: deleteRegistration,
variables: {
id: index + 1
}
})
.subscribe(
({ data }) => {
console.log("got editdata", data);
this.displayRegistrations();
},
error => {
console.log("there was an error sending the query", error);
}
);
}
// This method associate toCancel Button.
onCancel() {
// Hide registration entry section.
this.showNew = false;
}
// This method associate to Bootstrap dropdown selection change.
onChangeCountry(country: string) {
// Assign corresponding selected country to model.
this.regModel.country = country;
}
}
运行应用程序。 在运行之前,确保 GraphQL API 服务器正在运行。
ng serve
现在 web 服务器已经启动,注册应用程序可以在 http://localhost:4200/上访问,正如你在下面屏幕截图中看到的那样。
英文原文:Creating a GraphQL API server and consuming in Angular 5 application
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于