❗️❗️ 项目迁移 click here!
schema 是类型的集合,类型表示自定义对象,它是用于描述从服务端查询到的数据。为了方便定义类型,GraphQL 引入了模版定义语言(Schema Definition Language,SDL)。它和 GraphQL 的查询语言很相似,让我们能够和 GraphQL schema 之间可以无语言差异地沟通。
type Photo {id: ID!url: String!name: String!description: Stringcategory: PhotoCategory! # 枚举类型}
enum PhotoCategory {SELFIEPORTRAITACTIONLANDSCAPEGRAPHIC}
scalar DateTimetype Photo{...created: DateTime!}
也可以采用[graphql-custom-types]库,其中包含了很多自定义类型。
type User{githubLogin: ID!name: Stringavatar: StringpostedPhotos:[Photo!]!inPhotos:[Photo!]!}
列表声明 | 定义 | 无效实例 |
---|---|---|
[Int] | 可空的整数值列表 | 非整数 |
[Int!] | 不可空的整数值列表 | [1,null] |
[Int]! | 可空的整数值非空列表 | null |
[Int!]! | 不可空的整数值非空列表 | null、[null] |
和 TS 类似,它也是用来返回几种不同类型之一。
union AgendaItem=StudyGroup| Workouttype StudyGroup{name: String!subject: String!students: [User!]!}type Workout {name:String!reps:Int!}type Query{agenda:[AgendaItem!]!}
书写查询语句如下:
query schedule{agenda{...on Workout{name}...on StudyGroup{namesubjectstudents}}}
scalar DataTimeinterface AgendaItem{name: String!start: DateTime!end: DateTime!}type StudyGroup implements AgendaItem{name: String!start: DateTime!end: DateTime!topic: String}type Workout implements AgendaItem{name: String!start: DateTime!end: DateTime!reps: Int!}
type Query{User(login:ID!):User}
input PostPhotoInput {name: String!category: PhotoCategory = SELFIEdescription: String}type Mutation {postPhoto(input: PostPhotoInput!): Photo!}
type AuthPayload {token: String!user: User!}type Mutation{githubAuth(code:String!):AuthPayload!}
type Subscription {newPhoto:Photo!newUser: User!}
type Query{...}type Mutation{...}
npm i apollo-server npm i typescript ts-node-dev -D
package.json
"scripts": {"dev": "ts-node-dev --respawn --transpileOnly ./src/index.ts",},
import { ApolloServer } from 'apollo-server';const typeDefs = `enum PhotoCategory {SELFIEPORTRAITACTIONLANDSCAPEGRAPHIC}type Photo {id: ID!url: String!name: String!description: Stringcategory: PhotoCategory!}type Query{totalPhotos:Int!allPhotos: [Photo!]!}input PostPhotoInput {name: String!category: PhotoCategory=SELFIEdescription: String}type Mutation {postPhoto(input: PostPhotoInput!):Photo!}`;//_id 模拟数据自增IDlet _id = 0;const photos = [];const resolvers = {Photo: {url: (parent) => `http://https://blog.ccwgs.top/img/${parent.id}.jpg`,},Query: {totalPhotos: () => photos.length,allPhotos: () => photos,},Mutation: {postPhoto(_, args) {const newPhoto = {id: _id++,...args.input,};photos.push(newPhoto);return newPhoto;},},};const server = new ApolloServer({typeDefs,resolvers,});//开启服务监听 默认4000端口server.listen().then(({ url }) => console.log(`GraphQL Service running on ${url}`));
npm start 打开 连接 http://localhost:4000
喜欢 ts 伙伴可以查看 👉使用 node+typescript 搭建 GraphQL API
基于上面环境搭建将 apollo-server
更换apollo-server-express
npm i apollo-server-express graphql express mongoose ncp dotenv node-fetch npm i typescript ts-node-dev -D
package.json
"scripts": {"build": "tsc && ncp src/schema dist/schema ","clear": "rimraf dist/","start": "npm run clear && npm run build && node ./dist/index.js"},
构建结构
src├── index.ts //入口├── lib //工具库│ └── index.ts├── resolvers //解析器│ ├── Mutation.ts│ ├── Query.ts│ ├── Type.ts│ ├── index.ts│ └── types.ts└── schema└── typeDefs.graphql
重写构建服务如下:
import * as express from 'express';import { ApolloServer } from 'apollo-server-express';import expressPlayground from 'graphql-playground-middleware-express';import * as mongoose from 'mongoose';import * as path from 'path';import resolvers from './resolvers';import { readFileSync } from 'fs';const typeDefs = readFileSync(path.resolve(__dirname, './schema/typeDefs.graphql'),'UTF-8',);async function start() {const app = express();const server = new ApolloServer({typeDefs,resolvers,context,});server.applyMiddleware({ app });app.get('/', (req, res) => {res.send('Welcome to the PhotoShare API');});app.get('/playground', expressPlayground({ endpoint: '/graphql' }));app.listen({ port: 4000 }, () => {console.log(`GraphQL server running @ http://localhost:4000${server.graphqlPath}`,);});}start();
DB_HOST=mongodb://localhost:27017/<Your-DataBase-Name>
require('dotenv').config()function start(){// ....// const app = express();const MONGO_DB = process.env.DB_HOST;let db;try {const client= await mongoose.connect(MONGO_DB!,{ useNewUrlParser: true })db=client.connection.db} catch (error) {console.log(`Mongo DB Host not found!please add DB_HOST environment variable to .env fileexiting...`)process.exit(1)}const context = { db }; //创建上下文const server = new ApolloServer({typeDefs,resolvers,context})//...}
shema 如下:
type Query{totalPhotos:Int!allPhotos: [Photo!]!}
//resolves/Query.tsconst totalPhotos: Fn = (_, arg, { db }) =>db.collection('photos').estimatedDocumentCount();const allPhotos: Fn = (parent, args, { db }) =>db.collection('photos').estimatedDocumentCount();
//lib/index.tsimport fetch from 'node-fetch';type ReqGithub = {client_id: string,client_secret: String,code: String,};const requestGithubToken = (credentials: ReqGithub) =>fetch('https://github.com/login/oauth/access_token', {method: 'POST',headers: {'Content-Type': 'application/json',Accept: 'application/json',},body: JSON.stringify(credentials),}).then((res) => res.json());const requestGithubUserAccount = (token: string) =>fetch(`https://api.github.com/user?access_token=${token}`).then((res) =>res.json(),);export const authorizeWithGithub = async (credentials: ReqGithub) => {const { access_token } = await requestGithubToken(credentials);const githubUser = await requestGithubUserAccount(access_token);return { ...githubUser, access_token };};
//schema/typeDefs.graphqltype AuthPayload {token: String!user: User!}type Mutation {...githubAuth(code:String!):AuthPayload!}
//resolvers/Mutation.tsconst githubAuth:Fn=async(parent,{code},{db})=>{let {message,access_token,avatar_url,login,name} = await authorizeWithGithub({client_id: process.env.CLIENT_ID!,client_secret: process.env.CLIENT_SECRET!,code});if(message){throw new Error(message)}let latestUserInfo={name,githubLogin:login,githubToken:access_token,avatar:avatar_url}const {ops:[user]}=await db.collection('users').replaceOne({githubLogin:login},latestUserInfo,{upsert:true})return {user,token:access_token}}
https://github.com/login/oauth/authorize?client_id=**&scope=user
github 重定向地址 http://localhost:3000/oauth?code=\*\*\*
mutation github{githubAuth(code: "***"){tokenuser{githubLoginnameavatar}}}
我们通过根解析器解析 token 返回用户信息,如果无效则返回 null。
//src/index.ts// const context = { db };const server = new ApolloServer({typeDefs,resolvers,context: async ({ req }) => {const githubToken = req.headers.authorization;const currentUser = await db.collection('users').findOne({ githubToken });return { db, currentUser };},});
//schema/typeDefs.graphqltype Query {me:User}
//resovles/Query.tsconst me: Fn = (_, args, { currentUser }) => currentUser;
测试
query getCurrentUser{me{githubLoginnameavatar}}