sofa graphql 2 rest api 试用

 

大部分代码还是来自sofa 的官方文档,同时添加了docker && docker-compose集成
备注: 代码使用typescript 同时运行的时候为了方便直接运行使用ts-node 运行

环境准备

  • docker-compose 文件
version: "3"
services:
api:
build: ./
image: dalongrong/sofa-graphql2rest
ports:
- "4000:4000"
  • dockerfile
FROM node:alpine
WORKDIR /server
COPY . /server
RUN yarn install
CMD ["yarn", "start"]

代码说明

  • package.json 依赖包
{
"name": "sofa-graphql2rest",
"version": "1.0.0",
"license": "MIT",
"main": "server.js",
"scripts": {
"start": "ts-node index.ts"
},
"dependencies": {
"body-parser": "1.18.3",
"chalk": "2.4.2",
"express": "4.16.4",
"express-graphql": "0.7.1",
"graphql": "^14.0.2",
"graphql-subscriptions": "^1.0.0",
"graphql-tag": "2.10.0",
"graphql-tools": "4.0.3",
"sofa-api": "^0.2.2",
"swagger-ui-express": "4.0.2"
},
"devDependencies": {
"@types/swagger-ui-express": "3.0.0",
"@types/body-parser": "1.17.0",
"@types/express": "4.16.0",
"@types/express-graphql": "0.6.2",
"@types/graphql": "14.0.3",
"esm": "^3.0.84",
"ts-loader": "^5.3.3",
"ts-node": "7.0.1",
"typescript": "3.2.2"
}
}
  • graphql api 开发
    基于graphql-tools,同时集成express-graphql 以及swagger open api 文档
    graphql schema 定义 type.ts
import gql from 'graphql-tag';

export const typeDefs = gql`
type Pizza {
dough: String!
toppings: [String!]
} type Salad {
ingredients: [String!]!
} union Food = Pizza | Salad type Book {
id: ID!
title: String!
} type User {
id: ID!
name: String!
favoritePizza: Pizza!
favoriteBook: Book!
favoriteFood: Food!
shelf: [Book!]!
} type Post {
comments(filter: String!): [String!]
} type Query {
me: User
user(id: ID!): User
users: [User!]
usersLimit(limit: Int!): [User!]
usersSort(sort: Boolean!): [User!]
book(id: ID!): Book
books: [Book!]
never: String
feed: [Post]
} type Mutation {
addBook(title: String!): Book
} type Subscription {
onBook: Book
} schema {
query: Query
mutation: Mutation
subscription: Subscription
}
`;

resolvers 定义 resolvers.ts 同时集成了subscription

import { PubSub } from 'graphql-subscriptions';

const pubsub = new PubSub();

import {
UsersCollection,
BooksCollection,
PostsCollection,
} from './collections'; const BOOK_ADDED = 'BOOK_ADDED'; export const resolvers: any = {
Query: {
me() {
return UsersCollection.get(1);
},
user(_: any, { id }: any) {
return UsersCollection.get(id);
},
users() {
return UsersCollection.all();
},
usersLimit(_: any, { limit }: any) {
return UsersCollection.all().slice(0, limit);
},
usersSort(_: any, { sort }: any) {
const users = UsersCollection.all();
return sort ? users.sort((a, b) => b.id - a.id) : users;
},
book(_: any, { id }: any) {
return BooksCollection.get(id);
},
books() {
return BooksCollection.all();
},
feed() {
return PostsCollection.all();
},
},
Mutation: {
addBook(_: any, { title }: any) {
const book = BooksCollection.add(title); pubsub.publish(BOOK_ADDED, { onBook: book }); return book;
},
},
Subscription: {
onBook: {
subscribe: () => pubsub.asyncIterator([BOOK_ADDED]),
},
},
Food: {
__resolveType(obj: any) {
if (obj.ingredients) {
return 'Salad';
} if (obj.toppings) {
return 'Pizza';
} return null;
},
},
Post: {
comments(post: { comments: string[] }, { filter }: { filter: string }) {
return post.comments.filter(
comment =>
!filter || comment.toLowerCase().indexOf(filter.toLowerCase()) > -1
);
},
},
};

代码入口
index.ts

import * as express from 'express';
import { makeExecutableSchema } from 'graphql-tools';
import * as bodyParser from 'body-parser';
import * as useGraphQL from 'express-graphql';
import * as swaggerUi from 'swagger-ui-express';
import chalk from 'chalk';
import { resolve } from 'path';
import { typeDefs } from './types';
import { resolvers } from './resolvers';
import * as swaggerDocument from './swagger.json'; // Sofa import sofa, { OpenAPI } from 'sofa-api';
import { logger } from 'sofa-api/dist/logger'; const app = express(); app.use(bodyParser.json()); const schema = makeExecutableSchema({
typeDefs,
resolvers: resolvers as any,
}); const openApi = OpenAPI({
schema,
info: {
title: 'Example API',
version: '3.0.0',
},
}); app.use(
sofa({
schema,
ignore: ['User.favoriteBook'],
onRoute(info) {
openApi.addRoute(info, {
basePath: '',
});
},
})
); openApi.save(resolve(__dirname, './swagger.json')); app.use('/docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument)); app.use('/webhook', (req, res) => {
logger.info('Received a webhook', req.body); res.statusCode = 200;
res.statusMessage = 'OK';
res.send();
}); app.use(
'/graphql',
useGraphQL({
schema,
graphiql: true,
})
); const port = 4000; app.listen(port, () => {
const url = `http://localhost:${4000}`; function printUrl(path: string) {
return chalk.gray(url + path);
} console.log(`
${chalk.bold('GraphQL:')} ${printUrl('/graphql')} ${chalk.bold('Queries:')}
me: ${printUrl('/me')}
users: ${printUrl('/users')}
user: ${printUrl('/user/1')}
books: ${printUrl('/books')}
book: ${printUrl('/book/1')} ${chalk.bold('Mutations:')}
addBook: ${printUrl('/add-book')} ${chalk.italic.gray(
'POST: {title}'
)}
`);
});

运行&&测试

  • 运行
docker-compose build && docker-compose up -d
  • 效果
    openapi docs
http://localhost:4000/docs

sofa graphql 2 rest api 试用
graphql ui graphiql 工具

http://localhost:4000/graphql

sofa graphql 2 rest api 试用

  • 查询执行
    rest api
    sofa graphql 2 rest api 试用
    graphql api
    sofa graphql 2 rest api 试用

说明

sofa 在设计上充分graphql 的ast 语法,同时集成了open api ,同时按照设计的方案,可以集成比较多的graphql 的平台,主要是scema

参考资料

https://github.com/rongfengliang/sofa-graphql2rest-docker-compose
https://sofa-api.com/
https://github.com/Urigo/sofa

 
 
 
 
上一篇:java.lang.NoClassDefFoundError: org/apache/curator/RetryPolicy解决方法


下一篇:CORS 跨域 实现思路及相关解决方案