api-documentation

API documentation

The API exposed by prismic.io is at the same time very powerful, allowing you to query and manipulate your content as you wish, and very easy to use, with concepts and tools that help you do things efficiently.

The main things that you need to know and remember:

  • /api is the single endpoint of your content repository. You can learn and browse everything else from there. You should never forge any API URL in your code, as all usable forms (that define what URLs you can build) are available in /api (and the URLs might change as features get rolled out). Today, available forms are "everything" (to query within all the repository, potentially filtering with additional predicates), plus one form per collection defined in the writing-room (to query within the collection, potentially filtering with additional predicates); this flexible RESTful way to expose our API's capabilities lets us free to add new forms later as we roll out new features.
  • a ref is a place on the repository's timeline, identified by a ref ID. For instance: the documents that are being live now are on a ref we call the "master ref"; also each future content release has a ref ID; and each previously published state of the content repository also has a ref ID. Each time something changes on the master ref (live) or on a content release, a new ref ID is generated, and the former one is archived.
  • except on /api, you will always need the ref ID to query anything, because everything your content repository tells you is ref-dependent. You'll learn the ID of the master ref and of the content release's refs in /api.
  • for the above reasons, you should never cache your call to /api (we've made it especially performant anyway), because it is the document that allows you to know if you're in sync; but you may cache any other request virtually forever (and we already cache it for you anyway on our side).
an-overview-of-the-api-browser

An overview of the API browser

We'll see later what predicates are, but you'll see that the API browser is the most helpful friend to create predicate-based queries: try a query, view the result, correct the query (making it more precise with more predicates, for instance), repeat until you're happy!

Once you're done, you just have to copy and paste your predicates into your code, and you're good to go!

To get to the API browser from your writing room, you will find it behind the "?" icon at the bottom right of every page.

The first time you visit the API Browser, before doing anything, you may want to start by hitting the "Search documents" button directly; this will return all documents in the repository, paginated by 20.

1- Select a ref

The selectbox will offer you to work on any present or future release of which you have access.

If you're supposed to have access to future releases and you can't see them, you may have to log in (step 2).

2- Login

By checking your credentials in the writing room, the API browser will display all the releases you have rights to access. The lock is closed when you're not logged in, and open when you're logged in.

3- Choose your form

The form by default is "Everything", which retrieves all your content in a paginated way, and allows you to perform predicate-based querying on them; the other available forms today allow you to do the same thing on the limited content set of each collection in the writing-room.

4- Type in your predicates to query within your form

Press enter when you're done with one of them, double-click to modify one. To learn more about how to write the predicates, see our "Predicates' syntax" section below.

5- Type in your orderings

Here too, press enter when you're done with one of them, double-click to modify one. To learn more about how to write the orderings, see our "Orderings" section below.

6- There you go!

Launch your query, see what you're getting instantly. If you're not happy with it, reopen the form, and change your query, repeat!

7- HTML or JSON?

By default, the API browser will display its output as nicely-formatted HTML, optimized for developers to use the information as effectively as possible; but you can also choose to see the response in the same exact way it will be received by your application, in JSON.

You can also make that choice before launching the query.

predicate-based-queries

Predicate-based queries

What are predicates?

Queries in prismic.io are based on predicate, which, if you've never queried data this way, is very easy to learn, and extremely powerful.

For instance, picture your content repository as an attic with many objects of many colors, shapes and sizes. All you need to do is describe facts (predicates) about the objects you want, and prismic.io will return all the objects that match. For instance, if you want blue objects, you'll just say:

d has the color set to "blue"

prismic.io will return every object that can be d.

You can also say much more sophisticated things, that would be very hard to write with non-predicate-based query languages. For instance:

d has the color blue
e has the color red
d is the same shape as e

prismic.io will then know to return every pair of objects d and e, that match what you described. It's not more complicated!

Do note that while predicate-based queries are known to allow this kind of sophisticated querying effortlessly, prismic.io only supports single-document queries for the moment. The documentation below will therefore all be about querying a single document for now.

Predicates' general syntax

A predicate is usually a comparison, making sure there is some kind of match between a fragment and a value. The way we're matching them is described by an operator. The syntax looks like this:

[:d = namespace.operator(<fragment>, <value>)]
  • the variable "d" is simply the variable name for a given found document; this is useful when there are several predicates.
  • the namespace brings some nice classification to operators; the most used is the "root" namespace, that doesn't need to be written. For instance, here is an example using the "at" (equality) operator of the "root" namespace: [:d = at(document.type, "product")]
  • the fragment expresses which piece of information stored in a prismic.io document we're working with. See the "Writing your fragment" paragraph below to learn more
  • values can be strings, numbers, etc. to compare the fragment with

Sometimes, you might have operators that take several values (for instance to check that a fragment is between two values); in which case, a predicate can also look like this:

[:d = namespace.operator(<fragment>, <value1>, <value2>, ...)]

Lists of predicates

When you'll be using predicates in your code, you'll have to present them as a list of predicates, between square brackets, like this:

[[:d = at(document.type, "product")][:d = any(document.tags, ["Macaron"])]]

This also means that if you're presenting a single query in your code, you have to write it as a single-item array; this makes it look like a single predicate with two square brackets, so don't be surprised!

[[:d = any(document.tags, ["Macaron"])]]

Writing your fragment

There are two types of fragments:

  • a non-type-dependent fragment start with "document.*", and there are only three: either "document.type", "document.tags" or "document.id" (the API browser's autocomplete will help you with that). You will see that some operators that are able to apply operations to the whole document may also use "document" as a fragment. Note that document.slug is not available to run queries, read our "URLs, linkResolver() and asHtml()" paragraph at the very bottom of this API documentation.
  • a type-dependent fragment start with "my.*", and the complete syntax is: "my.documenttypename.fragmentname". For instance: "my.product.price", for the price of a product (the API browser's autocomplete will help you with that too).

Note that:

  • you can use a type-dependent fragment to query documents that are linking to a given document through a given link fragment: [:d = at(my.documentType.fragmentName, "targetedDocumentId")]. This makes document links effectively queryable in both directions.
  • you can also use a type-dependent fragment to query a sub-fragment inside a group fragment. This will match with every document that has one of their sub-fragments at the given value: [:d = at(my.documentType.groupFragmentName.subFragmentName, "value")].
predicate-operators

Predicate operators

The operator in a predicate is the part that defines how the fragment is matched to the value. For instance, in the predicate above that we just mentioned, the operator is "at", and the operator's namespace is "root" (which is the one that you don't have to write):

[:d = at(document.type, "product")]

The root namespace

Operators on the root namespace tend to be for basic tasks that work for all relevant fragment types.

The at operator is the equality operator, checking that the fragment matches the described value exactly. It takes as a value:

  • either an array of strings if you're applying it on tags, in which case it checks that ALL the listed tags are part of the document;
[:d = at(document.tags, ["Macaron", "Cupcake"])]
  • or a string for any other case (text fragment, document type, document id, ...).
[:d = at(document.type, "product")]

Warning: you shouldn't apply the "at" predicate to a structured text fragment, comparing its value to a full text string, as it won't match it. This is pretty logical: your structured text fragment contains so much more than just its full text value!

The any operator takes an array of strings as a value. It works exactly the same way as the at operator, but checks whether the fragment matches either one of the values in the array. You can use it with all fragment types; for instance, if you want to get the documents that are either a macaron or a cupcake:

[:d = any(document.tags, ["Macaron", "Cupcake"])]

The fulltext operator provides two capabilities: either you want to check if a certain string is anywhere inside a document (this is what you should use to make your project's search engine feature), or if the string is contained inside a specific document's structured text fragment. Checking whether a string is contained in the document would therefore look like this:

[:d = fulltext(document, "delight")]

And checking whether it is contained in a given structured text fragment would look like this:

[:d = fulltext(my.product.short_lede, "delight")]

The number namespace

Operators that belong to the number namespace are meant to be applied on number fragments.

The gt operator tests whether a Number fragment is strictly greater than a numeric value:

[[:d = number.gt(my.product.price, 2)]]

The lt operator tests whether a Number fragment is strictly lower than a numeric value:

[[:d = number.lt(my.product.price, 4.2)]]

The inRange operator tests whether the Number fragment is between two numeric values; this is one of those operators that take more than just one value:

[[:d = number.inRange(my.product.price, 2, 4)]]

The date namespace

Operators that belong to the date namespace are meant to be applied on Date fragments. Values for dates are potentially:

  • dates following the ISO 8601 standard: "2013-09-25"
  • datetimes following the ISO 8601 standard: "2013-11-19T15:30:59z"
  • or a numerical timestamp: 1384872209277

All of these formats are allowed when this documentation uses the placeholder <date>.

The date.before and date.after operators test whether the Date fragment is before/after the specified date:

[
[:d = date.after(my.blog-post.publication-date, <date>)]
[:d = date.before(my.blog-post.publication-date, <date>)]
]

The date.between operator tests whether the Date fragment is between the specified two dates:

[:d = date.between(my.blog-post.publication-date, <date>, <date>)]

The date.day-of-month operator tests whether the Date fragment matches the specified day of the month. The value must be between 1 and 31.

[:d = date.day-of-month(my.blog-post.publication-date, 10)]

The date.day-of-month-before and date.day-of-month-after operators operators test whether the day of month of the Date fragment is strictly lower/greater than the specified day of the month. The value must be between 1 and 31.

[
[:d = date.day-of-month-after(my.blog-post.publication-date, 10)]
[:d = date.day-of-month-before(my.blog-post.publication-date, 10)]
]

The date.day-of-week operator tests whether the Date fragment matches the specified day of the week. The value must either be between 1 and 7, or between "Monday" and "Sunday".

[
[:d = date.day-of-week(my.blog-post.publication-date, 2)]
[:d = date.day-of-week(my.blog-post.publication-date, "Tuesday")]
]

The date.day-of-week-before and date.day-of-week-after operators test whether the day of week of the Date fragment is strictly lower/greater than the specified day of the week. The value must either be between 1 and 7, or between "Monday" and "Sunday".

[
[:d = date.day-of-week-after(my.blog-post.publication-date, 4)]
[:d = date.day-of-week-before(my.blog-post.publication-date, "Friday")]
]

The date.month operator tests whether the Date fragment matches the specified month. The value must be either between 1 and 12, or between "January" and "December".

[
[:d = date.month(my.blog-post.publication-date, 2)]
[:d = date.month(my.blog-post.publication-date, "May")]
]

The date.month-before and date.month-after operator tests whether the month of the Date fragment is strictly lower/greater that the specified month. The value must be either between 1 and 12, or between "January" and "December".

[
[:d = date.month-after(my.blog-post.publication-date, 4)]
[:d = date.month-before(my.blog-post.publication-date, "December")]
]

The date.year function tests whether the Date fragment matches the specified year.

[
[:d = date.year(my.blog-post.publication-date, 2013)]
]

The date.year-before and date.year-after operators test whether the year of the Date fragment is strictly lower/greater that the specified year.

[
[:d = date.year-after(my.blog-post.publication-date, 2011)]
[:d = date.year-before(my.blog-post.publication-date, 2013)]
]

The date.hour operator tests whether the Date fragment matches the specified hour. The value must be between 0 and 23.

[
[:d = date.hour(my.blog-post.publication-date, 2)]
]

The date.hour-before and date.hour-after operators test whether the hour of the Date fragment is strictly lower/greater than the specified hour. The value must be between 0 and 23.

[
[:d = date.hour-after(my.blog-post.publication-date, 17)]
[:d = date.hour-before(my.blog-post.publication-date, 12)]
]
orderings

Orderings

Predicates help you get the right set of content, but you might want the JSON presented in a specific order, so you can use it as is, in your project.

To order a collection by a given fragment, you simply need to tell which fragment, between brackets (see our "Writing your expressions" paragraph above):

[my.product.price]

If you want to order it from greatest to lowest, you can simply add "desc" next to the fragment's name:

[my.product.price desc]

Depending on your query, your collection may contain documents of several different types. If some returned documents do not contain this fragment, you can specify as many fragments as you want, in order to address all of the documents you are querying:

[my.product.price desc,my.blog-post.date desc,my.store.country]

This can also be useful if, on a given type, several documents have the same value for your ordered fragment; you can then tell them apart by setting secondary orderings, that easily:

[my.product.price desc,my.product.name,my.blog-post.date desc,my.store.country,my.store.city]

Be careful: since your fragments are typed, you do have to list them all even when they have the same name for different types. For instance:

[my.product.name,my.job-offer.name]

At the moment, it is possible to order by those fragment types:

  • Text
  • Date
  • Number
  • Range
  • Select
  • StructuredText (the order will be based on the first Text block found inside the fragment)
json-responses

JSON responses

Although the API is very easy to experiment with, it is advised to use a kit for your technology; we guarantee that kits for your technology that appear on the prismic.io developers portal have been reviewed by our team, and are optimal to ensure compatibility with prismic.io's future features. If no library exists for your technology, you should read this to find out how easy it is to make one.

But for now, let's focus on the API's parameters and naked JSON responses.

The /api endpoint

This is the document that needs to be called each time you want to make sure you're about to display content that is up to date.

It takes a single, optional parameter:

  • access_token: OAuth2-based token that will allow the user to access the limited information depending on his rights as set in prismic.io's writing room. If the API is private, you won't be able to get anything without this access_token; if the API is public, leaving this access_token out will simply make the /api endpoint give you access to the master ref only.

The JSON response contains:

The refs object array lists the refs that can be accessed by the current user. Refs are places on the repository's timeline that you may wish to query on. The "current" ref (marking the documents that are currently live) is called the "master ref", and the other refs listed on /api are the upcoming content releases that are accessible to the current user. This list changes depending on the access_token parameter. Each object in this array has three fields:

  • ref (a string) is the ref's ID,
  • label (a string) is the ref's display name of the ref,
  • isMasterRef is a boolean defining whether the current ref is the master ref or not.

A ref item object may look like this:

{
"ref":"UjsEgLGIJ4QLK1j1",
"label":"Master",
"isMasterRef":true
}

The bookmarks object has as keys the existing bookmark IDs, and as values the matching document IDs. A bookmarks object may look like this:

"bookmarks":{
"about":"UjMvxsuvzT4ARdnq",
"jobs":"UjMuNrGIJ44BrirU",
"stores":"UjMwVbGIJ44BrirX"
}

The types object has as keys the document types' IDs, and as values the document types' display names. A types object may look like this:

"types":{
"blog-post":"Blog post",
"store":"Store",
"product":"Product"
}

The tags string array contains all tags existing in the repository. A tags string may look like this:

"tags":[
"Macaron",
"Cupcake",
"Featured",
"Pie"
]

The forms objects are very critical: they are here to ensure that your code doesn't need to include any API URL but /api itself. If you're using a kit, you shouldn't worry about this, and just call your kit's methods as described in the documentation; these form objects are basically telling your kit what it should allow you to do. That way, even when URLs and features happen to change (which may happen), your code will work forever.

As the form object describes proper RESTful forms, it defines actions you can perform to query the API; for instance, you always get a form "everything" that allows you to retrieve all the documents in the API. You also get a form for each existing collection set in your repository, to retrieve and query documents within that collection. A RESTful form is similar to a function, therefore it has parameters (that are called "fields"), which are stored in the "fields" array of the form object. Those fields include their type (for instance, "String"), and their default value if they are not mandatory.

Inside the form object, the other fields are:

  • name: the display name of the form
  • method: the HTTP method to apply to the REST request (for instance: "GET")
  • rel: the type of result you'll get (for instance: "collection")
  • enctype: the encoding type to apply to the REST request
  • action: the URL of the REST request

A form object may look like this:

"macarons":{
"name":"Macarons",
"method":"GET",
"rel":"collection",
"enctype":"application/x-www-form-urlencoded",
"action":"http://lesbonneschoses.prismic.io/api/documents/search",
"fields":{
"ref":{
"type":"String"
},
"q":{
"default":"[[:d = at(document.tags, [\"Macaron\"])][:d = any(document.type, [\"product\"])]]",
"type":"String"
}
}
}

Search form responses: global structure

Documents are retrieved in the form of search form responses, including all the structured information about the documents matching an expressed query. Search form responses are paginated and return 20 documents at a time by default.

Today, since the API only allows you to retrieve documents from the content repository, all the offered forms are search forms, and all return this kind of response. The parameters for search forms are described in the /api document, and they are:

  • ref (the only mandatory parameter) is the ID of the ref to apply the query on. A ref is a place on the repository's timeline that you may wish to query on, like: what is live right now (the master ref), or what will be live for that future content release, what was live in the past, etc.
  • q is the list of predicates defining the query, between square brackets (see our "Predicates' syntax" section)
  • orderings allows you to order your collection along the fields you wish (see our "Orderings" section)
  • pageSize and page allows you to page your content, specifying the size of each page in your paging, and the number of the page you wish to be querying. Please remember to use those parameters together (not just one without the other). Default values are pageSize=20, and page=1.

The response JSON object has several integer or string fields, and a single array field (results, the list of documents). Those integer and string fields are not official yet, and may not be supported by your development kit:

  • page, results_per_page, results_size, total_results_size, total_pages are self-explanatory fields that relate to how data is paginated
  • next_page and prev_page are RESTful links to the search form resources; they may be null if the current page is the first or last one

Within the results array, documents are object that have as fields:

  • id and type are self-explanatory!
  • href is the API URL to call to get the document itself (again, we don't want you to hardcode any API URL)
  • tags is a string array listing the tags
  • slugs is a string array listing all the past slugs (since slugs change as the document's title changes); the current slug is the first element of the array.
  • data is an object containing the structured content itself. I contains a single field, whose key is the document's type, and whose value is an object that lists the document's fragments. Each fragment has as a key the fragment's ID, and contains an object that has two fields: the type string and the value object (the structure of latter depends on the type; all the possibilities for the value object are listed in the next paragraph).

More clearly, a typical document object may look like this:

{
"id" : "UkL0gMuvzYUANCpF",
"type" : "product",
"href" : "https://lesbonneschoses.prismic.io/api/documents/search?ref=UkL0hcuvzYUANCrm&q=%5B%5B%3Ad+%3D+at%28document.id%2C+%22UkL0gMuvzYUANCpF%22%29+%5D%5D",
"tags" : [ "Macaron" ],
"slugs" : [ "speculoos-macaron", "a-speculoos-macaron" ],
"data" : {
"product" : { /* This is the ID of the document type */
"price" : {
"type" : "Number",
"value" : 3.5
},
"name" : {
"type" : "StructuredText",
"value" : [{ "type" : "heading1", "text" : "Speculoos Macaron", "spans" : [ ] }]
},
/* etc. other fragments */
}
}
}

Search form responses: the "value" field, per fragment type

For basic types and for selects, the value field contains the value directly, either as a number or a string.

"price" : {
"type" : "Number",
"value" : 2.5
}

For embeds, the value field contains a single field oembed, which is a regular oEmbed object, as described by the oEmbed spec.

"video" : {
"type" : "Embed",
"value" : {
"oembed" : {
"provider_url" : "http://www.youtube.com/",
"type" : "video",
"thumbnail_height" : 360,
"height" : 270,
"thumbnail_url" : "http://i1.ytimg.com/vi/baGfM6dBzs8/hqdefault.jpg",
"width" : 480,
"provider_name" : "YouTube",
"html" : "<iframe width=\"480\" height=\"270\" src=\"http://www.youtube.com/embed/baGfM6dBzs8?feature=oembed\" frameborder=\"0\" allowfullscreen></iframe>",
"author_name" : "Siobhan Wilson",
"version" : "1.0",
"author_url" : "http://www.youtube.com/user/siobhanwilsonsongs",
"thumbnail_width" : 480,
"title" : "Siobhan Wilson - All Dressed Up",
"embed_url" : "https://www.youtube.com/watch?v=baGfM6dBzs8"
}
}
}

For in-repository links ("Link.document"), the value field is an object containing a isBroken boolean, and a document object describing the document (id, type, slug).

"link" : {
"type" : "Link.document",
"value" : {
"document" : {
"id" : "Uo0X2gEAAJVG2ePz",
"type" : "article",
"tags" : [
"help"
],
"slug" : "first-document"
},
"isBroken" : false
}
}

For file links ("Link.file"), the value field is an object containing an file object, containing itself fields describing the image (name, kind, url, size). Images links are the same, except their object is called image, and they also contain height and width.

"link": {
"type" : "Link.file",
"value" : {
"file" : {
"name" : "Sponsor us.pdf",
"kind" : "document",
"url" : "https://prismic-io.s3.amazonaws.com/worldchanger%2Fe56fb9ce-20de-446c-b34b-cf0d731f23b8_sponsor+us.pdf",
"size" : "110972"
}
}
}

For web links ("Link.web"), the value field is an object containing a single url string field.

"link" : {
"type" : "Link.web",
"value" : {
"url" : "https:// prismic.io"
}
}

For images, the value field contains a main view, and an object of aternative views, which all together look like this:

{
"main":{
"url":"https://prismic-io.s3.amazonaws.com/lesbonneschoses/754c5929c6cc5907f5ef6c3ddad78d6f28c44b9d.jpg",
"alt":"",
"copyright": "",
"dimensions":{
"width":1500,
"height":500,
}
}
"views":{
"medium":{
"url":"https://prismic-io.s3.amazonaws.com/lesbonneschoses/fcc9921348bcccd2aa8dfe6fdf019fef5f952db9.jpg",
"alt":"",
"copyright":"",
"dimensions":{
"width":800,
"height":250,
}
}
"icon":{
"url":"https://prismic-io.s3.amazonaws.com/lesbonneschoses/5c2b5e5b2b3043846853a6f1f7e028f2577cd57c.jpg",
"alt":"",
"copyright":"",
"dimensions":{
"width":250,
"height":250
}
}
}

For structured texts, the value field is an array of sections, defined by their type ("heading1", "heading2", "paragraph", "image", "embed", ...). Depending on the section's type, the next fields in the object are the following:

  • if the type implies it is a text section ("heading1", "heading2", "paragraph", ...), then the other fields will be: text (the written content itself) and spans. The latter is an array of objects, to represent formatted bits of the section: each array contains the fields start, end and type. The latter has as possible values either "strong", "italic" or "hyperlink". If the value is "hyperlink", then the object will contain an additional field data, which is structured the same way as link fragments.
  • if the type is "embed", then the object will also contain an oembed field, that works the same way as in embed fragments.
  • if the type is "image", then the object will also contain the same fields as image fragments (url, alt, copyright, dimensions).

An example may look like this:

"content" : {
"type": "StructuredText",
"value" : [
{
"type": "heading1",
"text" : "This is a title",
"spans" : [
{
"start" : 8,
"end" : 9,
"type" : "em"
}
]
},
{
"type" : "paragraph",
"text" : "This is a paragraph that has a lot of formatting going on (even though this formatting could be on any prismic.io text block).",
"spans" : [
{
"start" : 38,
"end" : 48,
"type" : "strong"
},
{
"start" : 59,
"end" : 103,
"type" : "em"
},
{
"start" : 103,
"end" : 113,
"type" : "hyperlink",
"data": {
"type" : "Link.web",
"value" : {
"url": "https://prismic.io"
}
}
},
{
"start" : 103,
"end": 113,
"type" : "em"
},
{
"start" : 113,
"end" : 124,
"type" : "em"
}
]
},
{
"type" : "list-item",
"text" : "This is",
"spans" : [ ]
},
{
"type" : "list-item",
"text" : "a bulleted",
"spans" : [ ]
},
{
"type" : "list-item",
"text" : "list",
"spans" : [ ]
},
{
"type" : "image",
"url" : "https://prismic-io.s3.amazonaws.com/rudysandbox/abc59d245b772e06bd610d5ade73090e4765a971.jpg",
"alt" : "Sunset",
"copyright" : "",
"dimensions" : {
"width": 300,
"height": 225
}
}
]
}

For group fragments, the value field is simply an array of sets of sub-fragments. For instance, in the example below, the first group of this fragment is composed of a "linktodo" link sub-fragment and a "desc" structured text sub-fragment, while the second group of the same fragment is composed of a single "linktogo" link sub-fragment:

"docs" : {
"type" : "Group",
"value" : [
{
"linktodoc" : {
"type" : "Link.document",
"value" : {
"document" : {
"id" : "UrDejAEAAFwMyrW9",
"type" : "doc",
"tags" : [ ],
"slug" : "installing-meta-micro"
},
"isBroken" : false
}
},
"desc" : {
"type" : "StructuredText",
"value" : [{
"type" : "paragraph",
"text" : "A detailed step by step point of view on how installing happens.",
"spans" : [ ]
}]
}
},
{
"linktodoc" : {
"type" : "Link.document",
"value" : {
"document" : {
"id" : "UrDmKgEAALwMyrXA",
"type" : "doc",
"tags": [ ],
"slug" : "using-meta-micro"
},
"isBroken" : false
}
}
}
]
}
kits-and-helpers

Kits and helpers

In most technologies, we provide code not only to help you perform your queries, but also to help you output, manipulate and template your content the way you want it.

Although you definitely want to go check out what is available for your technology in the "Select your technology" section of the prismic.io developers portal, the kits all have common grounds that are explained here, and that will help you understand how to use them effectively.

Types of kit

First, note that there are three types of libraries you can find:

  • development kit, a minimal library, providing binding for a specific programming language to the prismic.io REST API. It can be embedded in any project as a dependency.
  • starter project which is a simple prismic.io project created for a specific technology and a specific development framework. It can be used as a foundation for any new prismic.io project. It depends on a prismic.io developement kit and on other external libraries that make it easier to work with a specific development framework.
  • example, a complete application written in a specific technology. It is based on an existing starter project but demonstrates more sophisticated ways to query and to display the content.

Typical features in a development kit

Any of our development kits is able to:

  • connect to a public prismic.io API
  • connect to a private prismic.io API using OAuth2 (under development in some kits)
  • let you compose and submit your prismic.io API queries
  • unmarshal the JSON response into usable objects in your technology
  • turn every document and fragment into a directly usable HTML output (that you're free to override if you need more specific HTML for your design)
  • turn every document and fragment into a directly usable plain text version
  • allow you to build a pagination easily (under development in some kits)
  • provide basic caching adapted to prismic.io, that can be overriden for a caching strategy of your choosing (under development in some kits)

Typical initialization in a starter project

Of course, the specifics of the following depends on your technology, but this will help you understand the idea. Those initialized variables are either stored as instance variables in your controller if your technology allows it, or passed around as a "ctx" object, for instance.

These typically get initiated at the beginning of your page's life:

The api object: it stores all the information you're getting from your initial single query on /api; this is typically done by a Prismic.Api function provided in your language kit. In starter projects and examples, it typically initializes from settings (the API endpoint, OAuth details, ...) that are in a configuration file (prismic.js, or prismic.yml, or...). The available fields of the api object are often:

  • bookmarks: an object where keys are the bookmark names, and values are the document's ID.
  • refs: an array of objects with three fields, "ref" for the ref ID, "label" for the displayable label, and "isMasterRef" which is a boolean telling if the ref is the master (displaying the documents as they are live now).
  • form(): this function returns the objects you'll use to perform queries, read "Typical querying" below to see how.
  • master(): returns the ID of the master ref (do not pass it directly in a ref() function, prefer to store it once in a page-scope variable and use the variable then; it will make it easier for you to change the queried ref for all queries at once)
  • oauthInitiate and oauthToken: stores the URL to log in using OAuth.

The form objects: it's an unavoidable road to run queries in prismic.io. Once you get the form you want to submit (for instance, the form "everything", which performs paginated queries in the whole repository), you may use:

  • ref(value): applies the value into data's "ref" parameter (which is the only one that is mandatory to submit the query), and returns the form so the function is chainable. The ref is the moment in the repository's timeline on which you wish to run your query; you should keep this variable in a page-scope variable (and not call api.master() directly here), it will make it easier for you to change the queried ref for all queries at once, and view your entire project in the future at once.
  • query(value): applies the value into data's "q" parameter (the list of predicates, between squared brackets), and returns the form so the function is chainable. See our "Predicates' syntax" section above to learn more about the syntax.
  • orderings(value): applies the value into data's "orderings" parameter, and returns the form so the function is chainable. See our "Orderings" section above to learn more about the syntax. This function might be missing from your kit as the ordering feature is currently under development, but you can always use the feature anyway through the similar set('orderings', value) function.
  • pageSize(value) and page(value): allow you to control the pagination of the results (default values are pageSize=20 and page=1), and returns the form so the function is chainable. Please remember to use those parameters together (not just one without the other). These functions might be missing from your kit as the pagination feature is currently under development, but you can always use the feature anyway through the similar set('page', value) function.
  • submit(): submits the form, and returns a list of documents whose fields you'll be able to use (see paragraph "Typical content manipulation and templating").

The controller instance variables or the ctx object itself: either one of them, because you need to keep the api object and other useful stuff around, but in some technologies you can't store them as variables in the page's scope, so you might have to use an object dedicated to passing the context from a function to the next (it is the case for JavaScript, for instance). The best way to achieve it per technology is already done in every starter project, but it's good for you to know that these variables are always at hand, whether this is done in page-scoped variables or in the ctx object:

  • the api object itself;
  • the ref you want to be using (that you may have chosen from the api object);
  • a maybeRef string, that is empty if the current ref is the master release and is the same as ref otherwise (for easier writing of ref-dependent URLs, for instance, since they shouldn't have a ref parameter at all if on the master release);
  • your customized linkResolver function, to transform documentLink objects into URLs on your front-office depending on the context (please read the "URLs, linkResolver() and asHtml()" paragraph in this page to know more);
  • your OAuth credentials if any.

Typical querying

As you may have understood, from a form, you can use the ref() function to specify the ref you want to use, and simply submit your query. For instance, if you wish to query the form "everything" (that exists in every repository, and retrieves every document), you can do it like this:

api.form("everything").ref(ref).submit(potential_callback);

Other forms that are always available for you are the ones allowing you to query within your collections; the form's ID is the same as the collection's ID:

api.form("products").ref(ref).submit(potential_callback);

On top of a given form and a given ref, you can use the query() function, to query your content using predicates (by pasting them from the API browser, for instance). It looks like this:

api.form("products").query(
'[[:d = any(document.tags, ["Macaron", "Cupcake"])][:d = any(my.product.flavour, ["Chocolate", "Vanilla"])]]'
).ref(ref).submit(potential_callback);

And of course, it looks like this when your query only contains a single predicate:

api.form("products").query(
'[[:d = any(document.tags, ["Macaron", "Cupcake"])]]'
).ref(ref).submit(potential_callback);

And you can also easily specify the way you want your collection to be ordered and paginated:

api.form("products").query('[[:d = any(document.tags, ["Macaron", "Cupcake"])]]')
.orderings('[my.product.price desc]').pageSize(10).page(2)
.ref(ref).submit(potential_callback);

Typical content manipulation and templating

As stated before, there are two types of fields:

Non-type-dependent fragments always exist no matter what document type you are using, and typically, you can use them like this (although the syntax depends on your technology):

  • document.id returns the id of the document.
  • document.type returns its type.
  • document.tags returns its tags.
  • document.slug returns its main slug, built from the first title in the document. prismic.io offers a basic slugging system for simple use cases, modern technologies are known to be the best at building URLs.
  • document.slugs returns an array of past slugs, existing when the document had another title, which will allow you to redirect cleanly.

Type-dependent fragments can be accessed using this function:

  • document.get("documentType.fragmentName"): returns the object holding the content of that fragment.

The returned object is always highly structured, and depending on the language you're using, you might have to explicit its type to use it in a template. Structured text is a good example of it: it takes a lot of structure to store all the relevant information the content writer expressed. Therefore, the data has to be processed to become a display-friendly information, and your language kit provides you with all the usual helper functions to do just that. Therefore, you may also get those:

  • document.getImage("documentType.fragmentName") returns for the given field, an object of the type "Image".
  • document.getImageView("documentType.fragmentName", "viewName") returns from the name of its image's field and the name of the view, an object of the type "ImageView".
  • document.getText("documentType.fragmentName") returns the given field as plain text.
  • document.getNumber("documentType.fragmentName") returns the given number field as a number.
  • document.getDate("documentType.fragmentName") returns the given date field as a native Date object in the technology.
  • document.getBoolean("documentType.fragmentName") returns the given field as a boolean.
  • document.getStructuredText("documentType.fragmentName") returns for the given field an object of the type "StructuredText".
  • document.getGroup("documentType.fragmentName") returns for the given field an object of the type "Group" (look at your kit's documentation to find out how to use it).
  • ...

On all types of objects the former functions return, you can always call a asHtml(linkResolver) function, which serializes the value of the fragment into HTML code that can be displayed as is, in your template (see next paragraph to learn more about linkResolver).

Except for asHtml(), fragment types come with extra functions allowing needed manipulations (mostly accessors). Do refer to your kit's technical documentation to see how it's done for yours (the URL of the documentation is listed in the kit's README file on GitHub).

Here are good examples of how this all may be used:

doc.get("document.body").asHtml(linkResolver);
doc.getStructuredText("document.body").asHtml(linkResolver);
doc.getImage("document.image").getView("icon").getUrl();

URLs, linkResolver() and asHtml()

In a nutshell:

  • asHtml() is a function that can be applied on document or fragment objects in your kit, providing basic (and overridable) serialization into HTML for any kind of content.
  • in case your fragment contains an internal link (we call it: a document link), and since prismic.io can't know your URL strategy, you have to pass asHtml() a linkResolver() closure or object carrying a customized method, whose purpose is to take a "documentLink" as a parameter, and turn it into its URL in your project (which is why you're the one who gets to build that function).

Here is an example of a realistic linkResolver() method in JavaScript:

linkResolver = function(ctx, documentLink) {
if (documentLink.isBroken) return;

/* Based on bookmark name of the document */
if(documentLink.id == ctx.api.bookmarks['about']) {
return '/about' + (ctx.maybeRef ? '?ref=' + ctx.maybeRef : '');
}
if(documentLink.id == ctx.api.bookmarks['jobs']) {
return '/jobs' + (ctx.maybeRef ? '?ref=' + ctx.maybeRef : '');
}

/* Based on document type of the document */
if(documentLink.type == 'store') {
return '/stores/' + documentLink.id + '/' + documentLink.slug + (ctx.maybeRef ? '?ref=' + ctx.maybeRef : '');
}
if(documentLink.type == 'product') {
return '/products/' + documentLink.id + '/' + documentLink.slug + (ctx.maybeRef ? '?ref=' + ctx.maybeRef : '');
}
}

To get you started, we included a very basic linkResolver() method in all the kits, so you can feel free to pass it as soon as now in all your asHtml() calls, and fine-tune the linkResolver() method. We advise you to have your linkResolver() method cover all the possible documents in your repository, so that your content writers can freely link to anything (meaning: if you create a new document type, you probably should iterate on your linkResolver() method).

The "documentLink" parameter is an object composed of the information available for a link in the document link JSON responses: the documentLink.id, the documentLink.type, the documentLink.tags, the documentLink.slug, and a boolean documentLink.isBroken. This is the information you get at all times to build your URLs (you could use the document's fragments in the URL, but that would take an extra query to retrieve your document, and we don't advise you to do that lightly).

In prismic.io, what uniquely identifies a document is its ID, which is the best way to build a URL. The best practice here is to build URLs just like Tumblr does, for instance: http://sadache.tumblr.com/post/65167373954/prismic-io-interviews. If you're worried about SEO, note that the ID in the URL is not hurtful; but it would be hurtful not to provide a natural-language slug somewhere in the URL. We've done the heavy lifting for you by providing that self-generated "slug" field, which we generate from the most relevant text in your document (either your first title if there's one, or you first bit of text). Since prismic.io remembers all of your past slugs for a document, our starter projects do some heavy lifting as well, by providing helpers that allow you to easily check if a slug is currently valid (and move on), was once valid but was changed since (and cleanly redirects), or was never valid (and displays a 404).

So remember: use the provided default linkResolver() method, and call asHtml() on anything you want! If the provided linkResolver() method doesn't suit your own document types (which is likely!), you can modify it as is best. Also: if the provided asHtml() method doesn't output the best HTML code for your design, they were designed to be easy to override, whatever your technology. Get in touch with us if you need to do that, and you're not sure how.