Add API Endpoint
Step-by-step guide for adding a new API endpoint using OpenAPI-first development.
Complete walkthrough for adding a new API endpoint with full type safety.
Overview
Adding an endpoint follows the OpenAPI-first pattern:
- Update OpenAPI spec
- Generate TypeScript clients
- Implement server route
- Use generated typed clients
Step 1: Update OpenAPI Specification
Add the endpoint to your OpenAPI spec in apps/api/openapi/openapi.json:
{
"paths": {
"/users/{id}": {
"get": {
"operationId": "getUser",
"summary": "Get user by id",
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": { "type": "string" }
}
],
"responses": {
"200": {
"description": "User found",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"id": { "type": "string" },
"email": { "type": "string" },
"name": { "type": "string" }
},
"required": ["id", "email", "name"]
}
}
}
},
"404": {
"description": "User not found",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"code": { "type": "string" },
"message": { "type": "string" }
}
}
}
}
}
}
}
}
}
}Step 2: Generate TypeScript Clients
Run hey-api to generate TypeScript clients:
# Generate clients for core and react packages
pnpm --filter @basilic/core gen
pnpm --filter @basilic/react genThis generates type-safe client code in:
packages/core/src/gen/- Runtime-agnostic clientpackages/react/src/gen/- React Query hooks
Step 3: Implement Server Route
Implement the route in apps/api/src/routes/:
// apps/api/src/routes/users.ts
import type { FastifyPluginAsync } from 'fastify'
const users: FastifyPluginAsync = async (fastify) => {
fastify.get<{
Params: { id: string }
}>('/users/:id', async (request, reply) => {
const { id } = request.params
// Your implementation here
const user = await getUserById(id)
if (!user) {
return reply.status(404).send({
code: 'NOT_FOUND',
message: 'User not found',
})
}
return reply.send(user)
})
}
export default usersStep 4: Use Generated Typed Client
The generated client is fully typed:
// In your frontend or other client
import { createApi } from '@basilic/core'
import { useUser } from '@basilic/react' // Generated React Query hook
// Runtime-agnostic client
const api = createApi({
baseUrl: process.env.NEXT_PUBLIC_API_URL!,
getAuthToken: async () => session?.token,
})
// Fully typed API call
const user = await api.getUser({ id: '123' })
// user is typed from OpenAPI spec
// Or use generated React Query hooks
const { data } = useUser({ id: '123' })
// data is typed as UserDTO | undefinedChecklist
- OpenAPI spec updated in
apps/api/openapi/openapi.json - Clients regenerated with hey-api
- Server route implemented
- Route matches OpenAPI spec
- Client usage tested
Related Documentation
- Code-First APIs - Understanding contracts
- Package Conventions - Package architecture