{"pageProps":{"error":null,"preview":false,"file":{"sha":"","fileRelativePath":"content/tina-cloud/docs/guides/setup-tina-cloud-client.md","data":{"frontmatter":{"title":"Setup Tina Cloud Client","id":"/tina-cloud/docs/guides/setup-tina-cloud-client","prev":"/tina-cloud/docs/guides/setup-tina-cloud-client","next":null},"excerpt":" Prerequisite This guide gets the client working on a fresh NextJS site but can also be used as a reference to add the client to an existing site. To start with a new site. you can create one with: or…","markdownBody":"\n## Prerequisite\n\nThis guide gets the client working on a fresh NextJS site but can also be used as a reference to add the client to an existing site.\n\nTo start with a new site. you can create one with:\n\n```bash\nnpx create-next-app\n```\n\nor\n\n```bash\nyarn create next-app\n```\n\nGoing forward `yarn` with be used for examples.\n\nName it whatever you like and `cd` in your new project.\n\n### Add Typescript\n\nFor this guide we will be using Typescript. Add it to the project with this:\n\n```bash\nyarn add --dev typescript @types/react @types/react-dom @types/node\n```\n\nThen replace `_app.js` with `_app.tsx` and `index.js` with `index.tsx`.\n\nRun:\n\n```bash\ntouch tsconfig.json\n```\n\nAnd finally run:\n\n```bash\nyarn dev\n```\n\nThat should generate the tsconfig.json content required.\n\n## Install the client package\n\nThis package provides you with:\n\n- A `Client` class (which you can use as a TinaCMS API Plugin), that takes care of all interaction with the GraphQL server.\n- A `useForm` hook, that you can use to hook into the Tina forms that let you edit your content.\n\n```bash\nyarn add tina-graphql-gateway\n```\n\n_Choose the latest version_\n\n## Install CLI package\n\nYou'll also likely want to install our CLI to help with development:\n\n```bash\nyarn add --dev tina-graphql-gateway-cli\n```\n\n_Choose the latest version_\n\nThis CLI performs a few functions:\n\n- Generates GraphQL queries (and optionally TypeScript types) based on your content's schema.\n- Auditing your content's schema and checking for errors.\n- Running a GraphQL server using the built-in filesystem adapter.\n\nFor full documentation of the CLI, see [here](/tina-cloud/docs/reference/tina-cli).\n\n## Implementation\n\nWe'll now show how to use this package in a NextJS site\n\nNote: This solution relies on a long-running server, and will not work with NextJS' serverless [build target](https://nextjs.org/docs/api-reference/next.config.js/build-target).\n\n### Create Example Content\n\nLet's start by creating a simple piece of content. Our goal will to be able to access and change this content through an auto-generated GraphQL API and Tina forms.\n\nFirst create a new folder at the root of the project called `content`, then a subfolder of `content` called `pages`, and in there create a markdown file called `index.md`.\n\nIn `content/pages/index.md`, add this:\n\n```md\n---\ntitle: A great sight\n---\n```\n\n### Configuration\n\nBefore we can define the schema of our content, we need set up some configuration. Create a `.tina` directory at the project root and then create the following files.\n\n**.tina/settings.yml**\n\n```yml\n---\nnew_page_extension: md\nauto_deploy: false\nadmin_path:\nwebhook_url:\nsections:\n - type: directory\n path: content/pages # replace this with the relative path to your content section\n label: Pages\n create: documents\n match: '*.md'\n new_doc_ext: md\n templates:\n - index # replace this with your template filename name\nupload_dir: public/uploads\npublic_path: '/uploads'\nfront_matter_path: ''\nuse_front_matter_path: false\nfile_template: ':filename:'\n```\n\nThese [files](/tina-cloud/docs/reference/config-files/index) will create a map to our content to content models. In the above settings file, we declare any markdown files in our project should be a \"index\" type (we'll define this index type next).\n\n### Define Content Schema\n\n[Templates](/tina-cloud/docs/reference/config-files/front-matter-templates) define the shape of different content models.\n\n**.tina/front_matter/templates/index.yml**\n\n```yml\n---\nlabel: Index\nhide_body: false\ndisplay_field: title\nfields:\n - name: title\n type: text\n config:\n required: false\n label: Title\npages:\n - content/pages/index.md # This keeps reference to all the pages using this template\n```\n\n## Sourcing your content\n\n### Store the project in a GitHub repo\n\n_If you're following this guide with an existing project, make sure it is stored in GitHub and .tina has been pushed up._\n\nIf you have created a new NextJS site, then please now go and create a repository in GitHub and push up the project to it.\n\n### Create a Tina Cloud \"app\"\n\nOnce you have you have a repository, it is time to create a Tina Cloud \"app\". For help doing so, you can checkout [this guide](/tina-cloud/docs/guides/setup-tina-cloud-app).\n\nOnce you have a Tina Cloud \"app\", add a `.env` file to the root of your project. It should look like this:\n\n```\nNEXT_PUBLIC_REALM_NAME=YOUR ORGANIZATION ID\nNEXT_PUBLIC_TINA_CLIENT_ID=YOUR APP'S CLIENT ID\n```\n\nFill out the environment variables with the values you've recieved by creating your app.\n\n#### Using the data within our Next.JS site\n\nFirst, install the TinaCMS dependencies:\n\n```bash\nyarn add tinacms styled-components\n```\n\nIn `_app.tsx`, add TinaCMS, register the `Client`, and wrap our main layout in the `TinaCloudProvider` to support authentication, like so:\n\n**\\_app.tsx**\n\n```tsx\nimport React from 'react'\nimport { TinaProvider, TinaCMS } from 'tinacms'\nimport { TinaCloudProvider } from 'tina-graphql-gateway'\nimport createClient from '../components/client'\n\nfunction App({ Component, pageProps }) {\n return (\n {\n const headers = new Headers()\n\n //TODO - the token should could as a param from onLogin\n headers.append('Authorization', 'Bearer ' + token)\n fetch('/api/preview', {\n method: 'POST',\n headers: headers,\n }).then(() => {\n window.location.href = '/'\n })\n return ''\n }}\n onLogout={() => {\n console.log('exit edit mode')\n }}\n >\n \n \n )\n}\n\nexport default function _App(props: any) {\n const cms = new TinaCMS({\n apis: {\n tina: createClient(true),\n },\n sidebar: props.pageProps.preview,\n enabled: props.pageProps.preview,\n })\n\n return (\n \n \n \n )\n}\n```\n\n`_app.tsx` imports `createClient` from `/components/client` which you don't have yet. So let's add that now.\n\nFirst create a folder at the project's root called `components` then create a file within it called `client.ts` and fill it with this:\n\n```tsx\nimport { Client, DEFAULT_LOCAL_TINA_GQL_SERVER_URL } from 'tina-graphql-gateway'\n\nconst createClient = (preview: boolean, getTokenFn?: () => string) =>\n new Client({\n realm: process.env.NEXT_PUBLIC_REALM_NAME || '',\n clientId: process.env.NEXT_PUBLIC_TINA_CLIENT_ID || '',\n redirectURI: 'http://localhost:3000',\n customAPI: DEFAULT_LOCAL_TINA_GQL_SERVER_URL,\n getTokenFn,\n tokenStorage: getTokenFn ? 'CUSTOM' : 'MEMORY',\n })\n\nexport default createClient\n```\n\nThis Next implementation relies on a backend function to save its auth details. Create a new file under `/pages/api/` called `preview.ts` and fill it in as such:\n\n```tsx\nimport Cookies from 'cookies'\n\nconst preview = (req: any, res: any) => {\n const token = (req.headers['authorization'] || '').split(' ')[1] || null\n\n res.setPreviewData({})\n\n const cookies = new Cookies(req, res)\n cookies.set('tinaio_token', token, {\n httpOnly: true,\n })\n\n res.end('Preview mode enabled')\n}\n\nexport default preview\n```\n\nThis file reqiures the `cookies` package, simply add it by running\n\n```bash\nyarn add cookies\n```\n\nThe last step is to add a way for the user to enter edit-mode. Let's create a `/login` page.\n\n**/pages/login.tsx**\n\n```tsx\nimport { useCMS } from 'tinacms'\n\nexport default function Login(props) {\n const cms = useCMS()\n\n return (\n <>\n \n {props.preview && (\n

\n You are logged in to Tina.io. Return to homepage\n

\n )}\n \n )\n}\n\nexport const getStaticProps = async ({ preview }) => {\n return {\n props: {\n preview: !!preview,\n },\n }\n}\n```\n\nAt this point, if you go to [/login](http://localhost:3000/login) you should now be able to login, for no good reason though because there's nothing yet to edit. Let's fix that next.\n\n## Editing your content\n\nLet's rejig `index.tsx` a bit:\n\n```tsx\nimport Head from 'next/head'\nimport styles from '../styles/Home.module.css'\nimport Cookies from 'cookies'\nimport createClient from '../components/client'\nimport { useForm, useTinaAuthRedirect } from 'tina-graphql-gateway'\nimport { DocumentUnion, Index as IndexResponse } from '../.tina/types'\n\nexport default function Home(props) {\n useTinaAuthRedirect()\n\n const data = props.preview\n ? useForm(props).data\n : props.document?.node.data || {}\n\n return (\n
\n \n Create Next App\n \n \n\n
\n

{data.title}

\n {props.preview &&

You are currently in edit mode.

}\n {!props.preview && (\n

\n To edit this site, go to login\n

\n )}\n
\n
\n )\n}\n\nexport async function getServerSideProps(props) {\n const relativePath = `index.md`\n const section = 'pages'\n\n const cookies = new Cookies(props.req, props.res)\n const authToken = cookies.get('tinaio_token')\n\n const getTokenFn = () => authToken || ''\n\n const client = createClient(props.preview, getTokenFn)\n\n const content = await client.getContentForSection({\n relativePath: relativePath,\n section: section,\n })\n\n return {\n props: {\n ...content,\n relativePath,\n section,\n preview: !!props.preview,\n },\n }\n}\n```\n\nUsing `getServerSideProps` and the tina-graphql-gateway client we are fetching the content for this page. Then in the page component we are conditionally registering a form and recieving data that we are displaying on the page.\n\nWe are missing something here though, a `.tina/types` file. We'll generate one very simply with the client CLI, just run:\n\n```bash\nyarn tina-gql schema:types\n```\n\nAlso update your `package.json` scripts to also run the graphql server like such:\n\n```json\n\"scripts\": {\n \"next-dev\": \"next dev\",\n \"dev\": \"yarn tina-gql server:start -c \\\"yarn next-dev\\\"\",\n \"next-build\": \"next build\",\n \"start\": \"next start\",\n \"build\": \"yarn tina-gql server:start -c \\\"yarn next-build\\\"\"\n }\n```\n\nAnd run the project again.\n\nYou should be able to edit the title using the sidebar and when you save it it'll update `content/pages/index.md` on the local filesytem.\n\n### Editing content on GitHub\n\nTo make your changes affect your repository on GitHub instead of the local filesystem, only one line of code needs to be changed.\n\nChange `components/client.ts` to:\n\n```tsx\nimport { Client, DEFAULT_LOCAL_TINA_GQL_SERVER_URL } from 'tina-graphql-gateway'\n\nconst createClient = (preview: boolean, getTokenFn?: () => string) =>\n new Client({\n realm: process.env.NEXT_PUBLIC_REALM_NAME || '',\n clientId: process.env.NEXT_PUBLIC_TINA_CLIENT_ID || '',\n redirectURI: 'http://localhost:3000',\n customAPI: preview ? undefined : DEFAULT_LOCAL_TINA_GQL_SERVER_URL,\n getTokenFn,\n tokenStorage: getTokenFn ? 'CUSTOM' : 'MEMORY',\n })\n\nexport default createClient\n```\n\nLine 8 changed from `customAPI: DEFAULT_LOCAL_TINA_GQL_SERVER_URL,` to `customAPI: preview ? undefined : DEFAULT_LOCAL_TINA_GQL_SERVER_URL,`.\n\nNow in edit mode, changes will be saved directly to GitHub.\n"}},"tocItems":" - [Prerequisite](#prerequisite)\n - [Add Typescript](#add-typescript)\n - [Install the client package](#install-the-client-package)\n - [Install CLI package](#install-cli-package)\n - [Implementation](#implementation)\n - [Create Example Content](#create-example-content)\n - [Configuration](#configuration)\n - [Define Content Schema](#define-content-schema)\n - [Sourcing your content](#sourcing-your-content)\n - [Store the project in a GitHub repo](#store-the-project-in-a-github-repo)\n - [Create a Tina Cloud \"app\"](#create-a-tina-cloud-app)\n - [Using the data within our Next.JS site](#using-the-data-within-our-nextjs-site)\n - [Editing your content](#editing-your-content)\n - [Editing content on GitHub](#editing-content-on-github)\n","docsNav":[{"title":"Introduction","id":"introduction","items":[{"id":"/tina-cloud/docs/index","slug":"/tina-cloud/docs/index","title":"What is Tina Cloud?"},{"id":"/tina-cloud/docs/introduction/architecting-with-tina-cloud","slug":"/tina-cloud/docs/introduction/architecting-with-tina-cloud","title":"Architecting with Tina Cloud"},{"id":"/tina-cloud/docs/introduction/tina-cloud-tina-cms","slug":"/tina-cloud/docs/introduction/tina-cloud-tina-cms","title":"How does Tina Cloud fit with TinaCMS?"}]},{"title":"Concepts","id":"concepts","items":[{"id":"/tina-cloud/docs/concepts/organizations","slug":"/tina-cloud/docs/concepts/organizations","title":"Organizations"},{"id":"/tina-cloud/docs/concepts/apps","slug":"/tina-cloud/docs/concepts/apps","title":"Apps"},{"id":"/tina-cloud/docs/concepts/content-modeling","slug":"/tina-cloud/docs/concepts/content-modeling","title":"Content Modeling"},{"id":"/tina-cloud/docs/concepts/git-backed-content","slug":"/tina-cloud/docs/concepts/git-backed-content","title":"Git-backed Content"}]},{"title":"Guides","id":"guides","items":[{"id":"/tina-cloud/docs/guides/setup-tina-cloud-app","slug":"/tina-cloud/docs/guides/setup-tina-cloud-app","title":"Setup an app with Tina Cloud"},{"id":"/tina-cloud/docs/guides/setup-tina-cloud-client","slug":"/tina-cloud/docs/guides/setup-tina-cloud-client","title":"Setup a Tina Cloud Client"}]},{"title":"Reference","id":"api-reference","items":[{"id":"/tina-cloud/docs/reference/config-files/index","slug":"/tina-cloud/docs/reference/config-files/index","title":"Config Files","items":[{"id":"/tina-cloud/docs/reference/config-files/settings","slug":"/tina-cloud/docs/reference/config-files/settings","title":"Settings"},{"id":"/tina-cloud/docs/reference/config-files/front-matter-templates","slug":"/tina-cloud/docs/reference/config-files/front-matter-templates","title":"Front Matter Templates"},{"id":"/tina-cloud/docs/reference/config-files/schema","slug":"/tina-cloud/docs/reference/config-files/schema","title":"Schema"}]},{"id":"/tina-cloud/docs/reference/content-api","slug":"/tina-cloud/docs/reference/content-api","title":"Content API","items":[{"id":"/tina-cloud/docs/reference/tina-cloud-client","slug":"/tina-cloud/docs/reference/tina-cloud-client","title":"Tina Cloud Client"},{"id":"/tina-cloud/docs/reference/graphql","slug":"/tina-cloud/docs/reference/graphql","title":"GraphQL Gateway"},{"id":"/tina-cloud/docs/reference/packages","slug":"/tina-cloud/docs/reference/packages","title":"Packages"}]},{"id":"/tina-cloud/docs/reference/tina-cli","slug":"/tina-cloud/docs/reference/tina-cli","title":"Tina CLI","items":[{"id":"/tina-cloud/docs/reference/tina-cli-commands","slug":"/tina-cloud/docs/reference/tina-cli-commands","title":"Commands"}]},{"id":"/tina-cloud/docs/reference/faq","slug":"/tina-cloud/docs/reference/faq","title":"Frequently Asked Questions"}]}],"nextPage":{"slug":null,"title":null},"prevPage":{"slug":"/tina-cloud/docs/guides/setup-tina-cloud-client","title":"Setup Tina Cloud Client"}},"__N_SSG":true}