November 17, 2020

"Hello World" from scratch

By Katie Kodes

Whenever I encounter a complex toolkit like Sanity, I find myself too curious about how it "really works" to rely on prebuilt starters -- and too impatient to do anything wild and crazy like read the docs.

My favorite way to learn is to strip a tool down to its minimum viable build. How little code can I write while still doing anything?

Join me to:

  1. Build a "Hello World" Sanity CMS content model from scratch
  2. Add data with Sanity Studio, and
  3. Validate the data by querying it with Postman

Introduction

This guide's content model won't be fancy. It contains just one document type, called "page." ("Page" seems like a good name, since I use Sanity data to control the creation of web pages through a static website generator.)

Each "page"-typed document will have 3 fields:

  1. A string called "template," meant for indicating to a static site generator which layout configuration to use when building a web page from the document
  2. A string called "slug," meant for indicating to a static site generator what URL to give the web page it builds from the document
  3. A string called "message," meant for a static site generator to insert into the body of a web page it builds from the document

Sanity Studio will take care of presenting these 3 fields as a user-friendly data entry form.

On the back end, when we're done, we should be able to query the Sanity API with Postman and see JSON-formatted data that looks something like this:

{
  "message": "Hello World",
  "slug": { "_type": "slug", "current": "hello-world" },
  "template": "basic"
}

Prerequisites

  1. Sign up for a Sanity.io account at https://accounts.sanity.io/signup/
  2. Log into it at https://manage.sanity.io/
  3. Install the Node.js JavaScript execution environment on your computer, being sure to include the npm package manager. Make sure that your "path" environment variable for your operating system ends up configured so that when you execute node -v or npm -v from a command line interface, you receive output indicating the installed version of the software.
  4. Install the Sanity CLI command-line tool using the npm command-line tool. Make sure that your "path" environment variable for your operating system ends up configured so that when you execute sanity -v, you get a message back saying what version of @sanity/cli you're on.
  5. If this was your first time installing command-line tools on your computer, pat yourself on the back. If it took you a week to get this far, you're not alone!
  6. Install a tool on your computer capable of helping you make HTTP requests over the internet, such as Postman, cURL, or a general-purpose programming language with a request-making library. This guide will walk you through using Postman to test Sanity, but you may want to learn how to use your tool to make a basic `GET`-typed HTTP request with customized headers. It's a skill you'll need often for web development and data integration.

Navigate the command line to an appropriate folder

I decided I wanted to work out of a folder on my hard drive called C:\example\mysite_sanity.

After I opened Windows's command prompt (**windows-key** + **cmd** + **enter**), I typed the following at the prompt and pressed **enter** to execute it:

cd C:\example\mysite_sanity

Log the Sanity CLI into Sanity.io

Once there, I executed the following command:

sanity login

I tapped the **down arrow** on my keyboard until "E-mail / password" was highlighted in turquoise with a > to the left of it and hit **enter**.

Because my computer's default web browser was already logged into Sanity.io, I saw a confirmation screen open within my web browser telling me that I could close it.

If I hadn't been logged in, I would have been prompted to log into my sanity.io account.

Initialize a new Sanity project

In my command line interface, I executed the following command:

sanity init

I used my keyboard's **up** and **down** arrow keys to highlight "Create new project" in turquoise (which also put a > to the left of it) and hit **enter**.

I **backspaced** out the auto-suggested "(My Sanity Project)" and instead typed a name I preferred for my project: "Minimum Viable Build."

In the spirit of exploring everything, I overrode the default dataset configuration by typing n when asked. Instead, I decided to create a Sanity "dataset" called "top_secret_stuff."

I arrowed up and down until "Private (authenticated requests only)" was highlighted turquoise and had a > to the left of it, then hit **enter**.

"Hello World" isn't really going to be a big secret -- it's going right on my web site -- but I wanted to learn more about private data sets in Sanity.

Since I had already navigated to C:\example\mysite_sanity before starting all this, I didn't have to backspace out and re-type the directory into which I wanted to store my computer's local copy of files for this "Sanity project" files in. I simply hit **enter** when Sanity suggested that I install my project there.

To start with as few files as Sanity allows, I arrowed down until I had selected "Clean project with no predefined schemas" and hit **enter**.

Sanity ran for some time while initializing my project. It took a moment, because it was downloading code from the internet.

Look around the project

When the initialization process was done, Sanity prompted me, "Success! Now what?"

Executing this command showed "Minimum Viable Build" as one of my projects:

sanity projects list

Visiting https://manage.sanity.io in my web browser, I could see a new project listed called "Minimum Viable Build." Success indeed!

Clicking on that project, I was taken to a management panel for the project at https://manage.sanity.io/projects/my-project-id/, with my-project-id being an actual project ID, of course.

Toward the top of the screen, I could see that there wasn't yet a link next to "Studio," the user interface for managing data stored in Sanity's cloud. I still needed to set one up.

Clicking on the "Datasets" tab, however, there was indeed one called top_secret_stuff.

Clicking on the "Settings" tab, I could change this project's color theme in the management to purple, just for fun. Changing the color theme can help keep one project straight from another in Sanity's management panel.

Add an API key to the project

Clicking the "API" navigation item at left in the "Settings" tab, I found a page where I could click "Add New Token."

Having a token allows computer systems like static site builders to read data out of my private Sanity dataset.

I called mine "Read-Only Token," set its rights to "Read", and clicked "Add New Token."

When the token popped up, I copied it to my password manager, because I can't retrieve it later from Sanity. That said, I can easily delete this token and start over with a new one.

Edit schema files on the hard drive

On my own hard drive at C:\example\mysite_sanity, I could see that Sanity installed a bunch of files.

Honestly, I only cared about the ones in the "schemas" folder, although there are other files and folders that it's useful to keep committed to source control.

File: schemas/schema.js

The file "schema.js" in the "schemas" folder was the master file defining my Sanity schema.

It allowed me to tell Sanity about all (1) of the other "schema definition" files I was about to create.

When Sanity installed it, its contents looked something like this:

// First, we must import the schema creator
import createSchema from 'part:@sanity/base/schema-creator'

// Then import schema types from any plugins that might expose them
import schemaTypes from 'all:part:@sanity/base/schema-type'

// Then we give our schema to the builder and provide the result to Sanity
export default createSchema({
  // We name our schema
  name: 'default',
  // Then proceed to concatenate our document type
  // to the ones provided by any plugins that are installed
  types: schemaTypes.concat([
  /* Your types here! */
  ])
})

I updated it to say this instead:

// First, we must import the schema creator
import createSchema from 'part:@sanity/base/schema-creator';

// Then import schema types from any plugins that might expose them
import schemaTypes from 'all:part:@sanity/base/schema-type';

// We import object and document schemas
import page from './page';

// Then we give our schema to the builder and provide the result to Sanity
export default createSchema({
  // We name our schema
  name: 'default',
  // Then proceed to concatenate our document type
  // to the ones provided by any plugins that are installed
  types: schemaTypes.concat([
    // The following are document types which will appear
    // in the studio.
    page,
    // When added to this list, object types can be used as
    // { type: 'typename' } in other document schemas
  ]),
});

File: schemas/page.js

I promised the "schema.js" file a "page" schema, so I also added a new file to the "schemas" folder and called it "page.js."

Its contents were as follows:

export default {
    name: 'page',
    title: 'Page',
    type: 'document',
    icon,
    fields: [
      {
        name: 'template',
        title: 'Template',
        type: 'string',
      },
      {
        name: 'slug',
        title: 'Slug',
        type: 'slug',
        description:
          'If not happy with what the system generated, you can hand-edit it here',
      },
      {
        name: 'message',
        title: 'Message',
        type: 'string',
      },
    ],
    preview: {
      select: {
        slug: 'slug',
        template: 'template',
        message: 'message',
      },
      prepare(selection) {
        const { slug, template, message } = selection;
        return {
          title: `${slug.current} [${template}]`,
          subtitle: message,
        };
      },
    },
  };

As a value for the "fields" property of the JavaScript object being exported from "page.js," I included an array of JavaScript objects that defined one "field" apiece for my "page" data model, naming them "template," "slug," and "message."

I used the "preview" property of the JavaScript object being exported from "page.js," defining "title" and "subtitle" patterns that would make it easier to distinguish items in a list of "page"-typed documents within Sanity Studio.

Protip

