sofa graphql 2 rest api 试用

时间:2023-02-03 08:35:25

大部分代码还是来自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