GraphQL
Queries
Content types, entry lists and single entries.
All queries need the content.view permission (except apiKeys, which needs api_keys.manage) and run against the environment the request asks for.
Content types
1query ContentTypes {2 contentTypes {3 id4 name5 slug6 description7 isActive8 fields {9 name10 slug11 fieldType12 isRequired13 order14 }15 }16}Returns every content type, newest first, each with its fields in order. Fetch a single one by slug or ID; both return null if it doesn't exist:
1query BlogPostType {2 contentType(slug: "blog-posts") {3 id4 name5 fields { slug fieldType validation fieldOptions defaultValue }6 }7}1query TypeById {2 contentTypeById(id: "p8d2k4m9x0s1") { name slug }3}ContentType fields: id, teamId, name, slug, description, icon, isActive, fields, createdAt, updatedAt. (color is still in the schema for older clients but is always null.)
ContentField fields: id, contentTypeId, name, slug, fieldType, description, isRequired, defaultValue (a string; JSON values are stringified), fieldOptions, validation, order, createdAt, updatedAt.
Entry lists
1query BlogPosts($limit: Int, $offset: Int) {2 contentEntries(3 contentType: "blog-posts"4 status: "published"5 limit: $limit6 offset: $offset7 ) {8 entries {9 id10 title11 slug12 fields13 publishedAt14 author { firstName lastName }15 }16 total17 limit18 offset19 }20}| Argument | Default | Description |
|---|---|---|
contentType | required | The content type's slug. An unknown slug is a NOT_FOUND error. |
status | any | published, draft or archived. Without it, every status is returned. |
language | any | Only entries written in this language. |
locale | en-US | Return translated text fields in this locale. |
limit | 10 | 1 to 100. |
offset | 0 | Entries to skip. |
Entries come newest first. total counts every match, not just this page.
REST's
/v1/content/:typereturns only published entries by default; GraphQL'scontentEntriesreturns all of them unless you passstatus. A public site should always passstatus: "published".
A single entry
By content type and slug:
1query Post($slug: String!) {2 contentEntry(contentType: "blog-posts", slug: $slug, locale: "fr-FR") {3 id4 title5 status6 stage { id name }7 locale8 fields9 contentType {10 name11 fields { slug fieldType }12 }13 }14}Or by ID:
1query PostById {2 contentEntryById(id: "k3x9m2p7q1a4") {3 title4 slug5 status6 }7}Both return null when nothing matches, and both return the entry whatever its status. contentEntry also takes language to pick one language version.
ContentEntry fields: id, contentTypeId, contentType, teamId, title, slug, status, language, locale, authorId, author, fields, publishedAt, createdAt, updatedAt.
author is a User with id, firstName, lastName and email (companyName is still accepted for older queries but is no longer collected, so it's usually null). For an entry created through the API, the author is the person who created the API key.
API keys
Lists the workspace's keys. Needs api_keys.manage. The keys themselves are never returned, only a short preview.
1query Keys {2 apiKeys {3 id4 name5 keyPreview6 permissions7 isActive8 lastUsedAt9 createdAt10 }11}Variables
Pass values as variables rather than building query strings, so nothing needs escaping:
1const query = `2 query Post($slug: String!, $locale: String) {3 contentEntry(contentType: "blog-posts", slug: $slug, locale: $locale) {4 title5 fields6 }7 }8`;910const response = await fetch('https://api.graphcms.io/graphql', {11 method: 'POST',12 headers: {13 Authorization: `Bearer ${process.env.GRAPH_API_KEY}`,14 'Content-Type': 'application/json',15 },16 body: JSON.stringify({ query, variables: { slug: 'hello-world', locale: 'de-DE' } }),17});1819const { data } = await response.json();