Skip to content

Instantly share code, notes, and snippets.

@merrickread
Last active February 1, 2024 21:31
Show Gist options
  • Save merrickread/336c6e0f3149a03165f422e3f81d93ad to your computer and use it in GitHub Desktop.
Save merrickread/336c6e0f3149a03165f422e3f81d93ad to your computer and use it in GitHub Desktop.
Sanity + Groq + Zod
// Okay, hope this track and makes sense. Created with the help of GPT
// Say we have Sanity Schema and client
import sanityClient from '@sanity/client';
const client = sanityClient({
projectId: 'yourProjectId', // Replace with your project ID
dataset: 'yourDataset', // Replace with your dataset name
useCdn: true, // `false` if you want fresh data
});
// The posts schema for Posts has titles, bodies, likes, and published dates.
const query = `*[_type == "post"]{
title,
body,
likes,
publishedAt
}`;
// We use a Zod schema to match Sanity data
import { z } from 'zod';
// This schema will be used to validate the data fetched from Sanity.
const BlogPostSchema = z.object({
title: z.string(),
body: z.string(),
likes: z.number().positive(),
publishedAt: z.string().datetime(),
});
const BlogPostsSchema = z.array(BlogPostSchema);
// Use Zod's `infer` to derive TypeScript types from the Zod schema
type BlogPost = z.infer<typeof BlogPostSchema>;
type BlogPosts = z.infer<typeof BlogPostsSchema>;
// Then in our component we can query and validate
// (Exact implementation can look different, maybe we validate earlier)
import React, { useEffect, useState } from 'react';
const BlogPostsComponent: React.FC = () => {
const [blogPosts, setBlogPosts] = useState<BlogPosts | null>(null);
const [loading, setLoading] = useState<boolean>(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
const fetchPosts = async () => {
try {
const data = await client.fetch(query);
const validatedData = BlogPostsSchema.parse(data); // << Validates and infers type
setBlogPosts(validatedData);
} catch (err) {
setError(err instanceof Error ? err : new Error('Failed to fetch or validate data'));
} finally {
setLoading(false);
}
};
fetchPosts();
}, []);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
{blogPosts?.map((post, index) => (
<div key={index}>
<h2>{post.title}</h2>
<div>{post.body}</div>
<p>Has so many likes: {post.likes}</p>
<p>Published on: {post.publishedAt}</p>
</div>
))}
</div>
);
};
export default BlogPostsComponent;
// This process is going to be a little more manual but maybe that's okay at the beginning here
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment