Last active
February 1, 2024 21:31
-
-
Save merrickread/336c6e0f3149a03165f422e3f81d93ad to your computer and use it in GitHub Desktop.
Sanity + Groq + Zod
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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