Readme · Migration · Advanced · TypeScript · Contributing
With version 10.0.0, we have completely rewritten the client to give the user more support on types.
Table of contents
When querying for entries and assets, you get full type support for keys and values. This applies to:
getEntrygetEntriesgetAssetgetAssetsparseEntries- initial
synccalls
We have 2 levels of support:
Static query keys are not influenced by the shape of the entries or assets you're querying for.
getEntries({
skip: 10,
limit: 20,
include: 5,
})Dynamic query keys are based on the given shape of the expected entries' content type.
To calculate dynamic keys, we have to provide the shape of the entries:
import * as contentful from 'contentful'
type CategoryEntrySkeleton = {
contentTypeId: 'category'
fields: {
categoryName: contentful.EntryFieldTypes.Text
}
}
type ProductEntrySkeleton = {
contentTypeId: 'product'
fields: {
productName: contentful.EntryFieldTypes.Text
image: contentful.EntryFieldTypes.AssetLink
price: contentful.EntryFieldTypes.Number
categories: contentful.EntryFieldTypes.Array<
contentful.EntryFieldTypes.EntryLink<CategoryEntrySkeleton>
>
location: contentful.EntryFieldTypes.Location
}
}We can then pass this shape to our getEntries call. This gives us the relevant information needed to calculate the dynamic keys and their possible value types.
const client = contentful.createClient({
space: '<space-id>',
accessToken: '<content-delivery-token>',
})
// content_type query parameter is required when filtering on any field
client.getEntries<ProductEntrySkeleton>({
content_type: 'product',
'fields.price[gt]': 100,
})- To limit the complexity of query types we use a simple type definition for search on references.
We only check that prefix of the form
fields.referencematches a reference field called "reference". The rest of the parameter is not evaluated and thus does not provide autocomplete functionality.
Query types that accept an array of values used to accept them as a comma-separated string which is no longer supported. The user must instead provide the original array.
The list of query filters that only accept arrays from now on:
selectorder[within][near][in][nin]
Example of the new usage:
client.getEntries<ProductEntrySkeleton>({
content_type: 'product',
'fields.location[near]': [10, 20, 30],
})With version 10.0.0 we introduce client chain modifiers to make better assumptions on response types.
Entries can be returned in six different response shapes. Thanks to the three client modifiers below, the expected return shape can be identified, making it safer to work with the returned data.
If the current chain includes withAllLocales, getAsset and getAssets expect an optional generic parameter for all existing locales in your space. parseEntries, getEntry and getEntries expect an optional second generic parameter.
If the Locale type is provided, your response type will define all locale keys for your field values:
import * as contentful from 'contentful'
const client = contentful.createClient({
space: '<space-id>',
accessToken: '<content-delivery-token>',
})
type ProductEntrySkeleton = {
fields: { productName: contentful.EntryFieldTypes.Text }
contentTypeId: 'product'
}
type Locales = 'en-US' | 'de-DE'
const entry = client.withAllLocales.getEntry<ProductEntrySkeleton, Locales>('some-entry-id')The return type of the getEntry is matching the fields shape
{
"fields": {
"productName": {
"de-DE": "<field-value>",
"en-US": "<field-value>"
}
}
}Similar for assets:
import * as contentful from 'contentful'
const client = contentful.createClient({
space: '<space-id>',
accessToken: '<content-delivery-token>',
})
type Locales = 'en-US' | 'de-DE'
const asset = client.withAllLocales.getAsset<Locales>('some-asset-id'){
"fields": {
"file": {
"de-DE": "<field-value>",
"en-US": "<field-value>"
}
}
}If the current chain includes withoutLinkResolution, the returned type doesn't resolve linked entities, but keeps them as link objects instead.
import * as contentful from 'contentful'
const client = contentful.createClient({
space: '<space-id>',
accessToken: '<content-delivery-token>',
})
type ProductEntrySkeleton = {
contentTypeId: 'product'
fields: {
productName: contentful.EntryFieldTypes.Text
image: contentful.EntryFieldTypes.AssetLink
price: contentful.EntryFieldTypes.Number
}
}
type ReferencedProductEntrySkeleton = {
fields: { relatedProduct: contentful.EntryFieldTypes.EntryLink<ProductEntrySkeleton> }
contentTypeId: 'referencedProduct'
}
const entry = client.withoutLinkResolution.getEntry<ReferencedProductEntrySkeleton>('some-entry-id')The return type of getEntry is matching the fields shape
{
"fields": {
"productName": {
"type": "Link",
"linkType": "Entry",
"id": "linkedProductId"
}
}
}If the current chain includes withoutUnresolvableLinks, the returned type doesn't include linked entries that are not resolvable, for example if the linked entity does not exist anymore or is not yet published.
import * as contentful from 'contentful'
const client = contentful.createClient({
space: '<space-id>',
accessToken: '<content-delivery-token>',
})
type ProductEntrySkeleton = {
contentTypeId: 'product'
fields: {
productName: contentful.EntryFieldTypes.Text
image: contentful.EntryFieldTypes.AssetLink
price: contentful.EntryFieldTypes.Number
}
}
type ReferencedProductEntrySkeleton = {
fields: { relatedProduct: contentful.EntryFieldTypes.EntryLink<ProductEntrySkeleton> }
contentTypeId: 'referencedProduct'
}
const entry =
client.withoutUnresolvableLinks.getEntry<ReferencedProductEntrySkeleton>('some-entry-id')The return type of getEntry is matching the fields shape
{
"fields": {}
}It is recommended to define field types for all your content types. This helps the type system to infer all possible query keys/value types for you. Doing this manually is cumbersome, but do not worry! There are several OSS projects out there to generate type definitions for Contentful content types:
- cf-content-types-generator
- contentful-typescript-codegen
- contentful-ts-type-generator
- contentful-ts-generator
If you prefer a GUI, you can also use an app in your Contentful space to automatically generate TypeScript definitions for your content types:

