@bernierllc/contentful-graphql-client
v1.2.0
Published
GraphQL Content API Client for flexible Contentful queries with batching and introspection
Readme
@bernierllc/contentful-graphql-client
GraphQL Content API Client for flexible Contentful queries with batching, introspection, and advanced error handling.
Features
- Flexible GraphQL Queries - Execute custom queries with variable support
- Query Batching - Execute multiple queries in parallel for efficiency
- Schema Introspection - Introspect and cache GraphQL schema
- Type-Safe - Full TypeScript support with strict typing
- Error Handling - Enhanced error messages with query context
- Logging - Integrated structured logging with @bernierllc/logger
- Preview API - Support for both production and preview APIs
Installation
npm install @bernierllc/contentful-graphql-clientQuick Start
import { ContentfulGraphQLClient } from '@bernierllc/contentful-graphql-client';
// Initialize client
const client = new ContentfulGraphQLClient({
space: 'your-space-id',
accessToken: 'your-access-token',
environment: 'master' // optional, defaults to 'master'
});
// Execute a query
const query = `
query {
blogPostCollection(limit: 10) {
items {
sys { id }
title
slug
publishDate
}
}
}
`;
const result = await client.query(query);
console.log(result);API Documentation
Constructor
new ContentfulGraphQLClient(config)
Creates a new GraphQL client instance.
Parameters:
config.space(string, required) - Contentful space IDconfig.accessToken(string, required) - Content Delivery API tokenconfig.environment(string, optional) - Environment ID (default: 'master')config.host(string, optional) - Custom GraphQL endpointconfig.timeout(number, optional) - Request timeout in ms (default: 30000)config.logger(Logger, optional) - Custom logger instanceconfig.preview(boolean, optional) - Use preview API (default: false)
Example:
const client = new ContentfulGraphQLClient({
space: 'abc123',
accessToken: 'token-xyz',
environment: 'staging',
timeout: 60000
});Methods
query<T>(query: string, variables?: object, options?: QueryOptions): Promise<T>
Execute a single GraphQL query.
Parameters:
query(string) - GraphQL query stringvariables(object, optional) - Query variablesoptions(QueryOptions, optional) - Query options (operationName, requestHeaders)
Returns: Promise resolving to query result data
Example:
const query = `
query GetBlogPost($id: String!) {
blogPost(id: $id) {
sys { id }
title
body
author {
name
bio
}
}
}
`;
const variables = { id: 'abc123' };
const result = await client.query(query, variables);batchQuery<T>(queries: ContentfulGraphQLQuery[]): Promise<T[]>
Execute multiple GraphQL queries in parallel.
Parameters:
queries(array) - Array of query objects withqueryand optionalvariables
Returns: Promise resolving to array of results in same order as input
Example:
const queries = [
{
query: '{ blogPostCollection(limit: 5) { items { title } } }'
},
{
query: 'query($id: String!) { author(id: $id) { name } }',
variables: { id: 'author-123' }
}
];
const [posts, author] = await client.batchQuery(queries);queryWithErrors<T>(query: string, variables?: object): Promise<ContentfulGraphQLResponse<T>>
Execute a query and return full response including errors.
Parameters:
query(string) - GraphQL query stringvariables(object, optional) - Query variables
Returns: Promise resolving to response object with data and optional errors
Example:
const result = await client.queryWithErrors('{ entries }');
if (result.errors) {
console.error('GraphQL errors:', result.errors);
}
console.log('Data:', result.data);introspectSchema(forceRefresh?: boolean): Promise<GraphQLSchema>
Introspect the GraphQL schema. Results are cached after first call.
Parameters:
forceRefresh(boolean, optional) - Force refresh cached schema (default: false)
Returns: Promise resolving to GraphQL schema object
Example:
const schema = await client.introspectSchema();
// Get all types
const typeMap = schema.getTypeMap();
console.log('Available types:', Object.keys(typeMap));clearSchemaCache(): void
Clear the cached schema. Useful when schema changes.
Example:
client.clearSchemaCache();setHeaders(headers: Record<string, string>): void
Update client headers (e.g., for changing access token).
Parameters:
headers(object) - Headers to merge with existing headers
Example:
client.setHeaders({
'Authorization': 'Bearer new-token',
'X-Custom-Header': 'custom-value'
});getEndpoint(): string
Get the current GraphQL endpoint URL.
Returns: Endpoint URL string
Example:
const endpoint = client.getEndpoint();
console.log('GraphQL endpoint:', endpoint);Advanced Usage
Using Custom Headers
const result = await client.query(
'{ entries }',
undefined,
{
requestHeaders: {
'X-Contentful-User-Agent': 'my-app/1.0.0'
}
}
);Using GraphQL Fragments
const query = `
fragment PostFields on BlogPost {
sys { id }
title
slug
publishDate
}
query {
blogPostCollection(limit: 10) {
items {
...PostFields
}
}
}
`;
const result = await client.query(query);Pagination
const query = `
query GetPosts($skip: Int!, $limit: Int!) {
blogPostCollection(skip: $skip, limit: $limit, order: publishDate_DESC) {
total
skip
limit
items {
sys { id }
title
publishDate
}
}
}
`;
let skip = 0;
const limit = 10;
let allPosts = [];
while (true) {
const result = await client.query(query, { skip, limit });
allPosts.push(...result.blogPostCollection.items);
if (result.blogPostCollection.items.length < limit) {
break;
}
skip += limit;
}Preview Mode
Use the preview API to fetch draft content:
const previewClient = new ContentfulGraphQLClient({
space: 'your-space-id',
accessToken: 'your-preview-token',
environment: 'master',
preview: true
});
const drafts = await previewClient.query('{ entries }');Custom Logger
import { Logger, LogLevel, ConsoleTransport } from '@bernierllc/logger';
const customLogger = new Logger({
level: LogLevel.DEBUG,
transports: [new ConsoleTransport()],
context: { app: 'my-app' }
});
const client = new ContentfulGraphQLClient({
space: 'your-space-id',
accessToken: 'your-token',
logger: customLogger
});Error Handling
The client provides enhanced error handling with query context:
try {
const result = await client.query('{ invalidField }');
} catch (error) {
console.error('Query failed:', error.message);
console.error('Status:', error.status);
console.error('GraphQL errors:', error.errors);
console.error('Original error:', error.originalError);
}MECE Principles
This package follows MECE (Mutually Exclusive, Collectively Exhaustive) principles:
Includes:
- GraphQL query execution
- Schema introspection
- Type-safe query building
- Variable interpolation
- Fragment support
- Query batching
Excludes:
- ❌ Mutations (Contentful GraphQL is read-only)
- ❌ Subscriptions (use webhooks)
- ❌ REST API operations (use CMA/CDA clients)
TypeScript Support
Full TypeScript support with strict typing:
interface BlogPost {
sys: { id: string };
title: string;
slug: string;
publishDate: string;
}
interface BlogPostCollection {
blogPostCollection: {
items: BlogPost[];
total: number;
};
}
const result = await client.query<BlogPostCollection>(query);
// result is fully typed as BlogPostCollectionPerformance Tips
- Use Query Batching - Execute multiple independent queries in parallel
- Cache Schema - Schema introspection is cached automatically
- Select Only Needed Fields - GraphQL allows precise field selection
- Use Fragments - Reuse field selections across queries
- Implement Pagination - Use
skipandlimitfor large collections
License
Copyright (c) 2025 Bernier LLC
This file is licensed to the client under a limited-use license. The client may use and modify this code only within the scope of the project it was delivered for. Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
Related Packages
- @bernierllc/contentful-types - TypeScript types for Contentful
- @bernierllc/contentful-client-core - Core HTTP client utilities
- @bernierllc/contentful-cda-client - Content Delivery API client
- @bernierllc/contentful-cma-client - Content Management API client
- @bernierllc/logger - Structured logging
