Skip to main content

Migrating from v0.x to v1.x

Migrating from v0.x is straightforward, since there are only a few API changes. Follow this guide to upgrade to the latest version.

Configuration

Obviously, you'll start by installing the latest version:

npm install --save groqd@latest

Create a groqd-client.ts file

The root q object is no longer exported by GroqD. Instead, you'll need to create it yourself:

Create a file ./groqd-client.ts with these contents:

import { createGroqBuilder } from 'groqd';
type SchemaConfig = any; // TODO: use generated Sanity Schema types
export const q = createGroqBuilder<SchemaConfig>();

By creating the root q this way, we'll be able to bind it to our SchemaConfig. We'll bind it to any for now, so our q will be schema-unaware -- same as v0.x.
Later, take a look at the Configuration docs to see how to generate a strongly-typed schema from your Sanity configuration.

Update all imports

Update all q imports to point to this new ./groqd-client file:

Example

Before

import { q } from 'groqd';

After

import { q } from "./groqd-client";

Migrate from the q("...") method

With v0.x, the root q was a function that allowed any Groq string to be passed.

With v1.x, all queries must be chained, using these type-safe methods.

Use q.star instead of q("*")

Use q.field("name") instead of q("name")

Use q.raw("....") instead of q("....")

The q.raw function should be used for more complex expressions.

Example

Before

q("*").grab({
imageUrl: q("imageUrl", q.string()),
itemsCount: q("count(items[])", q.number()),
});

After

q.star.grab({
imageUrl: q.field("imageUrl", q.string()),
itemsCount: q.raw("count(items[])", q.number()),
})

GroqD does not have strongly-typed helpers for all GROQ expressions, so the q.raw method can be used for complex expressions.

Extra: migrate from deprecated methods

The grab, grabOne, grab$, and grabOne$ methods still exist, but have been deprecated, and should be replaced with the project and field methods.

Sanity's documentation uses the word "projection" to refer to grabbing specific fields, so we have renamed the grab method to project (pronounced pruh-JEKT, if that helps).
Sanity also uses the phrase "naked projection" to refer to grabbing a single field, but to keep things terse, we've renamed grabOne to field. So we recommend migrating from grab to project, and from grabOne to field.

Use project instead of grab

The API is nearly identical, so simply change .grab to .project.

One caveat: grab supports a 2nd argument for "conditional projections". This is now handled by the q.conditional(...) utility. Please see the Conditionals documentation for more information.

Use field instead of grabOne

The API is identical, so simply change .grabOne to .field.

Alternatives to grab$ and grabOne$

Regarding grab$ and grabOne$, these 2 variants were needed to improve compatibility with Zod's .default(...) utility.

This feature has been dropped, in favor of using the new q.default utility.

Example

Before

q.grab$({
title: q.string().default("DEFAULT"),
imageUrls: q("images[]").deref().grabOne("url"),
})

After

q.project({ 
title: q.default(q.string(), "DEFAULT")),
imageUrls: q.field("images[]").deref().field("url"),
})

Extra: add a Strongly Typed Schema

With v0.x, we use Zod to define the shape of our queries, and validate this shape at runtime.

With v1.x, by adding a strongly-typed Sanity schema, we can validate our queries at compile-time too. This makes our queries:

  • Easier to write (provides auto-complete)
  • Safer to write (all commands are type-checked, all fields are verified)
  • Faster to execute (because runtime validation can be skipped)

For example, in a projection, we can skip runtime validation by simply using true instead of a validation method like q.string(). For example:

const productsQuery = q.star
.filterByType("product")
.project({
name: true, // 👈 'true' will bypass runtime validation
price: true, // 👈 and we still get strong result types from our schema
slug: "slug.current", // 👈 a naked projection string works too!
});

Since q is strongly-typed to our Sanity schema, it knows the types of the product's name, price, and slug.current, so it outputs a strongly-typed result. And assuming we trust our Sanity schema, we can skip the overhead of runtime checks.