Design schemas, resolvers, and service definitions for GraphQL and gRPC APIs.
GraphQL Schema Design
# 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 }
Queriestype 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! }
Mutationstype Mutation { createUser(input: CreateUserInput!): CreateUserPayload! updateUser(id: UUID!, input: UpdateUserInput!): UpdateUserPayload! deleteUser(id: UUID!): DeletePayload! createOrder(input: CreateOrderInput!): CreateOrderPayload! cancelOrder(id: UUID!): CancelOrderPayload! }
Subscriptionstype Subscription { orderStatusChanged(orderId: UUID!): Order! newOrder: Order! }
Inputs & Payloadsinput 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
{
"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
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