2 tips for mastering Sanity schema files

  1. See Sanity's official schema documentation.
  2. Make a 2nd Sanity project on your hard drive.
    1. Instead of executing sanity init with a setting of "Clean project with no predefined schemas," repeat this tutorial with one of the sample options like "Movie project," "E-commerce," or "Blog."
    2. Look through the "schema files" Sanity generates for you.
    3. Try to reverse-engineer which "schema" code seems to correspond to which user experiences you see in Sanity Studio (which I'm about to teach you how to build).
      Personally, I learned a lot about inter-document relationships from "Movie Project."

Build, deploy, build again, deploy again

At this point, I had written all of the configuration files I needed for Sanity Studio. All that was left was to "build" it and to deploy it into Sanity's cloud.

Back in Windows's command prompt, I typed the following command and pressed **enter** to execute it:

sanity build

Once that command finished executing, I ran the following command:

sanity deploy

The command prompt asked me for a URL prefix at which I wanted to access my "Sanity Studio" for this project. (The prefix has to be unique among all projects owned by all people using Sanity).

I chose "hello-world-tutorial-0001."

Gotcha

On Windows, there's a bug in which the execution of this program hangs for over 5 minutes. Refreshing https://manage.sanity.io/projects/my-project-id/ showed https://hello-world-tutorial-0001.sanity.studio as my studio URL, but if I tried to visit it, I received a "Studio not found" error as JSON-formatted text.

I was able to work around this problem by aborting the deployment process with **Ctrl**+**Break**, typing Y, and hitting **enter** to stop the deployment process.

Running sanity build and sanity deploy again resulted in a successful deployment to Sanity's cloud in less than a minute.

Log into Sanity Studio

Visiting https://hello-world-tutorial-0001.sanity.studio from my project dashboard, I was prompted to log in.

I clicked "E-mail / password" and let the my web browser log me in (it had preserved my login state from being logged into the management console).

After logging in, I was taken to https://hello-world-tutorial-0001.sanity.studio/desk, where I saw a content type of "Page," just like I defined as the "title" property of the JavaScript object I exported from "page.js."

Wonderful!

Clicking on "Page," I saw a plus sign in the far right and clicked it.

I was prompted to type in a "template" (I typed "basic"), a "slug" (I typed "hello-world"), and a "message" (I typed "Hello World").

In the top center area of my screen, in the list of pages in my dataset, I saw "hello-world [basic]" in big text and "Hello World" in small text as a "preview" of this particular item, exactly as I'd specified in my schema configuration file.

Test basic API access

To confirm that my data was really available to the outside world, I opened the Postman application on my computer.

I set up an HTTP request with a method of GET, an endpoint of https://my-project-id.api.sanity.io/v1/data/export/top_secret_stuff, an Authorization header that started with the phrase Bearer and ended with the secret token I saved earlier, and a Content-Type header of `application/json`.

Then I hit "Send."

At bottom, I could see traces of the data I entered in Sanity's HTTP response body. Inspecting it, I could see familiar phrases like "basic," "hello-world," and "Hello World."

Success.

Set up GraphQL API access

If I planned to query Sanity data exclusively with Sanity's GROQ query language, I'd be done at this point.

However, if I were working with a GraphQL-oriented static site generator like Gatsby, I might also want Sanity to make my data available to the outside world in a way that accepts a queries written in the GraphQL standard.

If I build an HTTP request in Postman with a method of POST, an endpoint of https://my-project-id.api.sanity.io/v1/graphql/top_secret_stuff/default, an Authorization header that starts with the phrase Bearer and ends with the secret token I saved earlier, and a body in GraphQL syntax with a blank "GraphQL Variables" section but a "Query" section containing the following code...

{
  allPage {
    message
    template
    slug {
      current
    }
  }
}

...then unfortunately, I get the following HTTP response body from Sanity:

{
  "statusCode": 404
  "error": "Not Found",
  "message": "GraphQL schema not found for dataset/tag"
}

Gotcha

It turns out that I have to explicitly tell Sanity that I'd like to enable GraphQL-based queries. They aren't enabled by default.

I enable GraphQL-based queries by typing the following command into my computer's command prompt:

sanity graphql deploy --playground

Now when I re-run the same HTTP request that had previously failed, I receive the following response body:

{
  "data": {
    "allPage": [
      {
        "message": "Hello World",
        "template": "basic",
        "slug": {
          "current": "hello-world"
        }
      }
    ]
  }
}

Much better!

If you're not accustomed to writing GraphQL queries, visit https://your-project-id.api.sanity.io/v1/graphql/your-dataset-name/default, in a web browser while logged into Sanity. (Replace your-project-id and your-dataset-name, of course.)

This URL takes you to the "playground" you set up by adding the --playground flag to the Sanity CLI command you ran setting up GraphQL.

The playground includes an editor and a big "run button" where you can make all the typos you want as you learn GraphQL.

It also gives you some hints while you type.

Need to see screenshots?

Protip

I haven't had a chance to take screenshots of myself building this project from scratch, but here's a very similar tutorial with screenshots.

Build something great

Now that you've successfully built a "Hello World" data model in Sanity on your own, are you ready to make your dream content model?

Tinker and be sure to join us in #i-made-this in the Sanity Slack to show it off!