Skip to content

Commit

Permalink
GroqD 1.0: Improved docs (#313)
Browse files Browse the repository at this point in the history
* chore: renamed `groqd-1` folder to just `groqd`

* chore: renamed `groq-builder` to `groqd` in docs

* chore: created shorter `createGroqBuilder` alias

* docs: expanded examples to include all necessary code

---------

Co-authored-by: scottrippey <[email protected]>
  • Loading branch information
scottrippey and scottrippey authored Jan 3, 2025
1 parent d38deee commit 7ae5b61
Show file tree
Hide file tree
Showing 12 changed files with 72 additions and 46 deletions.
4 changes: 3 additions & 1 deletion packages/groqd/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# groq-builder
# groqd

## 1.0.0

Expand All @@ -17,6 +17,8 @@

This release does include breaking API changes. Please see the [migration docs](https://commerce.nearform.com/open-source/groqd/docs/migration) for more information.

# groq-builder

## 0.10.1

### Patch Changes
Expand Down
21 changes: 15 additions & 6 deletions packages/groqd/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[![GROQD](https://oss.nearform.com/api/banner?badge=groqd&bg=c99f46)](https://commerce.nearform.com/open-source/groqd)
[![GroqD](https://oss.nearform.com/api/banner?badge=groqd&bg=c99f46)](https://commerce.nearform.com/open-source/groqd)

GroqD is a GROQ query builder, designed to give the _**best GROQ developer experience**_ possible.

Expand All @@ -18,21 +18,30 @@ It gives you the **flexibility** of GROQ, the **runtime safety** of [Zod](https:
Read the official documentation at https://commerce.nearform.com/open-source/groqd/docs/

## Usage Example
`GROQD` uses a strongly-typed chaining syntax to build queries:
`GroqD` uses a strongly-typed chaining syntax to build queries:

```ts
import { q } from "./groqd-client";
import { createGroqBuilder } from 'groqd';
// 👇 Import Sanity types, generated by your Sanity project. See "Configuration" docs for more details.
import type * as SanityTypes from "./sanity.types.ts";

// 👇 Create a strongly-typed query builder:
const q = createGroqBuilder<{
schemaTypes: SanityTypes.AllSanitySchemaTypes
referenceSymbol: typeof SanityTypes.internalGroqTypeReferenceTo;
}>();

// ✨ Write strongly-typed queries, with auto-complete and runtime safety!
const productsQuery = (
q.star
.filterByType("products")
.order("price desc")
.slice(0, 10)
.project(product => ({
.project(sub => ({
name: q.string(),
price: q.number(),
slug: product.field("slug.current", q.string()),
imageUrls: product.field("images[]").deref().field("url", q.string())
slug: sub.field("slug.current", q.string()),
imageUrls: sub.field("images[]").deref().field("url", q.string())
}))
);
```
Expand Down
8 changes: 4 additions & 4 deletions packages/groqd/src/commands/project.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -667,7 +667,7 @@ describe("project (object projections)", () => {
price: true,
})
).toThrowErrorMatchingInlineSnapshot(
`[TypeError: [groq-builder] Because 'validationRequired' is enabled, every field must have validation (like \`q.string()\`), but the following fields are missing it: "price"]`
`[TypeError: [groqd] Because 'validationRequired' is enabled, every field must have validation (like \`q.string()\`), but the following fields are missing it: "price"]`
);
});
it("should throw if a projection uses a naked projection", () => {
Expand All @@ -676,7 +676,7 @@ describe("project (object projections)", () => {
price: "price",
})
).toThrowErrorMatchingInlineSnapshot(
`[TypeError: [groq-builder] Because 'validationRequired' is enabled, every field must have validation (like \`q.string()\`), but the following fields are missing it: "price"]`
`[TypeError: [groqd] Because 'validationRequired' is enabled, every field must have validation (like \`q.string()\`), but the following fields are missing it: "price"]`
);
});
it("should throw if a nested projection is missing a parser", () => {
Expand All @@ -685,7 +685,7 @@ describe("project (object projections)", () => {
nested: qV.field("price"),
}))
).toThrowErrorMatchingInlineSnapshot(
`[TypeError: [groq-builder] Because 'validationRequired' is enabled, every field must have validation (like \`q.string()\`), but the following fields are missing it: "nested"]`
`[TypeError: [groqd] Because 'validationRequired' is enabled, every field must have validation (like \`q.string()\`), but the following fields are missing it: "nested"]`
);
});
it("should throw when using ellipsis operator ...", () => {
Expand All @@ -694,7 +694,7 @@ describe("project (object projections)", () => {
"...": true,
})
).toThrowErrorMatchingInlineSnapshot(
`[TypeError: [groq-builder] Because 'validationRequired' is enabled, every field must have validation (like \`q.string()\`), but the following fields are missing it: "..."]`
`[TypeError: [groqd] Because 'validationRequired' is enabled, every field must have validation (like \`q.string()\`), but the following fields are missing it: "..."]`
);
});
it("should work just fine when validation is provided", () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/groqd/src/commands/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ GroqBuilder.implement({
const invalidFields = fields.filter((f) => !f.parser);
if (invalidFields.length) {
throw new TypeError(
"[groq-builder] Because 'validationRequired' is enabled, " +
"[groqd] Because 'validationRequired' is enabled, " +
"every field must have validation (like `q.string()`), " +
"but the following fields are missing it: " +
`${invalidFields.map((f) => `"${f.key}"`)}`
Expand Down
11 changes: 11 additions & 0 deletions packages/groqd/src/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { describe, expect, it } from "vitest";

import * as root from "./index";

describe("root exports", () => {
it("should have proper exports", () => {
expect(typeof root.createGroqBuilderWithZod).toBe("function");
expect(typeof root.createGroqBuilderLite).toBe("function");
expect(typeof root.createGroqBuilder).toBe("function");
});
});
6 changes: 4 additions & 2 deletions packages/groqd/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export * from "./validation/validation-errors";
* see `createGroqBuilderWithZod` for more information.
*
* @example
* import { createGroqBuilderLite, ExtractDocumentTypes } from 'groq-builder';
* import { createGroqBuilderLite, ExtractDocumentTypes } from 'groqd';
* import { AllSanitySchemaTypes, internalGroqTypeReferenceTo } from "./sanity.types.ts";
*
* type SchemaConfig = {
Expand Down Expand Up @@ -54,7 +54,7 @@ export function createGroqBuilderLite<TRootConfig extends QueryConfig>(
* use `createGroqBuilderLite` instead.
*
* @example
* import { createGroqBuilderWithZod, ExtractDocumentTypes } from 'groq-builder';
* import { createGroqBuilderWithZod, ExtractDocumentTypes } from 'groqd';
* import { AllSanitySchemaTypes, internalGroqTypeReferenceTo } from "./sanity.types.ts";
*
* type SchemaConfig = {
Expand All @@ -71,3 +71,5 @@ export function createGroqBuilderWithZod<TRootConfig extends QueryConfig>(

export type GroqBuilderWithZod<TRootConfig extends QueryConfig> = ZodMethods &
GroqBuilder<RootResult, TRootConfig>;

export const createGroqBuilder = createGroqBuilderWithZod;
16 changes: 8 additions & 8 deletions website/docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ sidebar_position: 14

## Configure the GroqBuilder

In your project, create a file called `groqd-client.ts`:
In your project, we recommend creating a file called `groqd-client.ts`:

```ts
import { createClient } from "@sanity/client";
import { createGroqBuilderWithZod, makeSafeQueryRunner } from 'groq-builder';
import { createGroqBuilder, makeSafeQueryRunner } from 'groqd';

// Import the Sanity Schema types from this generated file:
import { AllSanitySchemaTypes, internalGroqTypeReferenceTo } from "./sanity.types.ts";
// 👇 Import Sanity types, generated by your Sanity project.
import type * as SanityTypes from "./sanity.types.ts";

const sanityClient = createClient({
/* ✨ your sanity config goes here */
Expand All @@ -24,10 +24,10 @@ export const runQuery = makeSafeQueryRunner((query) => sanityClient.fetch(query)

// 👇 Create a type-safe query builder
type SchemaConfig = {
schemaTypes: AllSanitySchemaTypes
referenceSymbol: typeof internalGroqTypeReferenceTo;
schemaTypes: SanityTypes.AllSanitySchemaTypes
referenceSymbol: typeof SanityTypes.internalGroqTypeReferenceTo;
};
export const q = createGroqBuilderWithZod<SchemaConfig>({});
export const q = createGroqBuilder<SchemaConfig>({});
```

## Generating the types from your Sanity Schema
Expand All @@ -49,5 +49,5 @@ If you don't want to, or can't, provide a schema, you can use `any` instead. You
We highly recommend using the `validationRequired: true` option for this scenario, which forces runtime type checks.

```ts
export const q = createGroqBuilderWithZod<any>({ validationRequired: true });
export const q = createGroqBuilder<any>({ validationRequired: true });
```
4 changes: 2 additions & 2 deletions website/docs/migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ The root `q` object is no longer exported by GroqD. Instead, you'll need to cre

Create a file `./groqd-client.ts` with these contents:
```ts
import { createGroqBuilderWithZod } from 'groqd';
import { createGroqBuilder } from 'groqd';
type SchemaConfig = any; // TODO: use generated Sanity Schema types
export const q = createGroqBuilderWithZod<SchemaConfig>();
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`.
Expand Down
26 changes: 17 additions & 9 deletions website/docs/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ slug: /

# Overview

[![GROQD](https://oss.nearform.com/api/banner?badge=groqd&bg=c99f46)](https://commerce.nearform.com/open-source/groqd)
[![GroqD](https://oss.nearform.com/api/banner?badge=groqd&bg=c99f46)](https://commerce.nearform.com/open-source/groqd)

GroqD is a GROQ query builder, designed to give the _**best GROQ developer experience**_ possible.

Expand All @@ -22,21 +22,30 @@ It gives you the **flexibility** of GROQ, the **runtime safety** of [Zod](https:
</details>

## Usage Example
`GROQD` uses a strongly-typed chaining syntax to build queries:
`GroqD` uses a strongly-typed chaining syntax to build queries:

```ts
import { q } from "./groqd-client";
import { createGroqBuilder } from 'groqd';
// 👇 Import Sanity types, generated by your Sanity project. See "Configuration" docs for more details.
import type * as SanityTypes from "./sanity.types.ts";

// 👇 Create a strongly-typed query builder:
const q = createGroqBuilder<{
schemaTypes: SanityTypes.AllSanitySchemaTypes
referenceSymbol: typeof SanityTypes.internalGroqTypeReferenceTo;
}>();

// ✨ Write strongly-typed queries, with auto-complete and runtime safety!
const productsQuery = (
q.star
.filterByType("products")
.order("price desc")
.slice(0, 10)
.project(product => ({
.project(sub => ({
name: q.string(),
price: q.number(),
slug: product.field("slug.current", q.string()),
imageUrls: product.field("images[]").deref().field("url", q.string())
slug: sub.field("slug.current", q.string()),
imageUrls: sub.field("images[]").deref().field("url", q.string())
}))
);
```
Expand Down Expand Up @@ -69,10 +78,10 @@ const results = await runQuery(productsQuery);
```


## Why `GROQD` over raw `GROQ`?
## Why `GroqD` over raw `GROQ`?

Sanity's CLI can generate types from your raw `GROQ` queries. This works well for simple use-cases.
However, `GROQD` aims to maximize the developer experience, improve generated types, and ensure scalability. Namely, it adds:
However, `GroqD` aims to maximize the developer experience, improve generated types, and ensure scalability. Namely, it adds:

- **Auto-completion** - write queries quickly and confidently
- **Runtime validation** - ensure data integrity, catch errors early
Expand All @@ -84,7 +93,6 @@ However, `GROQD` aims to maximize the developer experience, improve generated ty

We also provide a [Vision](https://www.sanity.io/docs/the-vision-plugin)-like Sanity Studio tool for experimenting with `groqd` queries and running them against your actual dataset.

(TODO: update this screenshot for 1.0)
![Screenshot of groqd playground in action](./img/groqd-playground-sample.png)

The playground is a drop-in Sanity plugin, and is an easy way to test out `groqd` within your dataset. See [the playground docs](./groqd-playground.mdx) for more information on the playground.
9 changes: 3 additions & 6 deletions website/src/arcade/playground/pokemon/groqd-client.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import { createGroqBuilderWithZod, makeSafeQueryRunner } from "groqd";
import type {
AllSanitySchemaTypes,
internalGroqTypeReferenceTo,
} from "./pokemon.sanity.types";
import type * as SanityTypes from "./pokemon.sanity.types";

type SchemaConfig = {
schemaTypes: AllSanitySchemaTypes;
referenceSymbol: typeof internalGroqTypeReferenceTo;
schemaTypes: SanityTypes.AllSanitySchemaTypes;
referenceSymbol: typeof SanityTypes.internalGroqTypeReferenceTo;
};

export const q = createGroqBuilderWithZod<SchemaConfig>({
Expand Down
2 changes: 1 addition & 1 deletion website/src/arcade/playground/run-query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export function createRunQuery({

try {
if (query instanceof GroqBuilder) {
// @ts-expect-error --- this is a hack so we can use`groq-builder` with the existing `groqd` logic:
// @ts-expect-error --- this is a hack so we can use`groqd` with the previous `groqd-legacy` logic:
query.schema = { parse: query.parser || ((x) => x) };
} else if (query instanceof q.BaseQuery) {
} else {
Expand Down
9 changes: 3 additions & 6 deletions website/src/arcade/playground/todo-list/groqd-client.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import { createGroqBuilderWithZod, makeSafeQueryRunner } from "groqd";
import type {
AllSanitySchemaTypes,
internalGroqTypeReferenceTo,
} from "./todo-list.sanity.types";
import type * as SanityTypes from "./todo-list.sanity.types";

type SchemaConfig = {
schemaTypes: AllSanitySchemaTypes;
referenceSymbol: typeof internalGroqTypeReferenceTo;
schemaTypes: SanityTypes.AllSanitySchemaTypes;
referenceSymbol: typeof SanityTypes.internalGroqTypeReferenceTo;
};

export const q = createGroqBuilderWithZod<SchemaConfig>({
Expand Down

0 comments on commit 7ae5b61

Please sign in to comment.