{"id":16494829,"url":"https://github.com/stipsan/groqz","last_synced_at":"2025-10-04T22:21:41.563Z","repository":{"id":65227028,"uuid":"582764571","full_name":"stipsan/groqz","owner":"stipsan","description":"(experimental) Transforms GROQ strings to zod schemas in your TypeScript codebase.","archived":false,"fork":false,"pushed_at":"2025-06-14T14:46:19.000Z","size":619,"stargazers_count":18,"open_issues_count":18,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-06-14T15:40:14.238Z","etag":null,"topics":["groq","sanity","typescript","zod"],"latest_commit_sha":null,"homepage":"https://groqz-docs.vercel.app","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/stipsan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2022-12-27T20:02:53.000Z","updated_at":"2025-06-10T09:03:12.000Z","dependencies_parsed_at":"2023-02-18T20:10:33.352Z","dependency_job_id":"a51660ac-6568-4b14-96ad-370b39709d54","html_url":"https://github.com/stipsan/groqz","commit_stats":{"total_commits":125,"total_committers":4,"mean_commits":31.25,"dds":0.4,"last_synced_commit":"a5d4facf60fe9082aaa1b765745a4590ec7d79f2"},"previous_names":[],"tags_count":33,"template":false,"template_full_name":null,"purl":"pkg:github/stipsan/groqz","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stipsan%2Fgroqz","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stipsan%2Fgroqz/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stipsan%2Fgroqz/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stipsan%2Fgroqz/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stipsan","download_url":"https://codeload.github.com/stipsan/groqz/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stipsan%2Fgroqz/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259843205,"owners_count":22920297,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["groq","sanity","typescript","zod"],"created_at":"2024-10-11T14:15:50.467Z","updated_at":"2025-10-04T22:21:36.503Z","avatar_url":"https://github.com/stipsan.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# GROQZ (experimental)\n\nTransforms [GROQ] strings to [zod] schemas in your TypeScript codebase.\nThis lets you check your app is using query responses to the [Sanity Content Lake](https://www.sanity.io/docs/datastore) in a way that is runtime safe.\nBasically eliminate bugs like:\n\n```console\nTypeError: Cannot read properties of undefined\n```\n\n## Status\n\nGROQZ can't yet be tested end-to-end until the first codegen plugin `@groqz/babel-plugin` is ready.\n\n## Concept\n\nIn a project with a `sanity.config.ts` file (introduced in [Sanity Studio v3]) that defines a schema like this:\n\n```ts\nimport { defineConfig } from 'sanity'\n\nexport default defineConfig({\n  schema: {\n    types: [\n      {\n        name: 'page',\n        type: 'document',\n        fields: [\n          {\n            name: 'title',\n            type: 'string',\n          },\n          {\n            name: 'likes',\n            type: 'number',\n          },\n        ],\n      },\n    ],\n  },\n})\n```\n\nIt takes [GROQ] strings as input, and provides `{query: string, schema: z.ZodType}` as output:\n\n```ts\nimport { groq } from 'groqz'\n\nconst { query, schema } = groq`*[_type == \"page\"]{_type, title, likes}`\n```\n\nYou forward the `query` string to [@sanity/client], or any other library that takes [GROQ] as input and a JSON response. And then call `schema.parse` on that response:\n\n```ts\n// Sanity Client on Content Lake\nimport {createClient} from `@sanity/client`\n\nconst client = createClient()\nconst data = schema.parse(await client.fetch(query))\n```\n\n```ts\n// Querying Content Lake with another client\nimport PicoSanity from 'picosanity'\n\nconst client = new PicoSanity()\n\nconst data = schema.parse(await client.fetch(query))\n```\n\n```ts\n// Type safety for live preview modes\nimport { definePreview } from '@sanity/preview-kit'\nimport { useDeferredValue, useMemo } from 'react'\n\nconst usePreview = definePreview()\nfunction ReactComponent() {\n  const deferredData = useDeferredValue(usePreview(query))\n  // live preview may re-render frequently if there's a lot of Studio activity (multiple people editing documents that are being previewed)\n  // so wrapping it in useDeferredValue + useMemo lets React defer the zod parsing of the response if IO is too busy for it to be done on every re-render\n  const data = useMemo(() =\u003e schema.parse(deferredData), [deferredData])\n}\n```\n\nYou can use `z.infer` to add typing to your custom code:\n\n```ts\nimport { groq } from 'groqz'\nimport { z } from 'zod'\n\nconst { query, schema } = groq`*[_type == \"page\"]{_type, title, likes}`\n\nfunction App(data: z.infer\u003ctypeof schema\u003e) {\n  //\n}\n\nasync function renderApp() {\n  return App(schema.parse(await client.fetch(query)))\n}\n```\n\nYou can also use `InferType` if your custom code operates on the parsed query object itself:\n\n```ts\nimport { groq, type InferType } from 'groqz'\n\nconst allDocuments = groq`*`\n\nfunction usedDocumentTypes(\n  documents: InferType\u003ctypeof allDocuments\u003e\n): Set\u003cstring\u003e {\n  return new Set(pages.map((page) =\u003e page._type))\n}\n\nclient.fetch(query).then(schema.parse).then(usedDocumentTypes).then(console.log)\n```\n\nThis is similar to how other libraries like [`groqd`] works, and libraries like [`@sanity/client`] may be updated to take `{query, schema}` as input in the future:\n\n```ts\n// This is just an example scenario, `@sanity/client` doesn't support this\nimport {createClient} from `@sanity/client`\nimport {groq} from 'groqz'\n\nconst client = createClient()\nconst data = await client.fetch(groq`*[]`)\n// data is typed as {_type: \"page\", title?: string, likes?: number}[]\n// as `client` is calling `schema.parse` on the resposne before returning it\n```\n\n## Design\n\nIt's necessary to provide both typegen and codegen for this to work end-to-end. The generated typings that TS uses to check that you are using your GROQ data in a runtime safe is built on [zod]. And [zod] typings live under the assumption that you called `schema.parse` on the data, letting it throw a parser error should the input JSON mismatch the generated schema.\nUnfortunately there's no tool we can use that can deal let us setup typegen and codegen at the same time. We have to provide tooling that integrates with peoples TypeScript setup, and their bundler/codegen, as two separate strategies.\n\n### Typegen\n\nIn order to do runtime checks in TypeScript it's necessary to generate typings. [This is because TS only have an API that lets us extend the \"editing experience\"](https://github.com/microsoft/TypeScript/wiki/Writing-a-Language-Service-Plugin), it doesn't have a plugin API that would let us generate typigns from arbitrary GROQ strings as part of running the TS cli `tsc`. It leaves us with two options: IDE plugins that integrate with the editing experience and generate typings in that process, and a cli.\n\n- [groqz] and its cli command `groqz typegen src/**/*.ts`\n- (planned) `groqz-vscode`\n- (maybe) `groqz-intellij` (can be done like [`import-cost`](https://github.com/denofevil/import-cost) so it works in WebStorm, Intellij IDEA and all the rest)\n\n### Codegen\n\nWe'll have to support a wide range of build plugins to integrate with the diversity of tooling that folks use.\n\n- (in progress) `@groqz/babel-plugin` that can be used with general babel setups, as well as [babel macros](https://github.com/kentcdodds/babel-plugin-macros) (used in frameworks like [CRA] that supports it instead of letting you use a custom `babel.config|.babelrc` file).\n- (planned) `@groqz/webpack-plugin`\n- (planned) `@groqz/rollup-plugin`\n- (planned) `@groqz/esbuild-plugin`\n- (planned) `@groqz/swc-plugin`\n- (planned) `@groqz/rollup-plugin`\n- (planned) `@groqz/parcel-plugin`\n- if all else fails, `@groqz/to-js` can be used to generate the runtime code for projects that use tooling that we don't have a plugin for.\n\n## When developing GROQZ\n\n### Useful commands\n\n- `pnpm build` - Build all packages and the docs site\n- `pnpm dev` - Develop all packages and the docs site\n- `pnpm lint` - Lint all packages\n- `pnpm changeset` - Generate a changeset\n- `pnpm clean` - Clean up all `node_modules` and `dist` folders (runs each package's clean script)\n\n## Versioning and Publishing packages\n\nPackage publishing has been configured using [Changesets](https://github.com/changesets/changesets). Please review their [documentation](https://github.com/changesets/changesets#documentation) to familiarize yourself with the workflow.\n\nThis example comes with automated npm releases setup in a [GitHub Action](https://github.com/changesets/action). To get this working, you will need to create an `NPM_TOKEN` and `GITHUB_TOKEN` in your repository settings. You should also install the [Changesets bot](https://github.com/apps/changeset-bot) on your GitHub repository as well.\n\nFor more information about this automation, refer to the official [changesets documentation](https://github.com/changesets/changesets/blob/main/docs/automating-changesets.md)\n\n[groq-js]: https://github.com/sanity-io/groq-js\n[zod]: https://zod.dev/\n[groq]: https://www.sanity.io/docs/groq\n[sanity content lake]: https://www.sanity.io/docs/datastore\n[sanity studio v3]: https://www.sanity.io/blog/sanity-studio-v3-simplified-yet-powerful-customization\n[`groqd`]: https://github.com/FormidableLabs/groqd\n[cra]: https://create-react-app.dev/\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstipsan%2Fgroqz","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstipsan%2Fgroqz","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstipsan%2Fgroqz/lists"}