Socket
Socket
Sign inDemoInstall

@arcteryx/sanity-content

Package Overview
Dependencies
32
Maintainers
3
Versions
144
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

    @arcteryx/sanity-content

This package exports methods to fetch content from our Sanity CMS. It is heavily influenced by this article recommended to us from Sanity tech contact. [Type-safe GROQ Queries for Sanity Data with Zod](https://www.simeongriggs.dev/type-safe-groq-queries-f


Version published
Weekly downloads
331
decreased by-24.08%
Maintainers
3
Created
Weekly downloads
 

Readme

Source

@arcteryx/sanity-content

This package exports methods to fetch content from our Sanity CMS. It is heavily influenced by this article recommended to us from Sanity tech contact. Type-safe GROQ Queries for Sanity Data with Zod.

Usage

import { fetchPage } from @arcteryx/sanity-content
import { createClient } from "@sanity/client";

const sanityClient = createClient({
  projectId: "<project id>",
  dataset: "development",
  useCdn: false, // set to `false` to bypass the edge cache
  apiVersion: '2023-05-03',
})

// The `page` object is type-safe. You can expect page to have the typed attributes defined in `pageSchema` of `fragments/page.ts`.
// For example: `page.region`, `page.breadcrumbs.items`, etc.
const page = await fetchPage(sanityClient, {
  market: "outdoor", // "sale" for outlet
  country: "ca",
  language: "en",
  slug: "help/womens/sizing",
});
`

// If you need to define an `interface` for the type of the `page` object, you can do something like this:
interface Props {
  page: Awaited<ReturnType<typeof fetchPage>>,
}

const MyApp = ({ page }: Props) => {
  const breadcrumbs = page.breadcrumbs?.items; // type-safe
}

Folder Structure

This repo is organized into 2 folders, documents and fragments.

documents

Files in this folder will export "fetch" functions that use a SanityClient object to retrieve documents from the content lake. Their function signature should generally be as follows:

// DocumentTypeParams would be the interface to describe the expected params that should be passed to the client to satisfy the groq query.
function fetchPage (client: SanityClient, params: DocumentTypeParams)

fragments

Files in this folder should be small and concise. They're the building blocks used to compose an entire groq query. In general they correspond to the schema definitions created in the sanity-cms repo, but they don't need to be. For example, a schema could have the firstName and lastName attributes, but our groq fragment could choose to query fullName by having groq concatenate both attributes.

In these files we utilize zod to do two things: Define the output schema for our query, and make that schema type-safe.

These files must export 2 objects:

  1. The schema in the format of <fragmentName>Schema. For example: breadcrumbSchema. This object is the zod definition. And it in itself can be composed of other schema fragments.
  2. The query in the format of <fragmentName>Query. For example: breadcrumbsQuery. This is the partial groq query. And it in itself can be composed of other query fragments.

See breadcrumbs.ts for an example of composition.

Document Fragments

These fragments are used in the fetch functions defined within the documents folder. So they should also export 3 more items:

  1. The filter in the format of <fragement name>Filter. For example pageFilter. This is used in the groq query to filter down or search the specific document.
  2. The params in the format of <FragmentName>Params. For example PageParams. (Note: capital case). This is a type-safe interface that specifies the required params that will be passed to SanityClient to satisfy the groq query.
  3. The queryParams validator function. This is to help ensure the params used in sanityClient.fetch match the schema defined by the interface above. This function should usually always look like this: export const queryParams = (params: <FragmentName>Params) => params;. Then the "fetch" function defined in the documents folder should validate the params like so: sanityClient.fetch(query, queryParams(params)).

See fragments/page.ts and documents/page.ts for an example.

sectionSchema

To accept new types for sections field in sectionSchema, add the new type in baseSectionListSchema and not in the sections field itself. This is so that schemas using baseSectionListSchema like orTab will be able to accept this new section type.

GROQ Methods

Referencing Fields

When constructing a query, you are able to access it directly using the name of the value in a shorthand notation.

export const query = groq`{
  name,
  content
}`;

If you need to conduct a more indepth query or rename the key, you need to wrap in double quotes.

export const query = groq`{
  name,
  "contentArray":content
}`;

Coalesce

There are times when your query may not have any data. This will typically return null for your value. To ensure that you return a value, you can use the coalesce function. This is valuable when creating a schema using an array; if there are no items in the array, return an empty array rather than null.

export const query = groq`{
  name,
  "content": coalesce(content, [])
}`;

Optional Fields

There are times when you want the output of your data from Sanity to include optional fields. An example would be the inclusion of a component in certain scenarios. If the component is to be included in the view, you would add it in sanity and expect that key to be present in the response. If the component is not to be included, than that key will be omitted from the response.

Example:

export const query = groq`{
  name,
  content != null => {
   content
  }
}`;

In this case, if content exists in the query than it will be retreived and returned in the response.

const response = {name:'', content:{...}}

If there is no content in the query, then it will be omitted from the response

const response = {name:'',}

Another option would be to use the coalesce function and return a default value

export const query = groq`{
  name,
  "content" : coalesce(content, {})
}`;

If there is no content in the query, then the fallback will be used. This method will also require a more complicated Zod schema since you will need to account for the nested undefined values.

const response = {name:'', content:{}}

However, this approach will produce a type signature that will contain multiple undefined fields. This may require adapting in the front end.

Zod Methods

Strictly Defined a String

To ensure that a value coming from the Content Lake is strictly equal, you can use the z.literal method.

export const schema = z.object({
	type: z.literal("button"),
});

If this schema is called and the type does not equal "button", the validation will fail.

Type Clarity

When using Zod data types, it helps to distinguish Zod types against Javascript data types when it's used like this:

z.object()
z.array()
z.string()

But not this:

const { object, array, string } = z;

object()
array()
string()

Releases

This repo will automatically publish releases to @arcteryx/sanity-content on npm when there is a merge to main. We utilize the semantic-release package to handle this for us. It also depends on our commit messages following conventional commits.

FAQs

Last updated on 18 Apr 2024

Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc