<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Parastesh</title>
    <description>The latest articles on DEV Community by Parastesh (@20parastesh01).</description>
    <link>https://dev.to/20parastesh01</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1239544%2F3d0bf48b-685d-41cc-95e7-903b8104e831.jpg</url>
      <title>DEV Community: Parastesh</title>
      <link>https://dev.to/20parastesh01</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/20parastesh01"/>
    <language>en</language>
    <item>
      <title>Error Handling in Go REST APIs: From Boilerplate to Beautiful</title>
      <dc:creator>Parastesh</dc:creator>
      <pubDate>Mon, 07 Jul 2025 07:42:50 +0000</pubDate>
      <link>https://dev.to/20parastesh01/error-handling-in-go-rest-apis-from-boilerplate-to-beautiful-1d7c</link>
      <guid>https://dev.to/20parastesh01/error-handling-in-go-rest-apis-from-boilerplate-to-beautiful-1d7c</guid>
      <description>&lt;p&gt;When building REST APIs in Go with frameworks like Fiber, Gin, or Echo, error handling looks simple—until your app grows. Mapping database and validation errors to proper HTTP responses quickly descends into a swamp of repetitive code, scattered switch statements, and too many custom error variables. Here’s my journey from manual error plumbing to a clean, centralized solution.&lt;/p&gt;




&lt;h3&gt;
  
  
  Approach 1: Layered Error Handling — Clear, but Awkward
&lt;/h3&gt;

&lt;p&gt;My first pass looked “proper”—each layer (repository, service, controller) defined errors, wrapped or mapped them up, and finally each endpoint mapped to HTTP codes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsc96uxg3ll5myqsnl5cn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsc96uxg3ll5myqsnl5cn.png" alt="Image description" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Repository:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var ErrNotFound = errors.New("not found")

func (r *Repo) FindUser(id int) (*User, error) {
    // ... DB logic
    if notFound {
        return nil, ErrNotFound
    }
    return user, nil
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Service:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var ErrUserNotFound = errors.New("user not found")

func (s *Service) GetUser(id int) (*User, error) {
    user, err := s.repo.FindUser(id)
    if err == ErrNotFound {
        return nil, ErrUserNotFound
    }
    return user, err
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Controller:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func (h *Handler) GetUser(c *fiber.Ctx) error {
    user, err := h.service.GetUser(id)
    if err == ErrUserNotFound {
        return c.Status(404).JSON(fiber.Map{"error": "User not found"})
    }
    if err != nil {
        return c.Status(500).JSON(fiber.Map{"error": "Internal error"})
    }
    return c.JSON(user)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why this goes wrong:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Switches and if blocks repeat everywhere.&lt;/li&gt;
&lt;li&gt;Adding a new kind of error means updating every layer.&lt;/li&gt;
&lt;li&gt;App logic gets buried in error handling code.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Approach 2: Chain of Responsibility — DRYer, Still Boilerplate
&lt;/h3&gt;

&lt;p&gt;To fix duplication, I moved error mapping into a chain of handlers—each responsible for their error type. (Using chain of responsibility pattern)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7o1cpym95ktq87risnq5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7o1cpym95ktq87risnq5.png" alt="Image description" width="800" height="526"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Example handler:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type ErrorHandler interface {
    Handle(err error) (*AppError, bool)
    SetNext(next ErrorHandler)
}

type NotFoundHandler struct {
    next ErrorHandler
}
func (h *NotFoundHandler) Handle(err error) (*AppError, bool) {
    if errors.Is(err, ErrUserNotFound) {
        return &amp;amp;AppError{Code: 404, Message: "Not found"}, true
    }
    if h.next != nil {
        return h.next.Handle(err)
    }
    return nil, false
}
func (h *NotFoundHandler) SetNext(next ErrorHandler) { h.next = next }
func NewErrorChain() ErrorHandler {
    chain := &amp;amp;NotFoundHandler{}
chain.SetNext(&amp;amp;ValidationHandler{})
chain.SetNext(&amp;amp;DefaultHandler{}) // ... etc

    return chain
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Using the chain:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;user, err := repository.GetUser(id)
if err != nil {
chain := NewErrorChain()
repositoryError := errHandler.Handle(err)
    return repositoryError
}
return c.JSON(user)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;This improved things:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Error mapping was reusable and composable.&lt;/li&gt;
&lt;li&gt;Handlers were easy to test.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;But:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You still set up the chain for every endpoint.&lt;/li&gt;
&lt;li&gt;Most of the time, everyone used the same error-handler chain.&lt;/li&gt;
&lt;li&gt;Error responses were handled in the controller, cluttering its main logic.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Approach 3: Central Error Middleware — Simple &amp;amp; Scalable
&lt;/h3&gt;

&lt;p&gt;Why Middleware Wins&lt;br&gt;
Eventually, I realized—error mapping is cross-cutting!&lt;br&gt;
The controller should focus on business logic, not error-&amp;gt;HTTP mapping. If all errors can be mapped centrally after the handler returns, the controller is blissfully unaware.&lt;br&gt;
You just write a middleware that looks for errors from handlers and returns a consistent response.&lt;br&gt;
Here’s how to do it in Fiber, Gin, and Echo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6r172k99ica0w681t0n7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6r172k99ica0w681t0n7.png" alt="Image description" width="800" height="793"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Shared Error Mapping Function.&lt;br&gt;
Let’s use a shared function in all three examples:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Fallback Controller:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// AppError represents an HTTP error
type AppError struct {
    Code    int
    Message string
}

func mapErrorToAppError(err error) *AppError {
    switch {
    case errors.Is(err, ErrUserNotFound):
        return &amp;amp;AppError{Code: 404, Message: "User not found"}
    case errors.Is(err, ErrValidation):
        return &amp;amp;AppError{Code: 422, Message: "Invalid input"}
    default:
        return &amp;amp;AppError{Code: 500, Message: "Internal server error"}
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fiber&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func FallbackMiddleware() fiber.Handler {
    return func(c *fiber.Ctx) error {
        err := c.Next()
        if err == nil {
            return nil
        }
        appErr := mapErrorToAppError(err)
        return c.Status(appErr.Code).JSON(fiber.Map{
            "error": appErr.Message,
        })
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Usage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;api := router.Group("/api")
api.Use(FallbackMiddleware())

Controller:
func (h *Handler) GetUser(c *fiber.Ctx) error {
    user, err := h.service.GetUser(id)
    if err != nil {
        return err
    }
    return c.JSON(user)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Gin&lt;/strong&gt;&lt;br&gt;
Gin makes this elegant with its built-in error context and global middleware system.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func ErrorMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        c.Next() // Process handler

        err := c.Errors.Last()
        if err == nil {
            return
        }
        appErr := mapErrorToAppError(err.Err)
        c.JSON(appErr.Code, gin.H{"error": appErr.Message})
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Usage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;r := gin.Default()
r.Use(ErrorMiddleware())

Controller:
func (h *Handler) GetUser(c *gin.Context) {
    user, err := h.service.GetUser(id)
    if err != nil {
        c.Error(err) // Just attach error to context
        return
    }
    c.JSON(http.StatusOK, user)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Echo&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Echo middleware completion is equally clean:
func ErrorMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
    return func(c echo.Context) error {
        err := next(c)
        if err == nil {
            return nil
        }
        appErr := mapErrorToAppError(err)
        return c.JSON(appErr.Code, map[string]string{
            "error": appErr.Message,
        })
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Usage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;e := echo.New()
e.Use(ErrorMiddleware)

Controller:
func (h *Handler) GetUser(c echo.Context) error {
    user, err := h.service.GetUser(id)
    if err != nil {
        return err // Let the middleware map the error
    }
    return c.JSON(http.StatusOK, user)
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Conclusion: Less Code, Fewer Bugs, More Focus
&lt;/h3&gt;

&lt;p&gt;After all my experiments, middleware-based centralized error handling is by far the cleanest, most maintainable solution I’ve tried for Go APIs.&lt;br&gt;
Repositories, Services and Controllers: Only return errors.&lt;br&gt;
Middleware: Decides response logic in one place.&lt;br&gt;
Easy to add, test, and extend.&lt;/p&gt;

</description>
      <category>errors</category>
      <category>go</category>
      <category>fallbackcontroller</category>
      <category>goerrorhandling</category>
    </item>
    <item>
      <title>Test Driven Development(TDD) as a Junior Back-End Developer (part1).</title>
      <dc:creator>Parastesh</dc:creator>
      <pubDate>Tue, 20 Feb 2024 14:54:29 +0000</pubDate>
      <link>https://dev.to/20parastesh01/test-driven-developmenttdd-as-a-junior-back-end-developer-part1-4hob</link>
      <guid>https://dev.to/20parastesh01/test-driven-developmenttdd-as-a-junior-back-end-developer-part1-4hob</guid>
      <description>&lt;p&gt;This article is about my experiences with TDD and a guideline I created for myself. It's my personal path from fearing writing tests to writing every feature using TDD.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;What This Document Does Not Cover:&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The definition of TDD&lt;/li&gt;
&lt;li&gt;Types of tests&lt;/li&gt;
&lt;li&gt;Tools for writing tests&lt;/li&gt;
&lt;li&gt;Mocking, faking, etc. (to be discussed in Part 2)&lt;/li&gt;
&lt;li&gt;Third parties or other services being used in the service you want to test which needs to be mocked.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I had several questions at first. I still do, but they are not stopping me now. I couldn’t even write a single word.&lt;/p&gt;

&lt;p&gt;Where should I start? What should I write when a test file is in front of me, waiting for me to type my code?&lt;/p&gt;

&lt;p&gt;Which type of tests should I use?&lt;/p&gt;

&lt;p&gt;How should I write the scenarios?&lt;/p&gt;

&lt;p&gt;How can I predict how I am going to write the feature?&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Seeking Inspiration from Examples&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;I tried to see as many samples as I could to form an initial picture in my mind about tests.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Writing Scenarios&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Cost (which includes time) is one of the most important factors while writing code. That is one of the reasons we write tests. But "you have to choose your battles" instead of battling with everything. So, you must make an equilibrium between time and quality by choosing a suitable test type.&lt;/p&gt;

&lt;p&gt;At this point, I didn’t know whether to choose Unit Tests or Feature Tests. A Feature Test, for example, for creating a user is a single path starting from a route (localhost:8000/user/create) to the logic until the user is being created successfully or not.&lt;/p&gt;

&lt;p&gt;What I am dealing with mostly are CRUDs. Using TDD for most of the CRUD codes needs Feature Test.&lt;br&gt;
Drafting Test Scenarios&lt;br&gt;
You are facing a new task. You may have no idea how you want to write the code. Start with what is almost obvious: The Feature. Creating a user, updating a price, deleting an order, getting a list of foods.&lt;/p&gt;

&lt;p&gt;Write what you expect:&lt;/p&gt;

&lt;p&gt;It can create a user.&lt;br&gt;
It can update the price.&lt;br&gt;
It can delete an order.&lt;br&gt;
It can get a list of foods.&lt;br&gt;
They may also fail too, right? Look for the failing conditions.&lt;/p&gt;

&lt;p&gt;It cannot create a user with an existing phone number.&lt;br&gt;
It cannot update the price with an invalid price format.&lt;br&gt;
...&lt;br&gt;
Voila! You have your scenarios. For the format, check the team and framework conventions.&lt;/p&gt;

&lt;p&gt;Write the scenarios in your test file as methods with no content.&lt;/p&gt;

&lt;p&gt;laravel example:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftvyzryj0ftjststi5sjp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftvyzryj0ftjststi5sjp.png" alt="Image description" width="800" height="250"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6ulalh7uj8gcvj2tsd1u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6ulalh7uj8gcvj2tsd1u.png" alt="Image description" width="800" height="250"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Detailing Your Tests&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Now that you have your scenarios, it is time to write the test contents. Slow down here a little bit if you have a challenge like I did. You are writing a path (Feature Test).&lt;/p&gt;

&lt;p&gt;Write the beginning and ending points first. The beginning point is the route; the ending point is the response, whether it is an HTTP status code or content.&lt;br&gt;
Well, you have come to the main part of the road.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Identifying Prerequisites&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Now, you must see what the prerequisites are. It can be a pre-created user (for failing tests of creating a user, for updating a price or for deleting an order) or a pre-created product you want to update its price.&lt;/p&gt;

&lt;p&gt;You successfully have failing tests! It is time to pass these tests. You may face the need for making changes that may affect your code; change the test first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep this cycle:&lt;br&gt;
Write the test that fails, write the code that passes the test.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That’s it! I would be happy to read your experiences too.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
