Toapi
Define your API:
export const api = defineApi() .route("/greeting", { GET: defineHandler({ authorize: loggedInUsers }, async (req) => { return TResponse.json({ message: `Hello, ${req.auth().name}`}) }) })Use the SDK:
const { message } = await client.greeting.get()Concept
Section titled “Concept”We want to migrate back to running true SPAs: Applications that run in the browser but behave like real applications, not like websites. No server side rendering, no spinner on every page view. We want to download (and cache) a single bundle while the application starts (or while the user authenticates) and then have fast interactions afterwards.
This means we need to fetch data while rendering components and invalidate it when it changes. With all existing solutions this means we use some useQuery hook and some useMutation hook that takes care of invalidating useQuery’s cache. The problem? This concept does not scale. Every mutation needs to know about every query it needs to invalidate.
Toapi solves this problem by introducing server-managed tags for invalidating caches. Instead of invalidating a cache on the client during a mutation, we tag all data and invalidate the tags during any mutation:
export const GET = defineHandler({ authorize: loggedInUsers}, async req => { const users = sql`SELECT * FROM users` return TResponse.json({ users }, { cache: { tags: ['users'] }})})
export const POST = defineHandler({ authorize: admins, body: userSchema}, async req => { const user = await req.data() sql`INSERT INTO users (name, email) VALUES (${user.name}, ${user.email})` return TResponse.json({ userCount }, { cache: { tags: ['users'] }})})This way, the users response will be cached until the users tag is invalidated
const usersFresh = await client.users.get()const usersCached = await client.users.get()assert(usersFresh === usersCached)
await client.users.post({ name: 'John Doe', email: 'john@doe.com' })
const usersUpdated = await client.users.get()assert(usersUpdated !== usersCached)The @toapi/react package provides a useQuery hook to subscribe react components to data from Toapi-APIs:
const users = useQuery(client.users.get())There’s a lot more, discover the individual packages. I recommend to start with the server.
The stack
Section titled “The stack”Toapi is a small family of composable packages. Start with @toapi/server to define your API, generate a typed client with @toapi/client, then add caching, routing, React bindings and a Vite plugin as you need them.
Get started
Section titled “Get started”pnpm add @toapi/server @toapi/clientThen head to the server introduction to define your first API.