Client Can't Infer Error When Using OnError

by ADMIN 44 views

Resolving Client-Side Type Inference Issues with onError in Hono

When working with APIs, it's essential to ensure that client-side type inference works correctly, especially when handling errors. In this article, we'll explore the issue of type inference not working as expected when using the onError middleware in Hono, and provide a solution to resolve this problem.

Understanding the Issue

In the example code, we have a Hono application with multiple routes, each returning a JSON response when successful or throwing an error. We're using the onError middleware to catch and handle errors in the application. However, when we try to infer the type of the response on the client-side, it doesn't work as expected when an error is thrown.

import { Hono } from "hono"

const app = new Hono().get("/", (c) => {

  if(!c.env){
    throw new Error("Something wrong happened")
  }

  const result = {message: "foo"}

  return c.text(result, 201)
})

export default app
import { onError } from "stoker/middlewares"
import { Hono } from "hono"

const app = new Hono()
  .route("/orders", orders)
  .onError(onError)

The Problem with onError

The issue arises because the onError middleware doesn't return a JSON response with an error code (e.g., 400 or 500). Instead, it catches and handles the error internally. This means that when we try to infer the type of the response on the client-side, it doesn't work as expected when an error is thrown.

export async function createOrder() {
  try {

    const client = hcWithType(import.meta.env.VITE_BASE_API_URL)
    const response = await client.orders.$post( )

    if (!response.ok) {
      // This returns the type I get when successful, I want the type when an error is thrown
      const data = await response.json() 
      throw new Error(data)
    }
    return await response.json()
  } catch (error) {
    console.error("Order creation error:", error)
    throw error
  }
}

Resolving the Issue

To resolve this issue, we need to modify the onError middleware to return a JSON response with an error code when an error is thrown. We can achieve this by using the c.error method provided by Hono.

import { onError } from "stoker/middlewares"
import { Hono } from "hono"

const app = new Hono()
  .route("/orders", orders)
  .onError((c, err) => {
    return c.error(500, { message: "Internal Server Error" })
  })

In this modified version, when an error is thrown, the onError middleware catches it and returns a JSON response with a 500 error code and a message.

Client-Side Type Inference

With this modification, the client-side type inference should now work correctly when an error is thrown. We can modify the createOrder function to handle the error response correctly.

export async function createOrder() {
  try {

    const client = hcWithType(import.meta.env.VITE_BASE_API_URL)
    const response = await client.orders.$post( )

    if (!response.ok) {
      // This returns the type I get when an error is thrown
      const data = await response.json() 
      throw new Error(data)
    }
    return await response.json()
  } catch (error) {
    console.error("Order creation error:", error)
    if (error instanceof Error && error.message.startsWith("Internal Server Error")) {
      // Handle the error response correctly
      console.error("Error message:", error.message)
    } else {
      throw error
    }
  }
}

In this modified version, when an error is thrown, we can handle the error response correctly by checking if the error message starts with "Internal Server Error". If it does, we can handle the error response accordingly.

Conclusion

In this article, we've explored the issue of type inference not working as expected when using the onError middleware in Hono. We've modified the onError middleware to return a JSON response with an error code when an error is thrown, and modified the client-side code to handle the error response correctly. With this modification, the client-side type inference should now work correctly when an error is thrown.
Frequently Asked Questions: Resolving Client-Side Type Inference Issues with onError in Hono

In our previous article, we explored the issue of type inference not working as expected when using the onError middleware in Hono. We also provided a solution to resolve this problem by modifying the onError middleware to return a JSON response with an error code when an error is thrown. In this article, we'll answer some frequently asked questions related to this topic.

Q: Why is type inference not working as expected when using onError?

A: Type inference is not working as expected when using onError because the middleware doesn't return a JSON response with an error code. Instead, it catches and handles the error internally. This means that when we try to infer the type of the response on the client-side, it doesn't work as expected when an error is thrown.

Q: How can I modify the onError middleware to return a JSON response with an error code?

A: You can modify the onError middleware to return a JSON response with an error code by using the c.error method provided by Hono. For example:

import { onError } from "stoker/middlewares"
import { Hono } from "hono"

const app = new Hono()
  .route("/orders", orders)
  .onError((c, err) => {
    return c.error(500, { message: "Internal Server Error" })
  })

Q: What is the difference between c.error and c.json?

A: c.error and c.json are both methods provided by Hono for returning a response. However, c.error is used to return an error response with a specific status code, while c.json is used to return a JSON response with a default status code of 200.

Q: How can I handle the error response correctly on the client-side?

A: You can handle the error response correctly on the client-side by checking if the error message starts with "Internal Server Error". If it does, you can handle the error response accordingly. For example:

export async function createOrder() {
  try {

    const client = hcWithType(import.meta.env.VITE_BASE_API_URL)
    const response = await client.orders.$post( )

    if (!response.ok) {
      // This returns the type I get when an error is thrown
      const data = await response.json() 
      throw new Error(data)
    }
    return await response.json()
  } catch (error) {
    console.error("Order creation error:", error)
    if (error instanceof Error && error.message.startsWith("Internal Server Error")) {
      // Handle the error response correctly
      console.error("Error message:", error.message)
    } else {
      throw error
    }
  }
}

Q: Can I use c.error with a custom status code?

A: Yes, you can use c.error with a custom status code. For example:

import { onError } from "stoker/middlewares"
import { Hono } from "hono"

const app = new Hono()
  .route("/orders", orders)
  .onError((c, err) => {
    return c.error(422, { message: "Validation failed" })
  })

In this example, we're using c.error with a custom status code of 422, which is typically used for validation errors.

Q: How can I log the error message on the client-side?

A: You can log the error message on the client-side by using the console.error method. For example:

export async function createOrder() {
  try {

    const client = hcWithType(import.meta.env.VITE_BASE_API_URL)
    const response = await client.orders.$post( )

    if (!response.ok) {
      // This returns the type I get when an error is thrown
      const data = await response.json() 
      throw new Error(data)
    }
    return await response.json()
  } catch (error) {
    console.error("Order creation error:", error)
    if (error instanceof Error && error.message.startsWith("Internal Server Error")) {
      // Log the error message
      console.error("Error message:", error.message)
    } else {
      throw error
    }
  }
}

In this example, we're logging the error message using console.error.

Conclusion

In this article, we've answered some frequently asked questions related to resolving client-side type inference issues with onError in Hono. We've provided examples and code snippets to help you understand how to modify the onError middleware to return a JSON response with an error code, handle the error response correctly on the client-side, and log the error message.