reactjstypescriptredux-toolkitrtk-queryzod

How to validate API response in RTK Query using Zod schema?


I want to validate the API response that I'm getting from a REST API using a Zod schema. For example, I have this user schema and this API

import { z } from 'zod';
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';


const userSchema = z.object({
  id: z.string(),
  name: z.string(),
  age: z.number(),
});

type User = z.infer<typeof userSchema>;


export const userAPI = createApi({
  reducerPath: 'userAPI',
  baseQuery: fetchBaseQuery({ baseUrl: 'https://some-api/' }),
  endpoints: (builder) => ({
    getPokemonByName: builder.query<user, string>({
      query: (id) => `user/${id}`,
    }),
  }),
})

I want to validate the API response against the userSchema using Zod. However, I'm unsure whether to use the parse or safeParse function provided by Zod.

Could you please clarify where in my code should I perform the response validation, and whether I should use parse or safeParse for this scenario?


Solution

  • With RTK 2.7 and higher

    You now have responseSchema and rawResponseSchema

    import { z } from 'zod';
    import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
    
    const userSchema = z.object({
      id: z.string(),
      name: z.string(),
      age: z.number(),
    });
    
    type User = z.infer<typeof userSchema>;
    
    export const userAPI = createApi({
      reducerPath: 'userAPI',
      baseQuery: fetchBaseQuery({ baseUrl: 'https://some-api/' }),
      endpoints: (build) => ({
        getPokemonByName: builder.query({
          query: (id) => `user/${id}`,
          responseSchema: userSchema,
        }),
        getTransformedPokemonByName: build.query({
          query: (id) => `user/${id}`,
          // you can infer untransformed results
          rawResponseSchema: userSchema,
          // then infer transformed results from here
          transformResponse: (response) => ({
            ...response,
            published_at: new Date(response.published_at),
          }),
        }),
      }),
    })
    

    Before RTK 2.7

    You can just use transformResponse provided by redux toolkit query to validate the response using the userSchema. If the response doesn't match the schema, an error will be triggered.

    import { z } from 'zod';
    import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
    
    const userSchema = z.object({
      id: z.string(),
      name: z.string(),
      age: z.number(),
    });
    
    type User = z.infer<typeof userSchema>;
    
    export const userAPI = createApi({
      reducerPath: 'userAPI',
      baseQuery: fetchBaseQuery({ baseUrl: 'https://some-api/' }),
      endpoints: (builder) => ({
        getPokemonByName: builder.query<User, string>({
          query: (id) => `user/${id}`,
          transformResponse: (response) => {
            userSchema.parse(response);
            return response;
          },
        }),
      }),
    });
    

    You can also write a wrapper for baseQuery with zod