Querying for content
There are two main types of content in Contentful Entry
and Asset
where assets are binary files such as an image, a video, a pdf etc. and entries are content contained in a space
and represent an instance of a content type.
There are a couple of methods on ContentfulClient
that allows you to query for content. In this section we will go through and look at them all. Every example below assumes you
have an ContentfulClient
with the name of client
available.
Get a single Entry
To get a single entry use the GetEntryAsync<T>
method.
var entry = await client.GetEntryAsync<Entry<dynamic>>("id of entry");
This would make a call to the contentful API returning json in the following format:
{
"sys": {
"space": {
"sys": {
"type": "Link",
"linkType": "Space",
"id": "SpaceId"
}
},
"id": "id of entry",
"type": "Entry",
"createdAt": "2016-11-03T10:50:05.033Z",
"updatedAt": "2016-11-08T14:30:23.857Z",
"revision": 4,
"contentType": {
"sys": {
"type": "Link",
"linkType": "ContentType",
"id": "ContentTypeId"
}
},
"locale": "en-US"
},
"fields": {
"productName": "SoSo Wall Clock",
"slug": "soso-wall-clock",
"productDescription": "The newly released SoSo Clock from Lemnos marries simple...",
"sizetypecolor": "10\" x 2.2\"",
"image": [
{
"sys": {
"type": "Link",
"linkType": "Asset",
"id": "AssetId"
}
}
],
"tags": [
"home décor",
"clocks",
"interior design",
"yellow",
"gifts"
],
"categories": [
{
"sys": {
"type": "Link",
"linkType": "Entry",
"id": "EntryId"
}
}
],
"price": 120,
"brand": {
"sys": {
"type": "Link",
"linkType": "Entry",
"id": "EntryId"
}
},
"quantity": 3,
"sku": "B00MG4ULK2",
"website": "http://store.dwell.com/soso-wall-clock.html"
}
}
Notice that an entry consists of two major JSON properties, "sys"
and "fields"
where sys is system defined meta data of the entry, such as its id and revision, while the fields
property is the actual fields and their values, for example price is 120 and productName is SoSo Wall Clock.
The Entry<T>
class encapsulates these two JSON properties in a SystemProperties
and a Fields
property. The Fields
property is generic and can serialize the JSON fields into
any POCO class of your choice. In our example above we use the Entry<dynamic>
which is a convenient way to quickly fetch some content.
We could now easily display any property of our entry like this.
Console.WriteLine(entry.Fields.productName.ToString()) // => SoSo Wall Clock
Console.WriteLine(entry.Fields.brand.sys.id.ToString()) // => EntryId
Console.WriteLine(entry.Fields.sku.ToString()) // => B00MG4ULK2
//Meta data properties can be found in the SystemProperties property
Console.WriteLine(entry.SystemProperties.Id) // => id of entry
Console.WriteLine(entry.SystemProperties.Revision?.ToString()) // => 4
Often we would rather like to have a strongly typed generic parameter for our Entry<T>
like this.
public class Product {
public int Quantity { get; set; }
public string ProductName { get; set; }
public string Slug { get; set; }
}
var entry = await client.GetEntryAsync<Entry<Product>>("id of entry");
Console.WriteLine(entry.Fields.ProductName) // => SoSo Wall Clock
Console.WriteLine(entry.Fields.Quantity.ToString()) // => 3
Console.WriteLine(entry.Fields.Slug) // => soso-wall-clock
//We still have access to the meta data properties
Console.WriteLine(entry.SystemProperties.Id) // => id of entry
Console.WriteLine(entry.SystemProperties.Revision?.ToString()) // => 4
In some cases we are not even interested in the meta data properties at all. We could then just pass any arbitrary class as a generic argument.
public class Product {
public int Quantity { get; set; }
public string ProductName { get; set; }
public string Slug { get; set; }
}
var entry = await client.GetEntryAsync<Product>("id of entry");
Console.WriteLine(entry.ProductName) // => SoSo Wall Clock
Console.WriteLine(entry.Quantity.ToString()) // => 3
Console.WriteLine(entry.Slug) // => soso-wall-clock
//This does not compile, Product does not contain a definition for SystemProperties
Console.WriteLine(entry.SystemProperties.Id) // => id of entry
Console.WriteLine(entry.SystemProperties.Revision?.ToString()) // => 4
This would essentially only serialize the fields property of the JSON object.
Get multiple entries
There are several methods to retrieve multiple entries available. Let us look at each in turn.
Get all entries of a space
var entries = await client.GetEntriesAsync<Entry<dynamic>>();
//entries would be an IEnumerable of Entry<dynamic>
This would return all entries in a space in an IEnumerable<Entry<dynamic>>
. Just as in GetEntryAsync<T>
we could choose to provide GetEntriesAsync<T>
with another implementation
of T
For example Entry<Product>
or (if we're not interested in the meta data properties) Product
.
var entries = await client.GetEntriesAsync<Product>();
//entries would be an IEnumerable of Product
Every collection returned by the Contentful api has this JSON structure:
{
"sys": {
"type": "Array"
},
"total": 2,
"skip": 0,
"limit": 100,
"items": [
{
//...items
}
]
}
This is useful if the number of entries returned are very many and you need to paginate the result. The maximum number of entries ever returned for a single
result set is 1000 items, the default is 100 items. Our IEnumerable<T>
responses above does not correspond to this structure. If you're interested in the skip
, total
and limit
properties you should use the GetEntriesCollectionAsync<T>
method. This will return a ContentfulCollection<T>
which includes Skip
, Total
and Limit
properties. The generic T
parameter is in this case restricted to an IContentfulResource
but you would normally use it with an Entry<T>
.
Note that ContentfulCollection
implements IEnumerable<T>
and thus
you can write normal LINQ syntax directly against the collection instead of against collection.Items e.g.
entries.First()
as opposed to entries.Items.First()
which of course also works.
var entries = await client.GetEntriesCollectionAsync<Entry<Product>>();
Console.WriteLine(entries.Total.ToString()); // => 2
Console.WriteLine(entries.Skip.ToString()); // => 0
Console.WriteLine(entries.Limit.ToString()); // => 100
Console.WriteLine(entries.First().Fields.ProductName) // => SoSo Wall Clock
Get and filter entries
More often than not though we're not interested in every entry in a space. Rather we would like to filter the entries returned by a number of parameters. Contentfuls API exposes many powerful filtering options. You can read more in the api documentation.
When using the GetEntries
methods we can easily filter our query by using the QueryBuilder
class.
var builder = new QueryBuilder().ContentTypeIs("ContentTypeId").FieldEquals("fields.slug","soso-wall-clock")
var entries = await client.GetEntriesAsync<Product>(builder);
//entries would be an IEnumerable of Product
This example would filter the entries returned to be of content type with the id "ContentTypeId" and with the "fields.slug" property to be equal to "soso-wall-clock".
In fact filtering by content type id is such a common scenario that the ContentfulClient
exposes a helpful method to do just that.
var entries = await client.GetEntriesByTypeAsync<Product>("ContentTypeId");
//entries would be an IEnumerable of Product
This method can also take an optional QueryBuilder
for further filtering.
var builder = new QueryBuilder().FieldGreaterThan("sys.updatedAt", DateTime.Now.AddDays(-7).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssK"))
var entries = await client.GetEntriesByTypeAsync<Product>("ContentTypeId");
//entries would be an IEnumerable of Product
This would only return the entries with the content type with the id "ContentTypeId" and that has been updated in the last week.
You can also pass the query string directly to the GetEntries
methods if you wish.
var entries = await client.GetEntriesAsync<Product>("?content_Type=ContentTypeId&fields.productName[match]=Clock");
//entries would be an IEnumerable of Product
While this is possible, the recommended approach is to use the QueryBuilder
whenever possible as it will make sure your query string is correctly formatted.
Get a single asset
To get a single asset use the GetAssetAsync
method.
var asset = await client.GetAssetAsync("assetId");
This would return an asset as a JSON:
{
"sys": {
"space": {
"sys": {
"type": "Link",
"linkType": "Space",
"id": "SpaceId"
}
},
"id": "assetId",
"type": "Asset",
"createdAt": "2016-11-03T15:06:48.621Z",
"updatedAt": "2016-11-03T15:06:48.621Z",
"revision": 1,
"locale": "en-US"
},
"fields": {
"title": "ihavenoidea",
"file": {
"url": "//images.contentful.com/SpaceId/321/123/ihavenoidea.jpg",
"details": {
"size": 46022,
"image": {
"width": 600,
"height": 600
}
},
"fileName": "ihavenoidea.jpg",
"contentType": "image/jpeg"
}
}
}
That is then serialized into a strongly typed Asset
.
var asset = await client.GetAssetAsync("assetId");
Console.WriteLine(asset.SystemProperties.CreatedAt.ToShortDateString()); // => 2016-11-03
Console.WriteLine(asset.Title); // => ihavenoidea
Console.WriteLine(asset.File.Url); // => //images.contentful.com/SpaceId/321/123/ihavenoidea.jpg
Asset
is not generic as the Entry
class is. This would not make sense as assets always have roughly the same structure and every property is already present in the Asset
class.
Get multiple assets
Getting multiple assets is similar to getting multiple entries. The only real difference is that the methods are not generic but all return some variation of an IEnumerable<Asset>
instead.
Get all assets of a space
var assets = await client.GetAssetsAsync();
//assets would be an IEnumerable of Asset
Every collection returned by the Contentful api has this JSON structure:
{
"sys": {
"type": "Array"
},
"total": 7,
"skip": 0,
"limit": 100,
"items": [
{
//...items
}
]
}
This is useful if the number of entries returned are very many and you need to paginate the result. The maximum number of entries ever returned for a single
result set is 1000 items, the default is 100 items. Our IEnumerable<Asset>
responses above does not correspond to this structure. If you're interested in the skip
, total
and limit
properties you should use the GetAssetsCollectionAsync
method. This will return a ContentfulCollection<Asset>
which includes Skip
, Total
and Limit
properties.
Note that ContentfulCollection
implements IEnumerable<T>
and thus
you can write normal LINQ syntax directly against the collection instead of against collection.Items e.g.
assets.First()
as opposed to assets.Items.First()
which of course also works.
var assets = await client.GetAssetsCollectionAsync();
Console.WriteLine(assets.Total.ToString()); // => 7
Console.WriteLine(assets.Skip.ToString()); // => 0
Console.WriteLine(assets.Limit.ToString()); // => 100
Console.WriteLine(assets.First().Title) // => ihavenoidea
Get and filter assets
Just as with entries we can filter the assets by using eiher a handcrafted querystring or more conveniently a QueryBuilder
.
var builder = new QueryBuilder().MimeTypeIs(MimeTypeRestriction.Image).OrderBy("sys.createdAt");
var assets = await client.GetAssetsAsync(builder);
//assets would be an IEnumerable of Asset
This would return all assets that are images ordered by their creation date and would be equivalent of using the following querystring directly.
var assets = await client.GetAssetsAsync("?mimetype_group=image&order=sys.createdAt");
//assets would be an IEnumerable of Asset