In the previous post I generated an OpenAPI YAML from a TypeSpec API definition.
Now that we have an OpenAPI spec, let’s generate client code and even React hooks for data fetching from the frontend.
With Orval, we can generate hooks for SWR, so let’s try it.
What is SWR?
SWR is a data‑fetching hooks library for React providing caching, error handling, and more.
It makes implementing API requests in the frontend easy. Example from the docs:
function Profile() {
const { data, error, isLoading } = useSWR('/api/user', fetcher)
if (error) return <div>failed to load</div>
if (isLoading) return <div>loading...</div>
return <div>hello {data.name}!</div>
}
What is Orval?
Orval generates TypeScript clients from OpenAPI (Swagger). Features:
- Generate TypeScript models
- Generate HTTP calls
- Generate MSW (Mock Service Worker) mocks
It can also generate SWR hooks, React Query, Angular clients, etc.
Install
npm i orval -D
Prepare files
We’ll need the TypeSpec tsp
file and Orval config.
main.tsp
Define a simple API with TypeSpec:
using TypeSpec.Http;
model User {
id: string;
name: string;
email: string;
}
@route("/users")
interface Users {
@get
getUsers(): User[];
@get
@route("/{id}")
getUser(@path id: string): User;
}
Generate openapi.yml
as in the previous post:
tsp compile main.tsp --emit @typespec/openapi3
orval.config.ts
We want SWR hooks; docs:
Create orval.config.ts
:
module.exports = {
userstore: {
input: {
target: "./openapi.yml",
},
output: {
mode: "split",
target: "./api/endpoints",
schemas: "./api/models",
client: "swr",
},
},
};
This places model types under api/models
and SWR files under api/endpoints
.
Run prettier on generated files
Use afterAllFilesWrite
hook:
module.exports = {
userstore: {
input: {
target: "./openapi.yml",
},
output: {
mode: "split",
target: "./api/endpoints",
schemas: "./api/models",
client: "swr",
},
hooks: {
afterAllFilesWrite: "prettier --write",
},
},
};
Generate
npx orval
Warnings may appear, but files should be generated under api
.
models/user.ts
Contains data models used by the API, reflecting the TypeSpec definitions:
export interface User {
email: string;
id: string;
name: string;
}
endpoints/userAPI.ts
SWR hooks for each API endpoint are generated, e.g.:
export const useUsersGetUsers = (...) => { /* ... */ }
export const useUsersGetUser = (id: string, ...) => { /* ... */ }
Usage:
const { data, error, isLoading } = useUsersGetUsers();
You can pass SWR options; e.g., disable caching with enabled: false
:
const { data, error, isLoading } = useUsersGetUsers({
swr: {
enabled: false,
},
});
Set API base URL
By default the generated client requests the same origin as the frontend. To target a different domain, set axios’s baseURL (Orval uses axios under the hood):
axios.defaults.baseURL = '<BACKEND URL>';
See also: Base URL guide.
Summary
Workflow:
- Generate OpenAPI from TypeSpec
tsp
files. - Use Orval to generate SWR hooks for the frontend.
Since Orval can also generate MSW mocks, you can proceed with frontend dev using a TypeSpec definition even before the backend is ready.