Last active
February 17, 2023 00:47
-
-
Save WoLfulus/1f4cc5da924d2697b40ad5e7313ce2e6 to your computer and use it in GitHub Desktop.
Strongly typed Next.js routes and SWR
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
// pages/index.tsx | |
import { preload } from "@routes/server"; | |
import People from "../lib/loaders/people"; | |
export default Index({ defaultCount }) { | |
const [count, setCount] = useState(defaultCount); | |
return ( | |
<> | |
{/* | |
typeof count (prop) = number | undefined | |
autocomplete works, inferrerd from the route definition | |
*/} | |
<People.Loader count={count}> | |
<People.Error>Error loading people</People.Error> | |
<People.Loading>Loading {count} people</People.Loading> | |
<People.Data> | |
{/* | |
typeof list = People[] | |
autocomplete works, inferred from the route definition | |
*/} | |
{list => list.map(people => ( | |
<div>{people.name} is {people.gender} and is {people.height}cm tall.</div> | |
))} | |
</People.Data> | |
</People> | |
<div>Showing {count} people.</div> | |
<button onClick={() => setCount(count - 1)}>Less</button> | |
<button onClick={() => setCount(count + 1)}>More</button> | |
</> | |
); | |
} | |
export async function getStaticProps() { | |
return { | |
props: { | |
defaultCount: 10, | |
// Plays nice with SSR/SSG/ISR with support for preloading on the server | |
fallback: await preload([ | |
People({ | |
count: defaultCount | |
}) | |
]) | |
} | |
} | |
} |
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
// lib/loaders/people.ts | |
import { z, loader } from "@routes/core"; | |
// typeof count = number | undefined, autocomplete works, inferred from route definition | |
export default loader("/api/people", ({ count }) => `/api/people/?count=${count}`); |
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
// pages/api/people.ts | |
import { z } from "@routes/core"; | |
import { route } from "@routes/server"; | |
type People = { | |
name: string; | |
height: string; | |
gender: string; | |
}; | |
export default route( | |
async ({ query, data }) => { | |
const request = await fetch("https://swapi.dev/api/people/"); | |
const response = await request.json(); | |
const people = (response.results as People[]) | |
.sort(() => Math.random() - 0.5) | |
.slice(0, query.count) // typeof query.count = number, autocomplete works | |
.map(({ name, height, gender }) => ({ | |
name, | |
height, | |
gender, | |
})); | |
return data(people); | |
}, | |
{ | |
method: "get", | |
query: { | |
count: z.coerce.number().min(0).optional().default(5), | |
}, | |
} | |
); | |
// GET /api/people/ | |
// [ | |
// { | |
// "name": "Luke Skywalker", | |
// "height": "172", | |
// "gender": "male" | |
// }, | |
// { | |
// "name": "R2-D2", | |
// "height": "96", | |
// "gender": "n/a" | |
// }, | |
// { | |
// "name": "C-3PO", | |
// "height": "167", | |
// "gender": "n/a" | |
// }, | |
// { | |
// "name": "Biggs Darklighter", | |
// "height": "183", | |
// "gender": "male" | |
// }, | |
// { | |
// "name": "Beru Whitesun lars", | |
// "height": "165", | |
// "gender": "female" | |
// } | |
// ] | |
// | |
// GET /api/people/?count=2 | |
// [ | |
// { | |
// "name": "R2-D2", | |
// "height": "96", | |
// "gender": "n/a" | |
// }, | |
// { | |
// "name": "R5-D4", | |
// "height": "97", | |
// "gender": "n/a" | |
// } | |
// ] | |
// | |
// GET /api/people/?count=-1 | |
// { | |
// "error": { | |
// "code": "invalid_query", | |
// "message": "Number must be greater than or equal to 0", | |
// "issues": [ | |
// { | |
// "code": "too_small", | |
// "message": "Number must be greater than or equal to 0", | |
// "path": "query.count" | |
// } | |
// ] | |
// } | |
// } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment