Overview
The most basic way to define an API specification in typed-api-spec is using TypeScript types. However, you may want to perform runtime validation to check that request parameters are provided in the expected type.
The simplest way is to use only the type information provided by a validation library. For exmple, you can use zod to define the API specification of typed-api-spec.
import { z } from "zod";
const User = z.object({
id: z.string(),
name: z.string(),
});
type User = z.infer<typeof User>;
const UsersQuery = z.object({
page: z.string(),
});
type Spec = DefineApiEndpoints<{
"/users": {
get: {
query: UsersQuery;
responses: { 200: { body: User } };
};
};
}>;
In this way, you can completely separate runtime validation and API definition, allowing you to use any validation library. To validate, just use the validation library. Following code is an example of how to validate request parameters using zod and express.
const app = Express();
app.get("users", (req, res) => {
const usersQuery = UsersQuery.parse(req.query)
})
This is not bad, but the problem is that there is no guarantee that the UsersQuery is actually the expected schema. For example, you may accidentally write something like User.parse(req.query)
.
(User
is expected as a response schema, not a query.)
typed-api-spec provides a way to define the API specification using the validation library directly.
import { z } from "zod";
const Spec = {
"/users": {
get: {
query: z.object({ page: z.string() }),
responses: { 200: { body: z.object({ id: z.string(), name: z.string() }) } }
}
}
} satisfies ZodApiEndpoints
In this way, you can use validate function directly.
const app = Express();
app.get("users", (req, res) => {
const usersQuery = Spec["/users"]["get"].query.parse(req.query)
})
Even if you define API Specification with a validation library, typed-api-spec does not validate the request parameters automatically. You need to explicitly perform validation, whether on the server side or on the client side.