Server Validation only pickups the message but not each error

I'm using the simple rest data provider and I want to do server validation on create/update, but the errors are not shown in the form, just a generic "There was an error creating device (status code: 400)" the message sent from the server is there in the notification but the fields are not marked as error. this is the response I'm sending when validation fails on the server.
{
"message": "algo fallo",
"statusCode": 400,
"errors": {
"ip": [
"dog"
],
"title": [
"cat"
]
}
}
{
"message": "algo fallo",
"statusCode": 400,
"errors": {
"ip": [
"dog"
],
"title": [
"cat"
]
}
}
8 Replies
kapa.ai
kapa.ai4w ago
Hi @xxmelonxx. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- To ensure that server-side validation errors are properly displayed in your form fields when using the simple REST data provider in Refine, you need to make sure that the error response from the server matches the HttpError interface expected by Refine's useForm. Here is an example of how to set up the data provider and handle server-side validation errors: 1. Define the HttpError Interface: Ensure that your error response matches the HttpError interface. The errors field should contain the specific field errors. 2. Set Up the Data Provider: Customize your data provider to return the error response in the correct format. Here is an example:
import type { HttpError } from "@refinedev/core";
import baseDataProvider from "@refinedev/simple-rest";

const dataProvider = {
...baseDataProvider("<https://api.fake-rest.refine.dev">),
create: async () => {
// For demo purposes, we're hardcoding the error response.
// In a real-world application, the error of the server should match the `HttpError` interface
// or should be transformed to match it.
return Promise.reject({
message: "This is an error from the server",
statusCode: 400,
errors: {
name: "Name should be at least 3 characters long",
material: "Material should start with a capital letter",
description: "Description should be at least 10 characters long",
},
} as HttpError);
}
};

export default dataProvider;

import type { HttpError } from "@refinedev/core";
import baseDataProvider from "@refinedev/simple-rest";

const dataProvider = {
...baseDataProvider("<https://api.fake-rest.refine.dev">),
create: async () => {
// For demo purposes, we're hardcoding the error response.
// In a real-world application, the error of the server should match the `HttpError` interface
// or should be transformed to match it.
return Promise.reject({
message: "This is an error from the server",
statusCode: 400,
errors: {
name: "Name should be at least 3 characters long",
material: "Material should start with a capital letter",
description: "Description should be at least 10 characters long",
},
} as HttpError);
}
};

export default dataProvider;

3. Handle Errors in the Form: Use the useForm hook to handle the form submission and error propagation. Here is an example of how to set up the form: ```typescript import { useForm } from "@refinedev/core"; const MyFormComponent = () => { const { mutationResult: { error }, formLoading, onFinish, } = useForm(); const { errors } = error ?? {}; return ( <div style={{ position: "relative" }}> <form onSubmit={(event) => { event.preventDefault(); const formData = new FormData(event.currentTarget); const variables = Object.fromEntries(formData.entries()); onFinish(variables).catch(() => {}) }} style={{ display: "flex", flexDirection: "column", gap: "12px" }}> <label> <span>Name</span> <input type="text" id="name" name="name" /> </label> <span style={{ color: "red" }}>{errors?.name ?? ""}</span> <label> <span>Material</span> <input type="text" id="material" name="material" /> </label> <span style={{ color: "red" }}>{errors?.material ?? ""}</span> <label> <span>Description</span> <textarea id="description" name="description" /> </label> <span style={{ color: "red" }}>{errors?.description ?? ""}</span> <button type="submit">Save</button> </form> {formLoading && (<div style={{ position: "absolute", inset: 0, width: "100%", height: "100%", display: "flex", alignItems: "center", justifyContent: "center", backgroundColor: "rgba(255, 255, 255, 0.5)", color: "#000", }}>loading...</div>)} </div> ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai4w ago
<@833526940967108638> kapa.ai is still learning and improving, please let us know how it did by reacting below
genetic-orange
genetic-orange4w ago
@kapa.ai I'm already returning the expected Interface, but is not picking up the errors just the message
kapa.ai
kapa.ai4w ago
Let me check the knowledge base for you. One moment! :books: ---------------------- If you are already returning the expected HttpError interface but the errors are not being picked up by the form fields, you might need to ensure that the useForm hook is correctly set up to handle and propagate these errors. Here’s a step-by-step guide to ensure everything is configured correctly: 1. Ensure Error Format: Make sure your server response matches the HttpError interface exactly. Here’s an example of the expected format:
{
"message": "algo fallo",
"statusCode": 400,
"errors": {
"ip": ["dog"],
"title": ["cat"]
}
}

{
"message": "algo fallo",
"statusCode": 400,
"errors": {
"ip": ["dog"],
"title": ["cat"]
}
}

2. Set Up the Data Provider: Ensure your data provider is correctly set up to return this error format. Here’s an example:
import type { HttpError } from "@refinedev/core";
import baseDataProvider from "@refinedev/simple-rest";

const dataProvider = {
...baseDataProvider("<https://api.fake-rest.refine.dev">),
create: async () => {
return Promise.reject({
message: "algo fallo",
statusCode: 400,
errors: {
ip: ["dog"],
title: ["cat"],
},
} as HttpError);
}
};

export default dataProvider;

import type { HttpError } from "@refinedev/core";
import baseDataProvider from "@refinedev/simple-rest";

const dataProvider = {
...baseDataProvider("<https://api.fake-rest.refine.dev">),
create: async () => {
return Promise.reject({
message: "algo fallo",
statusCode: 400,
errors: {
ip: ["dog"],
title: ["cat"],
},
} as HttpError);
}
};

export default dataProvider;

3. Handle Errors in the Form: Use the useForm hook to handle the form submission and error propagation. Here’s an example of how to set up the form: ```typescript import { useForm } from "@refinedev/react-hook-form"; import { Create } from "@refinedev/chakra-ui"; import { FormControl, FormErrorMessage, FormLabel, Input, Textarea, } from "@chakra-ui/react"; export const ProductCreate = () => { const { refineCore: { formLoading }, saveButtonProps, register, formState: { errors }, } = useForm(); return ( <Create saveButtonProps={saveButtonProps}> <FormControl mb="3" isInvalid={!!errors?.ip}> <FormLabel>IP</FormLabel> <Input id="ip" type="text" {...register("ip", { required: "IP is required" })} /> <FormErrorMessage> {errors.ip?.message} </FormErrorMessage> </FormControl> <FormControl mb="3" isInvalid={!!errors?.title}> <FormLabel>Title</FormLabel> <Input id="title" type="text" {...register("title", { required: "Title is required" })} /> <FormErrorMessage> {errors.title?.message} </FormErrorMessage> </FormControl> <FormControl mb="3" isInvalid={!!errors?.description}> <FormLabel>Description</FormLabel> <Textarea id="description" {...register("description", { required: "Description is required" })} /> <FormErrorMessage> {errors.description?.message} </FormErrorMessage> </FormControl> <FormControl mb="3" isInvalid={!!errors?.price}> <FormLabel>Price</FormLabel> <Input id="price" type="number" {...register("price", { required: "Price is required" })} /> <FormErrorMessage> {errors.price?.message} </FormErrorMessage> </FormControl> </Create> ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai4w ago
<@833526940967108638> kapa.ai is still learning and improving, please let us know how it did by reacting below
genetic-orange
genetic-orange4w ago
@kapa.ai if I use the Promise.reject it works as expected, but the request will not go to the server obviously, the response from the server is the same as the interface but I'm getting an AxiosError which includes the request/response witht he expected data
kapa.ai
kapa.ai4w ago
Let me check the knowledge base for you. One moment! :books: ---------------------- To handle server-side validation errors properly when using Axios with Refine, you need to ensure that the error response from the server is transformed to match the HttpError interface expected by Refine. This can be done using Axios interceptors to transform the error before it is returned to your code. Here’s how you can set up Axios interceptors to handle and transform the error response: 1. Set Up Axios Interceptors: Use Axios interceptors to transform the error response to match the HttpError interface.
import axios from "axios";
import { DataProvider, HttpError } from "@refinedev/core";

// Create an Axios instance
const axiosInstance = axios.create();

// Add a response interceptor
axiosInstance.interceptors.response.use(
(response) => {
return response;
},
(error) => {
const customError: HttpError = {
message: error.response?.data?.message || "Something went wrong",
statusCode: error.response?.status || 500,
errors: error.response?.data?.errors || {},
};

return Promise.reject(customError);
}
);

// Define the data provider
export const dataProvider = (apiUrl: string): DataProvider => ({
create: async ({ resource, variables }) => {
try {
const response = await axiosInstance.post(`${apiUrl}/${resource}`, variables);
return { data: response.data };
} catch (error) {
return Promise.reject(error);
}
},
// Other methods (getOne, update, delete, etc.)
});

import axios from "axios";
import { DataProvider, HttpError } from "@refinedev/core";

// Create an Axios instance
const axiosInstance = axios.create();

// Add a response interceptor
axiosInstance.interceptors.response.use(
(response) => {
return response;
},
(error) => {
const customError: HttpError = {
message: error.response?.data?.message || "Something went wrong",
statusCode: error.response?.status || 500,
errors: error.response?.data?.errors || {},
};

return Promise.reject(customError);
}
);

// Define the data provider
export const dataProvider = (apiUrl: string): DataProvider => ({
create: async ({ resource, variables }) => {
try {
const response = await axiosInstance.post(`${apiUrl}/${resource}`, variables);
return { data: response.data };
} catch (error) {
return Promise.reject(error);
}
},
// Other methods (getOne, update, delete, etc.)
});

2. Handle Errors in the Form: Use the useForm hook to handle the form submission and error propagation. Here’s an example of how to set up the form: ```typescript import { useForm } from "@refinedev/react-hook-form"; import { Create } from "@refinedev/chakra-ui"; import { FormControl, FormErrorMessage, FormLabel, Input, Textarea, } from "@chakra-ui/react"; export const ProductCreate = () => { const { refineCore: { formLoading }, saveButtonProps, register, formState: { errors }, } = useForm(); return ( <Create saveButtonProps={saveButtonProps}> <FormControl mb="3" isInvalid={!!errors?.ip}> <FormLabel>IP</FormLabel> <Input id="ip" type="text" {...register("ip", { required: "IP is required" })} /> <FormErrorMessage> {errors.ip?.message} </FormErrorMessage> </FormControl> <FormControl mb="3" isInvalid={!!errors?.title}> <FormLabel>Title</FormLabel> <Input id="title" type="text" {...register("title", { required: "Title is required" })} /> <FormErrorMessage> {errors.title?.message} </FormErrorMessage> </FormControl> <FormControl mb="3" isInvalid={!!errors?.description}> <FormLabel>Description</FormLabel> <Textarea id="description" {...register("description", { required: "Description is required" })} /> <FormErrorMessage> {errors.description?.message} </FormErrorMessage> </FormControl> <FormControl mb="3" isInvalid={!!errors?.price}> <FormLabel>Price</FormLabel> <Input id=" ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai4w ago
<@833526940967108638> kapa.ai is still learning and improving, please let us know how it did by reacting below