GraphQL 實戰(zhàn)篇之前端Vue+后端
(給前端大學加星標,提升前端技能.)
作者:PatWu16
https://segmentfault.com/a/1190000039087433
這篇文章記錄使用Vue+GraphQL搭建客戶端。
客戶端項目目錄結構如下:
安裝
首先我們先使用vue-cli新建項目,接著安裝依賴:
npm?install?apollo-cache-inmemory?apollo-client?apollo-link?apollo-link-http?apollo-link-ws?apollo-utilities?vue-apollo?-S
引入依賴
//?main.js
import?Vue?from?'vue'
import?App?from?'./App.vue'
import?{?apolloProvider?}?from?'./vue-apollo';
Vue.config.productionTip?=?false
new?Vue({
????render:?h?=>?h(App),
????//?像?vue-router?或?vuex?一樣注入apolloProvider?
????apolloProvider,
}).$mount('#app')
//?vue-apollo.js
//?相關文檔請查閱?https://apollo.vuejs.org/zh-cn/
import?{?ApolloClient?}?from?'apollo-client'
import?{?createHttpLink?}?from?'apollo-link-http'
import?{?InMemoryCache?}?from?'apollo-cache-inmemory'
import?Vue?from?'vue'
import?VueApollo?from?'vue-apollo'
//?新的引入文件
import?{?split?}?from?'apollo-link'
import?{?WebSocketLink?}?from?'apollo-link-ws'
import?{?getMainDefinition?}?from?'apollo-utilities'
Vue.use(VueApollo)
//?與?API?的?HTTP?連接
const?httpLink?=?createHttpLink({?//?你需要在這里使用絕對路徑?
????uri:?'http://localhost:3001/graphql',
})
//?創(chuàng)建訂閱的?websocket?連接
const?wsLink?=?new?WebSocketLink({
????uri:?'ws://localhost:3001/graphql',
????options:?{
????????reconnect:?true,
????}
})
//?使用分割連接的功能
//?你可以根據(jù)發(fā)送的操作類型將數(shù)據(jù)發(fā)送到不同的連接
const?link?=?split(({?query?})?=>?{
????const?definition?=?getMainDefinition(query)
????return?definition.kind?===?'OperationDefinition'?&&?definition.operation?===?'subscription'
????},
????wsLink,
????httpLink
)
//?創(chuàng)建?apollo?客戶端
const?apolloClient?=?new?ApolloClient({
????link,
????cache:?new?InMemoryCache(),
????connectToDevTools:?true,
})
export?const?apolloProvider?=?new?VueApollo({???????
????defaultClient:?apolloClient,
})
編寫業(yè)務代碼
//?App.vue
?"app">?"Vue?logo"?src="./assets/logo.png">? ?
//?HelloGraphQL.vue
????"hello">
????????作者姓氏:{{author.firstName}}
????????
????????
?
?
????????新增標題:{{post.title}}
????????
????????
?
????????訂閱成功次數(shù):{{receivedSubscriptionTimes}}
????
至此,我們便可以請求server端服務了!??????
前面我們介紹了GraphQL的概念和基礎知識,這篇文章記錄下使用Nestjs+GraphQL搭建Node服務。
安裝
npm?i?--save?@nestjs/graphql?graphql-tools?graphql?apollo-server-express
注冊
//?app.module.ts
import?{?Module?}?from?'@nestjs/common';
import?{?GraphQLModule?}?from?'@nestjs/graphql';
import?{?ConfigModule,?ConfigService?}?from?'nestjs-config';
@Module({
?imports:?[
????ConfigModule.load(path.resolve(__dirname,?'config',?'**/!(*.d).{ts,js}')),
????GraphQLModule.forRootAsync({
????????imports:?[ConfigModule],
????????useFactory:?(config:?ConfigService)?=>?config.get('graphql'),
????????inject:?[ConfigService],
????})?
?],})
export?class?ApplicationModule?{}
//?src/config/graphql.ts
import?*?as?path?from?'path';
export?default?{
??autoSchemaFile:?path.join(process.cwd(),?'src/schema.gql'),?//?最后生成的`Schema?文件,不可修改`
??installSubscriptionHandlers:?true,?//?啟用訂閱
};
啟動項目,并訪問 http://localhost:3000/graphql,我們便可以看到graphql頁面。

編寫服務端邏輯
接下來我們注冊一個author模塊,目錄結構如下:

//?author.module.ts
import?{?Module?}?from?'@nestjs/common';
import?{?AuthorService?}?from?'./author.service';
import?{?AuthorResolver?}?from?'./author.resolver';
@Module({
??providers:?[
????AuthorService,?
????AuthorResolver
??]
})
export?class?AuthorModule?{}
//?author.service.ts
//?此文件用于寫數(shù)據(jù)庫查詢等邏輯,我們著重學習GraphQL的使用,故此處不做相關Demo
import?{?Injectable?}?from?'@nestjs/common';
@Injectable()
export?class?AuthorService?{
??async?findOneById()?{}
}
//?author.resolver.ts
import?{?Args,?Mutation,?Query,?Resolver,?Subscription,?ResolveField,?Parent,?Int?}?from?'@nestjs/graphql';
import?{?PubSub?}?from?'graphql-subscriptions';
import?{?Author?}?from?'./models/author.model';
import?{?Post?}?from?'./models/post.model';
import?{?AuthorService?}?from?'./author.service';
//?import?{?GetAuthorArgs?}?from?'./dto/get-author.args';
const?pubSub?=?new?PubSub();
@Resolver(()?=>?Author)
export?class?AuthorResolver?{
??constructor(
?????private?authorsService:?AuthorService
??)?{}
?//?根據(jù)id查詢作者信息
?@Query(returns?=>?Author,?{
????name:?'author',
????description:?'get?author?info?by?id',
????nullable:?false
?})
?async?getAuthor(@Args('id',?{
?????type:?()?=>?Int,
?????description:?'author?id',
?????nullable:?false
?})?id:?number):?Promise?{
?//?return?this.authorsService.findOneById(id);
????return?{
????????id,
????????firstName:?'wu',
????????lastName:?'pat',
????};
}
//?使用DTO接受參數(shù)
//?@Query(returns?=>?Author)
//?async?getAuthor(@Args()?args:?GetAuthorArgs)?{
//???return?this.authorsService.findOneById(args);
//?}
?//?修改作者信息
?@Mutation(returns?=>?Author,?{
????name:?'changeAuthor',
????description:?'change?author?info?by?id',
????nullable:?false
?})
?async?changeAuthor(
????@Args('id')?id:?number,
????@Args('firstName')?firstName:?string,
????@Args('lastName')?lastName:?string,
?):?Promise?{
?//?return?this.authorsService.findOneById(id);
?return?{
????id,
????firstName,
????lastName,
??};
}
?//?解析posts字段
?@ResolveField()
?async?posts(@Parent()?author:?Author):?Promise?{
?????const?{?id?}?=?author;
?????//?return?this.postsService.findAll({?authorId:?id?});
?????return?[{
????????id:?4,
????????title:?'hello',
????????votes:?2412,
?????}];
?}
?
?//?新增文章
?@Mutation(returns?=>?Post)
?async?addPost()?{
?????const?newPost?=?{
????????id:?1,
????????title:?'新增文章'
?????};
?????//?新增成功后,通知更新
?????await?pubSub.publish('postAdded',?{?postAdded:?newPost?});
?????return?newPost;
?}
?
?//?監(jiān)聽變更
?@Subscription(returns?=>?Post,?{
?????name:?'postAdded',
?????//?filter:?(payload,?variables)?=>?payload.postAdded.title?===?variables.title,
?????//?過濾訂閱
?????//?resolve(this:?AuthorResolver,?value)?{?//?修改payload參數(shù)
?????//???return?value;
?????//?}?})
?async?postAdded(/*@Args('title')?title:?string*/)?{
????return?(await?pubSub.asyncIterator('postAdded'));
?}}
//?author.model.ts
import?{?Field,?Int,?ObjectType?}?from?'@nestjs/graphql';
import?{?Post?}?from?'./post.model';
@ObjectType({?description:?'Author?model'?})
export?class?Author?{
?@Field(type?=>?Int,?{
????description:?'作者id'
?})
?id:?number;
?
?@Field({
????nullable:?true,
????description:?'作者姓姓氏'
?})
?firstName?:?string;
?
?@Field({?
????nullable:?true,
????description:?'作者姓名字'
?})
?lastName?:?string;
?
?//?要聲明數(shù)組的項(而不是數(shù)組本身)是可為空的,請將nullable屬性設置'items'
?//?如果數(shù)組及其項都是可空的,則設置nullable為'itemsAndList'?
?@Field(type?=>?[Post],?{
????nullable:?'items',
????description:?'作者發(fā)表的文章'
?})
?posts:?Post[];
}
//?posts.model.ts
import?{?Field,?Int,?ObjectType?}?from?'@nestjs/graphql';
@ObjectType()
export?class?Post?{
?@Field(type?=>?Int)
?id:?number;
?
?@Field()?title:?string;
?
?@Field(type?=>?Int,?{
????nullable:?true
?})
?votes?:?number;
}
上面的代碼包含了查詢、變更、訂閱類型,此時我們會發(fā)現(xiàn)src下面新增了一個文件schema.gql,這個文件就是自動生成的類型文件:
#?------------------------------------------------------
#?THIS?FILE?WAS?AUTOMATICALLY?GENERATED?(DO?NOT?MODIFY)
#?------------------------------------------------------
type?Post?{
????id:?Int!
????title:?String!
????votes:?Int
}
"""Author?model"""
type?Author?{
????"""作者id"""
????id:?Int!
????
????"""作者姓姓氏"""
????firstName:?String
????
????"""作者姓名字"""
????lastName:?String
????
????"""作者發(fā)表的文章"""
????posts:?[Post]!
}
type?Query?{
????"""get?author?info?by?id"""
????author(
????????"""author?id"""
????????id:?Int!
????):?Author!
}
type?Mutation?{
????"""change?author?info?by?id"""?
????changeAuthor(lastName:?String!,?firstName:?String!,?id:?Float!):?Author!
????addPost:?Post!
}
type?Subscription?{
????postAdded:?Post!
}
執(zhí)行查詢
這時我們的服務已經(jīng)運行起來,可以執(zhí)行查詢了。
#?左下角編寫QUERY?VARIABLES
{
????"id":?1
}
#?Write?your?query?or?mutation?here
#?查詢作者信息
query?author($id:?Int!)?{
????alias:?author(id:?$id)?{
????????id,
????????firstName,
????????posts?{
????????????id,
????????????title
????????}
????}
}
#?修改作者信息
mutation?changeAuthor?{
????changeAuthor(id:?3,?firstName:?"firstName"?lastName:?"lastName")?{
????????id,
????????firstName,
????????lastName
????}
}
#?發(fā)布文章
mutation?addPost?{
????post:?addPost?{
????????id,
????????title
????}
}
#?訂閱文章新增
subscription?postAdded{
????postAdded?{
????????id,
????????title
????}
}
//?自省查詢
query?schema{
????__schema?{
????????types?{?
????????????name
????????}
????}
}
至此,我們的Nestjs+GraphQL服務便搭建完成,給自己一個??!
評論
圖片
表情
