Orval is Awesome
Published Mon Jan 15 2024Orval is a REST API client generator for JavaScript and TypeScript. Orval is great because it is simple, uses an open industry standard (OpenAPI), and has broad tooling support. This allows it to serve it’s purpose without making any tooling choices for you. To illustrate the benefits I will show you a simplified code sample from Orval’s repo:
Input
When using Orval, you need to provide 2 things: an Orval configuration file and a valid OpenAPI spec. Here is a simplified YAML description of an endpoint Orval could consume:
/pets:
get:
summary: List all pets
operationId: listPets
parameters:
- name: limit
in: query
description: How many items to return at one time (max 100)
required: false
schema:
type: string
responses:
'200':
description: A paged array of pets
headers:
x-next:
description: A link to the next page of responses
schema:
type: string
content:
application/json:
schema:
Pets:
type: array
items:
type: object
fields:
id:
type: integer
format: int64
name:
type: string
required:
- id
- name
The specification has a lot of details, but for us the most important are:
- Path:
/pets
- HTTP Method:
GET
- A query parameter named
limit
- The expected shape of a successful response
[{ id: 123, name: 'Winston' }]
Output
With this information Orval will generate some great boilerplate code to help kick start your API integration. Let’s take a look at what is generated for react-query with the above spec:
/**
* @summary List all pets
*/
export const listPets = (
params?: ListPetsParams, options?: AxiosRequestConfig
): Promise<AxiosResponse<unknown>> => {
return axios.get(
`/pets`,{
...options,
params: {...params, ...options?.params},}
);
First we have a function listPets
that calls axios
with the specified path of /pets
. This bit of code is helpful, but not exactly a game changer. It will be used in Orval’s later react-query code.
export const getListPetsQueryKey = (params?: ListPetsParams,) => {
return [`/pets`, ...(params ? [params]: [])] as const;
}
Next is a function for creating a query key. This is important for integrating with react-query where each request is cached and accessible using a unique query key. Since our endpoint has a query parameter, Orval has included params
in the key so that requests with different parameters are cached separately!
/**
* @summary List all pets
*/
export const useListPets = <TData = Awaited<ReturnType<typeof listPets>>, TError = AxiosError<unknown>>(
params?: ListPetsParams, options?: { query?:UseQueryOptions<Awaited<ReturnType<typeof listPets>>, TError, TData>, axios?: AxiosRequestConfig}
): UseQueryResult<TData, TError> & { queryKey: QueryKey } => {
const queryOptions = getListPetsQueryOptions(params,options)
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & { queryKey: QueryKey };
query.queryKey = queryOptions.queryKey ;
return query;
}
With the two previous generated functions Orval assembles a custom React hook for each endpoint when targeting react-query. The resulting function useListPets
can be easily imported and called within a React component now, where you get immediate access to type safety provided by Orval and async query state provided by react-query! I have posted the code referenced here on my GitHub.
Wrapping Up
Orval provides useful code generation for familiar tools, once you are generating API client code you can try using their faker and MSW integrations to streamline testing as well. Since many APIs today are documented using OpenAPI your integration may be minutes away! If your API is currently undocumented, the benefits of Orval may help push you or your organization to begin documenting with OpenAPI.