April 5, 2022
By James Perkins
Read-only tokens allow you to query data from your project at any point in your application, whether that is on the server or on the client. Prior to Read-only tokens everything Tina did was through getStaticProps
or getStaticPaths
. This, for the most part, would handle most cases when using a headless CMS with an SSG. However as we move towards the 1.0 release of TinaCMS we want to be able to support more frameworks including React, Remix, and Gatsby.
Below are some use cases for Read-only tokens
Before you start with Read-only tokens you will need to make sure the repository you are using has the data layer enabled. This is required for the read-only tokens to work and also be performant.
Navigate to TinaCloud and click on the project you wish to add a token to, click on the "tokens" tab
Next, click "New Token" and fill out the fields required. The token name is how you can identify the token and "Git branches" is the list of branches separated by commas that the token has access to.
Finally, click "Create Token".
At this point you are now ready to make requests using the Read-only token. I have put together some examples of different use cases, they include SSR, CSR, SSG with fallback which should satisfy most use cases with Tina.
In most cases your content will be statically generated at build time, but on occasion you might need to use SSR in your Tina-powered app. It could be a page that isn’t powered by Tina but you are using our graphQL layer to power your whole application.
const query = `post(relativePath: "example.md") {titlebody}`export async function getServerSideProps(context) {let dataconst res = await fetch('https://content.tinajs.io/<VersionOfTina>/content/<CLIENT_ID>/github/<BRANCH>',{method: 'POST',body: JSON.stringify({ query, variables }),headers: {'X-API-KEY': 'API_KEY','Content-Type': 'application/json',},})const jsonData = await res.json()data = jsonData.datareturn {props: {data,query,variables,}, // will be passed to the page component as props}}
Every time a user returns to this page, they will receive a freshly served page with the latest content from Tina.
Client side rendering can be a great way to keep content on the page up to date, every-time someone visits a page. Tina content can be retrieved using your favorite http client such as fetch or axios.
import { useState, useEffect } from 'react'import { useTina } from 'tinacms/dist/edit-state'// This is a query you want.const query = `query ContentQuery($relativePath: String!) {<collection.name>(relativePath: $relativePath) {bodytitle}}`// Variables used in the GraphQL query;const variables = {relativePath: 'HelloWorld.md',}function BlogPostPage() {const [initalData, setData] = useState(null)const [isLoading, setLoading] = useState(false)useEffect(() => {setLoading(true)fetch('https://content.tinajs.io/<VersionOfTina>/content/<ClientId>/github/<Branch>',{method: 'POST',body: JSON.stringify({ query, variables }),headers: {'X-API-KEY': '<ReadOnlyToken>','Content-Type': 'application/json',},}).then((res) => res.json()).then((data) => {console.log({ data })setData(data)setLoading(false)}).catch((e) => {console.error(e)})}, [query, JSON.stringify(variables)])const { data } = useTina({ query, variables, data: initalData })if (isLoading) return <p>Loading...</p>if (!data) return <p>No data</p>return <div>{JSON.stringify(data)}</div>}export default BlogPostPage
As you can see for this example we are using useEffect to fetch the data from Tina using our read-only token. The URL you see is powered by your clientId
and GitHub branch of choice. We then set the data to use useTina
and present the data through the UI.
Up until now most Tina users use fallback: blocking
for creating new pages with Tina.
This comes with issues:
With Read-only tokens this has a lot less developer friction and a better user experience, we can break the getStaticsProps code into three paths.
import { staticRequest } from 'tinacms'const query = `query PostQuery($relativePath: String!) {post(relativePath: $relativePath) {titlebody}}`export const getStaticProps = async (ctx) => {const variables = {relativePath: ctx.params.slug + '.md',}let datalet errorerror = falsetry {// use the local client at build timedata = await staticRequest({query,variables,})} catch (error) {// swallow errors related to document creation}// if there isn't data set the error flagif (!data) {error = true}if (error) {// use read-only tokens to get live dataconst res = await fetch('https://content.tinajs.io/<VersionOfTina>/content/<CLIENT_ID>/github/<BRANCH>',{method: 'POST',body: JSON.stringify({ query, variables }),headers: {'X-API-KEY': 'API_KEY','Content-Type': 'application/json',},})const jsonData = await res.json()data = jsonData.data// if there is no data set the notFound true (This returns 404if (!data) {return {notFound: true,}}}return {props: {data,query,variables,},}}
The code above does a lot of different things, so let us break it down into the sections stated previously:
notFound: true
(this is a special flag for Next.js). This flag will return your 404 error page as well as 404
in the status code.The best way to keep up with Tina is to subscribe to our newsletter, we send out updates every two weeks. Updates include new features, what we have been working on, blog posts you may have missed, and so much more!
You can subscribe by following this link and entering your email: https://tina.io/community/
Tina has a community Discord that is full of Jamstack lovers and Tina enthusiasts. When you join you will find a place:
Our Twitter account (@tinacms) announces the latest features, improvements, and sneak peeks to Tina. We would also be psyched if you tagged us in projects you have built.
© TinaCMS 2019–2024