Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GroqD 1.0: Improved docs #313

Merged
merged 5 commits into from
Jan 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading