Skip to main content

Create an API endpoint

While ZenStack automatically handles CRUD operations for your data models, you can also create custom API endpoints using tRPC. This is particularly useful for implementing custom business logic, integrating external services, or creating specialized endpoints. Existing plugins such as file upload are already defined following this pattern in the project.

Creating a Custom Router

Here's a basic example of how to create a custom router:

import { z } from 'zod'
import { Trpc } from '~/core/trpc/base'

export const CustomRouter = Trpc.createRouter({
greet: Trpc.procedure
.input(z.object({ name: z.string() }))
.query(async ({ input }) => {
return `Hello, ${input.name}!`
}),

randomNumber: Trpc.procedurePublic
.query(() => {
return Math.random()
}),
})

In this example, we've created two endpoints:

  1. greet: Takes a name as input and returns a greeting. This endpoint is protected by default.
  2. randomNumber: Returns a random number. This endpoint is public and can be accessed without authentication.

Adding the Custom Router to the App Router

To make your custom router available, you need to add it to the main app router at app/core/trpc/server/index.tsx:

import { createRouter } from '../../.marblism/zenstack/routers'
import { Trpc } from '../base'
import { CustomRouter } from './custom.router'

export const appRouter = Trpc.mergeRouters(
createRouter(Trpc.createRouter, Trpc.procedure),

// Add your custom router here
Trpc.createRouter({
custom: CustomRouter,
// ... other routers
}),
)

export type AppRouter = typeof appRouter

export const Server = {
appRouter,
}

Using the Custom Endpoint in the Frontend

Once your custom router is added to the app router, you can use it in your frontend components like this:

import { Api } from '@/core/trpc'

// In a React component
const MyComponent = () => {
const greetQuery = Api.custom.greet.useQuery({ name: 'World' })
const randomNumberQuery = Api.custom.randomNumber.useQuery()

if (greetQuery.isLoading || randomNumberQuery.isLoading) return <div>Loading...</div>

return (
<div>
<p>{greetQuery.data}</p>
<p>Random number: {randomNumberQuery.data}</p>
</div>
)
}

By following this pattern, you can create custom endpoints for various functionalities while still leveraging the power of tRPC and ZenStack's automatic CRUD operations. Remember that you can control the access level of your endpoints using Trpc.procedure for protected routes and Trpc.procedurePublic for public routes.