api-graphql-grpc
>
Works with
---
name: api-graphql-grpc
description: >
license: MIT
---
# GraphQL & gRPC Skill
Design schemas, resolvers, and service definitions for GraphQL and gRPC APIs.
---
## GraphQL Schema Design
```graphql
# Scalars
scalar DateTime
scalar UUID
scalar JSON
# Enums
enum OrderStatus { PENDING PAID SHIPPED DELIVERED CANCELLED }
enum UserRole { ADMIN EDITOR VIEWER }
# Types
type User {
id: UUID!
name: String!
email: String!
role: UserRole!
orders(first: Int, after: String): OrderConnection!
createdAt: DateTime!
}
type Order {
id: UUID!
status: OrderStatus!
total: Float!
items: [OrderItem!]!
user: User!
createdAt: DateTime!
}
type OrderItem {
id: UUID!
product: Product!
quantity: Int!
price: Float!
}
# Pagination (Relay cursor spec)
type OrderConnection {
edges: [OrderEdge!]!
pageInfo: PageInfo!
totalCount: Int!
}
type OrderEdge { node: Order!; cursor: String! }
type PageInfo {
hasNextPage: Boolean!
hasPreviousPage: Boolean!
startCursor: String
endCursor: String
}
# Queries
type Query {
me: User
user(id: UUID!): User
users(first: Int, after: String, role: UserRole): UserConnection!
order(id: UUID!): Order
orders(status: OrderStatus, first: Int, after: String): OrderConnection!
}
# Mutations
type Mutation {
createUser(input: CreateUserInput!): CreateUserPayload!
updateUser(id: UUID!, input: UpdateUserInput!): UpdateUserPayload!
deleteUser(id: UUID!): DeletePayload!
createOrder(input: CreateOrderInput!): CreateOrderPayload!
cancelOrder(id: UUID!): CancelOrderPayload!
}
# Subscriptions
type Subscription {
orderStatusChanged(orderId: UUID!): Order!
newOrder: Order!
}
# Inputs & Payloads
input CreateUserInput { name: String!; email: String!; role: UserRole }
type CreateUserPayload { user: User; errors: [UserError!] }
type UserError { field: String; message: String! }
```
---
## Resolver Pattern (DataLoader — solves N+1)
```javascript
// Without DataLoader: N+1 queries
// With DataLoader: batch all user IDs into one SQL IN(...)
const userLoader = new DataLoader(async (userIds) => {
const users = await db.query(`SELECT * FROM users WHERE id = ANY($1)`, [userIds]);
// Return in same order as input IDs
return userIds.map(id => users.find(u => u.id === id) || null);
});
const resolvers = {
Order: {
user: (order, _, { loaders }) => loaders.user.load(order.userId),
},
Query: {
orders: async (_, { status, first = 20, after }) => {
return paginatedQuery('orders', { status, first, after });
}
}
};
```
---
## Error Handling in GraphQL
```json
{
"data": { "createUser": null },
"errors": [
{
"message": "Email already in use",
"locations": [{ "line": 2, "column": 3 }],
"path": ["createUser"],
"extensions": {
"code": "USER_EMAIL_TAKEN",
"field": "email"
}
}
]
}
```
---
## gRPC Proto Definition
```protobuf
syntax = "proto3";
package users.v1;
option go_package = "github.com/example/api/users/v1";
import "google/protobuf/timestamp.proto";
import "google/protobuf/empty.proto";
service UsersService {
// Unary RPCs
rpc GetUser(GetUserRequest) returns (User);
rpc CreateUser(CreateUserRequest) returns (User);
rpc UpdateUser(UpdateUserRequest) returns (User);
rpc DeleteUser(DeleteUserRequest) returns (google.protobuf.Empty);
rpc ListUsers(ListUsersRequest) returns (ListUsersResponse);
// Server streaming
rpc WatchUser(GetUserRequest) returns (stream User);
// Bidirectional streaming
rpc SyncUsers(stream SyncRequest) returns (stream SyncResponse);
}
message User {
string id = 1;
string name = 2;
string email = 3;
string role = 4;
google.protobuf.Timestamp created_at = 5;
}
message GetUserRequest { string id = 1; }
message CreateUserRequest { string name = 1; string email = 2; string role = 3; }
message UpdateUserRequest { string id = 1; string name = 2; string email = 3; }
message DeleteUserRequest { string id = 1; }
message ListUsersRequest { int32 page = 1; int32 limit = 2; string role = 3; }
message ListUsersResponse { repeated User users = 1; int32 total = 2; }
```
---
## REST vs GraphQL vs gRPC Decision Matrix
| Factor | REST | GraphQL | gRPC |
|--------|------|---------|------|
| Public API | ✓ Best | ✓ Good | ✗ |
| Mobile clients (bandwidth) | ✗ Over-fetch | ✓ Best | ✓ |
| Microservices (internal) | ✓ | ✗ | ✓ Best |
| Streaming / real-time | ✗ | ✓ Subscriptions | ✓ Best |
| Complex queries | ✗ N endpoints | ✓ Best | ✗ |
| Caching | ✓ HTTP cache | ✗ Complex | ✗ |
| Browser native | ✓ | ✓ | ✗ (needs proxy) |
---
## After Completing the API Design
Once the graphql/grpc design output is delivered, ask the user:
"Would you like me to generate API documentation for this design? (yes/no)"
If the user says **yes**:
- Check if the API Documentation skill is available in the installed skills list
- If the skill **is available**:
- Read and follow the instructions in the API Documentation skill
- Use the API design output above as the input
- If the skill **is NOT available**:
- Inform the user: "It looks like the API Documentation skill isn't installed.
You can install it and re-run.
If the user says **no**:
- End the task here
---More API Design skills
lark-event
larksuite/cli
Lark/Feishu real-time event listening / subscribing / consuming: stream events as NDJSON via `lark-cli event consume <EventKey>` (covers IM messages/reactions/chat changes, Approval status changes, Task updates, VC meeting started/joined/ended, Minutes generated, Whiteboard updated, etc.). Use for Lark bots, real-time message processing, long-running subscribers, streaming webhook/push handlers. Supports `--max-events` / `--timeout` bounded runs and a stderr ready-marker contract — designed for AI agents running as subprocesses.
lark-contact
larksuite/cli
飞书 / Lark 通讯录:按姓名 / 邮箱解析成 open_id,或按 open_id 反查姓名 / 部门 / 邮箱 / 联系方式 / 个人状态 / 签名,以及按关键词搜索当前用户可见的机器人 / 智能体(agent)。当用户提到一个名字要下一步发消息 / 排日程,或拿到 open_id 想查具体信息时使用。不负责部门树遍历、按部门列员工、组织架构图,这类需求走原生 OpenAPI。
lark-openapi-explorer
larksuite/cli
飞书/Lark 原生 OpenAPI 探索:从官方文档库中挖掘未经 CLI 封装的原生 OpenAPI 接口。当用户的需求无法被现有 lark-* skill 或 lark-cli 已注册命令满足,需要查找并调用原生飞书 OpenAPI 时使用。

