Package Exports
- @sanity-typed/groq-js
- @sanity-typed/groq-js/dist/index.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@sanity-typed/groq-js) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
@sanity-typed/groq-js
groq-js with typed GROQ Results
Page Contents
Install
npm install groq-js @sanity-typed/groq-js
Usage
Use parse
and evaluate
exactly as you would from groq-js
. Then, use the results with the typescript types!
your-typed-groq-js.ts
:
// import { evaluate, parse } from "groq-js";
import { evaluate, parse } from "@sanity-typed/groq-js";
const input = '*[_type == "product"]{productName}';
const tree = parse(input);
/**
* typeof tree === {
* type: "Projection";
* base: {
* type: "Filter";
* base: {
* type: "Everything";
* };
* expr: {
* type: "OpCall";
* op: "==";
* left: {
* name: "_type";
* type: "AccessAttribute";
* };
* right: {
* type: "Value";
* value: "product";
* };
* };
* };
* expr: {
* type: "Object";
* attributes: [{
* type: "ObjectAttributeValue";
* name: "productName";
* value: {
* type: "AccessAttribute";
* name: "productName";
* };
* }];
* };
* }
*/
const value = await evaluate(tree, {
dataset: [
{
_type: "product",
productName: "Some Cool Product",
// ...
},
{
_type: "someOtherType",
otherField: "foo",
// ...
},
],
});
const result = await value.get();
/**
* typeof result === [{
* productName: "Some Cool Product";
* }]
*/
Considerations
Using your derived types
You can also use your typed schema to keep parity with the types your typed client would receive.
npm install sanity groq-js @sanity-typed/types @sanity-typed/groq-js
product.ts
:
// import { defineArrayMember, defineField, defineType } from "sanity";
import {
defineArrayMember,
defineField,
defineType,
} from "@sanity-typed/types";
/** No changes using defineType, defineField, and defineArrayMember */
export const product = defineType({
name: "product",
type: "document",
title: "Product",
fields: [
defineField({
name: "productName",
type: "string",
title: "Product name",
}),
defineField({
name: "tags",
type: "array",
title: "Tags for item",
of: [
defineArrayMember({
type: "object",
name: "tag",
fields: [
{ type: "string", name: "label" },
{ type: "string", name: "value" },
],
}),
],
}),
],
});
sanity.config.ts
:
// import { defineConfig } from "sanity";
import { defineConfig } from "@sanity-typed/types";
import type { InferSchemaValues } from "@sanity-typed/types";
import { product } from "./schemas/product";
/** No changes using defineConfig */
const config = defineConfig({
projectId: "your-project-id",
dataset: "your-dataset-name",
schema: {
types: [
product,
// ...
],
},
});
export default config;
/** Typescript type of all types! */
export type SanityValues = InferSchemaValues<typeof config>;
/**
* SanityValues === {
* product: {
* _createdAt: string;
* _id: string;
* _rev: string;
* _type: "product";
* _updatedAt: string;
* productName?: string;
* tags?: {
* _key: string;
* label?: string;
* value?: string;
* }[];
* };
* // ... all your types!
* }
*/
your-typed-groq-js-with-sanity-types.ts
:
// import { evaluate, parse } from "groq-js";
import { evaluate, parse } from "@sanity-typed/groq-js";
import type { SanityDocument } from "@sanity-typed/types";
import type { SanityValues } from "./sanity.schema";
const input = '*[_type == "product"]{productName}';
const tree = parse(input);
const value = await evaluate(tree, {
dataset: [
{
_type: "product",
productName: "Some Cool Product",
// ...
},
{
_type: "someOtherType",
otherField: "foo",
// ...
},
] satisfies Extract<
SanityValues[keyof SanityValues],
Omit<SanityDocument, "_type">
>[],
});
const result = await value.get();
/**
* typeof result === {
* productName: string;
* }[]
*/
// Notice how `productName` is inferred as a `string`, not as `"Some Cool Product"`.
// Also, it's in an array as opposed to a tuple.
// This resembles the types you'd receive from @sanity-typed/client,
// which wouldn't be statically aware of `"Some Cool Product"` either.
The parsed tree changes in seemingly breaking ways
@sanity-typed/groq
attempts to type its parsed types as close as possible to groq-js
's parse
function output. Any fixes to match it more correctly won't be considered a major change and, if groq-js
changes it's output in a version update, we're likely to match it. If you're using the parsed tree's types directly, this might cause your code to break. We don't consider this a breaking change because the intent of these groq libraries is to match the types of a groq query as closely as possible.
GROQ Query results changes in seemingly breaking ways
Similar to parsing, evaluating groq queries will attempt to match how sanity actually evaluates queries. Again, any fixes to match that or changes to groq evaluation will likely not be considered a major change but, rather, a bug fix.