When building modern web applications, type safety and API consistency are essential. Instead of manually writing API clients and models (and risking drift between backend and frontend), you can automate the process using OpenAPI and Orval.
In this post, we’ll focus on how to generate TypeScript functions and interfaces using Orval, starting from an openapi.json description file.
Orval is a powerful RESTful client generator designed to streamline frontend development by automating the creation of TypeScript clients from OpenAPI v3 or Swagger v2 specifications.
Orval helps developers by generating typed API clients – TypeScript models from API schemas, HTTP request functions using different clients – and it can even mock up using MSW (Mock Service Worker) for frontend testing. This means:
… a.k.a. the Workflow in a Nutshell:
Backend →
openapi.json→ Orval → Typed API Client + Models → Vue App
In our case, we have a backend – written in Rust with Rocket and okapi – that generates openapi.json, where Orval then consumes that spec and generates the client part (used in our frontend) built with Vue.js.
Here’s an example of the configuration we use in orval.config.ts:
import { defineConfig } from 'orval';
export default defineConfig({
satayo2fetch: {
input: {
target: 'openapi.json'
},
output: {
mode: 'single',
client: 'axios',
workspace: 'src/api/',
target: './client.ts',
schemas: './models',
baseUrl: '/api',
prettier: true,
clean: true
},
hooks: {
afterAllFilesWrite: [
'prettier --write',
'eslint --fix'
]
}
}
});
output.client: we use axios (and we’ll explain below why this is better than fetch)output.mode 'single': generates one client file plus a folder for modelshooks: automatically formats and lints the generated codeRunning Orval with npx orval --config ./orval.config.ts will create:
src/api/
client.ts // All API functions
models/ // TypeScript interfaces from OpenAPI schemas
fetch for axiosWe initially generated a fetch-based client, but quickly realized axios was a better fit for production. Why?
In short, axios gives you a battle-ready toolkit for real-world apps, while fetch is fine for simpler cases.
To keep your client in sync with the backend contract, you need a repeatable process that guarantees the generated code always reflects the latest OpenAPI specification. This avoids type mismatches, runtime errors, and the dreaded “works on my machine” scenario.
So here’s a flow I can recommended for you:
The backend should produce an openapi.json file as part of its build or release process. This can be done by exposing an endpoint (e.g., /openapi.json) or exporting the file during CI.
This step regenerates the TypeScript client and models based on the updated spec.
Run your usual build and test steps. If the generated client introduces breaking changes, TypeScript will catch them during compilation, giving you early feedback.
By using Orval, your frontend becomes type-safe, maintainable, and future-proof. The backend can evolve, and as long as the OpenAPI spec is updated, your client stays in sync: no manual work, no guesswork.
So ends this tale of typed enchantments and automated conjuring… until the next deployment awakens the need for new magic.
Did you find this article interesting? Are you an “under the hood” kind of person? We’re really big on automation and we’re always looking for people in a similar vein to fill roles like this one as well as other roles here at Würth Phoenix.