Design scalable GraphQL schemas with best practices
# GraphQL Schema Design for Google Antigravity
Design robust, scalable GraphQL schemas following best practices with Google Antigravity IDE.
## Schema Definition
```graphql
# schema.graphql
type Query {
# User queries
me: User
user(id: ID!): User
users(
first: Int
after: String
filter: UserFilter
orderBy: UserOrderBy
): UserConnection!
# Product queries
product(id: ID!, slug: String): Product
products(
first: Int
after: String
filter: ProductFilter
): ProductConnection!
}
type Mutation {
# Auth mutations
login(input: LoginInput!): AuthPayload!
register(input: RegisterInput!): AuthPayload!
logout: Boolean!
# User mutations
updateProfile(input: UpdateProfileInput!): User!
# Product mutations
createProduct(input: CreateProductInput!): CreateProductPayload!
updateProduct(id: ID!, input: UpdateProductInput!): UpdateProductPayload!
deleteProduct(id: ID!): DeleteProductPayload!
}
type Subscription {
productUpdated(id: ID!): Product!
orderStatusChanged(orderId: ID!): Order!
}
# Types
type User implements Node {
id: ID!
email: String!
name: String!
avatar: String
role: UserRole!
createdAt: DateTime!
updatedAt: DateTime!
# Relationships
orders(first: Int, after: String): OrderConnection!
reviews: [Review!]!
}
type Product implements Node {
id: ID!
slug: String!
name: String!
description: String!
price: Money!
inventory: Int!
status: ProductStatus!
images: [Image!]!
category: Category!
reviews(first: Int, after: String): ReviewConnection!
averageRating: Float
createdAt: DateTime!
}
# Connections for pagination
type UserConnection {
edges: [UserEdge!]!
pageInfo: PageInfo!
totalCount: Int!
}
type UserEdge {
node: User!
cursor: String!
}
type PageInfo {
hasNextPage: Boolean!
hasPreviousPage: Boolean!
startCursor: String
endCursor: String
}
# Input types
input UserFilter {
role: UserRole
search: String
createdAfter: DateTime
}
input UserOrderBy {
field: UserOrderField!
direction: OrderDirection!
}
input CreateProductInput {
name: String!
description: String!
price: MoneyInput!
categoryId: ID!
images: [Upload!]
}
# Payload types for mutations
type CreateProductPayload {
product: Product
errors: [Error!]
}
type Error {
field: String
message: String!
code: ErrorCode!
}
# Scalars and enums
scalar DateTime
scalar Upload
scalar Money
enum UserRole {
ADMIN
CUSTOMER
VENDOR
}
enum ProductStatus {
DRAFT
PUBLISHED
ARCHIVED
}
enum ErrorCode {
VALIDATION_ERROR
NOT_FOUND
UNAUTHORIZED
FORBIDDEN
}
# Interface
interface Node {
id: ID!
}
```
## Resolver Implementation
```typescript
// resolvers/user.ts
import { GraphQLContext } from "../context";
import { UserResolvers } from "../generated/graphql";
export const userResolvers: UserResolvers<GraphQLContext> = {
Query: {
me: async (_, __, { user, dataSources }) => {
if (!user) return null;
return dataSources.userAPI.getById(user.id);
},
user: async (_, { id }, { dataSources }) => {
return dataSources.userAPI.getById(id);
},
users: async (_, { first, after, filter, orderBy }, { dataSources }) => {
return dataSources.userAPI.paginate({ first, after, filter, orderBy });
}
},
Mutation: {
updateProfile: async (_, { input }, { user, dataSources }) => {
if (!user) throw new AuthenticationError("Not authenticated");
return dataSources.userAPI.update(user.id, input);
}
},
User: {
orders: async (user, { first, after }, { dataSources }) => {
return dataSources.orderAPI.getByUserId(user.id, { first, after });
},
reviews: async (user, _, { dataSources }) => {
return dataSources.reviewAPI.getByUserId(user.id);
}
}
};
```
## DataLoader for N+1 Prevention
```typescript
// dataloaders/index.ts
import DataLoader from "dataloader";
import { db } from "../db";
export function createLoaders() {
return {
userLoader: new DataLoader<string, User>(async (ids) => {
const users = await db.query.users.findMany({
where: (users, { inArray }) => inArray(users.id, [...ids])
});
const userMap = new Map(users.map(u => [u.id, u]));
return ids.map(id => userMap.get(id) || null);
}),
productLoader: new DataLoader<string, Product>(async (ids) => {
const products = await db.query.products.findMany({
where: (products, { inArray }) => inArray(products.id, [...ids])
});
const productMap = new Map(products.map(p => [p.id, p]));
return ids.map(id => productMap.get(id) || null);
}),
reviewsByProductLoader: new DataLoader<string, Review[]>(async (productIds) => {
const reviews = await db.query.reviews.findMany({
where: (reviews, { inArray }) => inArray(reviews.productId, [...productIds])
});
const grouped = new Map<string, Review[]>();
reviews.forEach(review => {
const existing = grouped.get(review.productId) || [];
grouped.set(review.productId, [...existing, review]);
});
return productIds.map(id => grouped.get(id) || []);
})
};
}
```
## Best Practices
1. **Use Relay-style connections** for pagination
2. **Implement DataLoader** to prevent N+1 queries
3. **Design mutation payloads** with errors field
4. **Use input types** for complex arguments
5. **Implement proper authorization** at resolver level
6. **Version your schema** with deprecations
7. **Generate types** from schema with codegen
Google Antigravity helps design GraphQL schemas and generates type-safe resolvers automatically.This graphql prompt is ideal for developers working on:
By using this prompt, you can save hours of manual coding and ensure best practices are followed from the start. It's particularly valuable for teams looking to maintain consistency across their graphql implementations.
Yes! All prompts on Antigravity AI Directory are free to use for both personal and commercial projects. No attribution required, though it's always appreciated.
This prompt works excellently with Claude, ChatGPT, Cursor, GitHub Copilot, and other modern AI coding assistants. For best results, use models with large context windows.
You can modify the prompt by adding specific requirements, constraints, or preferences. For graphql projects, consider mentioning your framework version, coding style, and any specific libraries you're using.