How to use JSON Patch in ASP.NET Core Minimal API

How to use JSON Patch in ASP.NET Core Minimal API

JSON Patch is a standardized format for describing changes to a JSON document. It is defined by RFC 6902 and is commonly used with HTTP PATCH requests to update resources without sending the entire document.

A JSON Patch document is itself a JSON array of operations. Each operation specifies:

  • op: the operation to perform
  • path: a JSON Pointer identifying the target location
  • Additional fields such as value or from, depending on the operation
Example:

Original document:

{
  "name": "Alice",
  "age": 30,
  "tags": ["admin"]
}

Patch:

[
  {
    "op": "replace",
    "path": "/age",
    "value": 31
  },
  {
    "op": "add",
    "path": "/tags/1",
    "value": "editor"
  }
]

Result:

{
  "name": "Alice",
  "age": 31,
  "tags": ["admin", "editor"]
}

/tags/1 -> Index 1 is the position immediately after the first element “admin”, so “editor” is inserted there. So it becomes “admin”, “editor”.

Supported operations:

OperationPurpose
addAdd a value to an object or array.
removeRemove a value.
replaceReplace an existing value.
moveMove a value from one location to another.
copyCopy a value from one location to another.
testVerify that a value matches an expected value before continuing.
Examples:

Add a property:

{
  "op": "add",
  "path": "/email",
  "value": "alice@example.com"
}

Remove a property:

{
  "op": "remove",
  "path": "/email"
}

Move a value:

{
  "op": "move",
  "from": "/oldName",
  "path": "/newName"
}

Test before replacing:

[
  {
    "op": "test",
    "path": "/version",
    "value": 5
  },
  {
    "op": "replace",
    "path": "/version",
    "value": 6
  }
]

If the test operation fails, the entire patch fails.

JSON Patch is useful when:

  • Updating REST API resources via HTTP PATCH
  • Synchronizing documents between clients and servers
  • Tracking edits efficiently
  • Applying incremental changes in collaborative applications

It is especially valuable when only a small portion of a large JSON document changes, because it avoids sending the entire document.

JSON Patch vs HTTP Patch

JSON Patch and HTTP PATCH are related, but they are not the same thing.

  • HTTP PATCH is an HTTP method (like GET, POST, PUT, DELETE).
  • JSON Patch is a document format (RFC 6902) that is commonly used as the payload of an HTTP PATCH request.

Relationship:

HTTP PATCH (method)
        │
        ├── JSON Patch (RFC 6902)
        ├── JSON Merge Patch (RFC 7396)
        └── Custom partial update format

Note that a HTTP PATCH request can use different formats for its request body. JSON Patch is just one of them.

HTTP PATCH

HTTP PATCH tells the server: “Update only part of this resource.”

PATCH /users/1 HTTP/1.1
Content-Type: application/json

{
  "age": 31
}

The HTTP specification does not define what the body should look like. That depends on the API.

JSON Patch

JSON Patch defines how to describe changes.

PATCH /users/1 HTTP/1.1
Content-Type: application/json-patch+json

[
  {
    "op": "replace",
    "path": "/age",
    "value": 31
  }
]

JSON PATCH in ASP.NET Core Minimal API

In ASP.NET Core Minimal APIs, you can support JSON Patch (application/json-patch+json) using the Microsoft.AspNetCore.JsonPatch package. JSON Patch follows RFC 6902 and allows partial updates with operations like add, remove, replace, move, copy, and test.

1. Install the package

dotnet add package Microsoft.AspNetCore.JsonPatch.SystemTextJson

2. Create Models

Here we have Department and Employee class where Departments contains one or many Employees.

public class Department
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Manager { get; set; }
    public List<Employee>? Employees { get; set; }
}

public class Employee
{
    public int Id { get; set; }

    public string Name { get; set; }
    public DateTime JoiningDate { get; set; }
    public decimal Salary { get; set; }
}

3. JSON Patch code

This endpoint implements a JSON Patch endpoint in an ASP.NET Core Minimal API. It retrieves a Department from the database, applies the JSON Patch operations, reports any patch errors as validation errors, saves the changes, and returns the updated entity.

app.MapPatch("/customers/{id}", async Task<Results<Ok<Department>, ValidationProblem, NotFound<ProblemDetails>>> (WorkDb db, int id,
    JsonPatchDocument<Department> patchDoc) =>
{
    var department = await db.Departments.Include(c => c.Employees).FirstOrDefaultAsync(c => c.Id == id);
    
    if (department is null)
        return TypedResults.NotFound<ProblemDetails>(new());
    
    if (patchDoc != null)
    {
        Dictionary<string, string[]>? errors = null;
        patchDoc.ApplyTo(department, jsonPatchError =>
        {
            errors ??= new();
            var key = jsonPatchError.AffectedObject.GetType().Name;
            if (!errors.ContainsKey(key))
            {
                errors.Add(key, new string[] { });
            }
            errors[key] = errors[key].Append(jsonPatchError.ErrorMessage).ToArray();
        });

        if (errors != null)
        {
            return TypedResults.ValidationProblem(errors);
        }

        // Only save if there are no errors
        await db.SaveChangesAsync();
    }

    return TypedResults.Ok(department);
})
.Accepts<JsonPatchDocument<Department>>("application/json-patch+json");
Explanation:

Retrieve the Department:

The endpoint retrieves a Department object from the database from the provided id. When no department is found then it returns 404 Not Found response via TypedResults.NotFound() method.

Apply JSON Patch:

The ApplyTo(Object) method executes the JSON Patch operations defined in patchDoc on the retrieved Department object. If any errors occur while applying the patch, such as invalid paths, unsupported operations, or conflicting changes, the provided error-handling delegate is invoked. The delegate captures these errors and stores the corresponding error messages in a dictionary, using the affected object’s type name as the key.

Return validation errors:

If the error-handling delegate detects any issues while applying the patch operations, the endpoint returns a ValidationProblem response using TypedResults.ValidationProblem(errors), which includes the collected error details.

Save and return the Updated Department:

If the patch operations are applied successfully without any errors, the changes are persisted to the database using SaveChangesAsync(). The endpoint then returns the updated Customer object in an OK response through TypedResults.Ok(department).

Example error response

The following example demonstrates the response body of a validation problem returned for a JSON Patch operation when the specified path is invalid:

{
  "type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "errors": {
    "Department": [
      "The target location specified by path segment 'Admin' was not found."
    ]
  }
}

Testing with .http File Editor in Visual Studio

We have 2 Departments and each department containing 2 Employees. See below structure:

[
  {
    "id": 1,
    "name": "Development",
    "manager": "Adam Dorsey",
    "employees": [
      {
        "id": 1,
        "name": "John Doe",
        "joiningDate": "2024-08-04T20:18:50.1041658+05:30",
        "salary": 60000
      },
      {
        "id": 2,
        "name": "Jane Smith",
        "joiningDate": "2025-08-04T20:18:50.1042795+05:30",
        "salary": 55000
      }
    ]
  },
  {
    "id": 2,
    "name": "Support",
    "manager": "Elon Musk",
    "employees": [
      {
        "id": 3,
        "name": "Alice Johnson",
        "joiningDate": "2023-08-04T20:18:50.1042808+05:30",
        "salary": 70000
      },
      {
        "id": 4,
        "name": "Bob Brown",
        "joiningDate": "2022-08-04T20:18:50.104281+05:30",
        "salary": 65000
      }
    ]
  }
]

To replace the 1st Manager name from John Doe to Jack Doe, the Patch request is:

PATCH {{DailyWork_HostAddress}}/departments/1
Content-Type: application/json-patch+json

[
  {
    "op": "replace",
    "path": "/Manager",
    "value": "Jack Doe"
  }
]

We used replace for “op” and given path as “/Manager” with value containing the new name i.e. Jack Doe.

Lets send a Patch request to add a new Employee in the 1st department. The patch request is given below.

PATCH {{DailyWork_HostAddress}}/departments/1
Content-Type: application/json-patch+json

[
  {
    "op": "add",
    "path": "/Employees/-",
    "value": {
      "name": "Yogi S",
      "joiningDate": "2026-08-04T00:00:00",
      "salary": 165000
    }
  }
]

Why /Employees/-? In JSON Patch:

  • /Employees/0 → insert at index 0
  • /Employees/1 → insert at index 1
  • /Employees/- → append to the end of the array

The “-” is defined by the JSON Patch specification to mean “append”.

To insert at index 0:

PATCH {{DailyWork_HostAddress}}/departments/1
Content-Type: application/json-patch+json

[
  {
    "op": "add",
    "path": "/Employees/0",
    "value": {
      "name": "Alice Johnson",
      "joiningDate": "2026-08-04T00:00:00",
      "salary": 65000
    }
  }
]

Update an existing employee – For example, change the first employee’s salary:

PATCH {{DailyWork_HostAddress}}/departments/1
Content-Type: application/json-patch+json

[
  {
    "op": "replace",
    "path": "/Employees/0/Salary",
    "value": 70000
  }
]

Remove the second employee:

PATCH {{DailyWork_HostAddress}}/departments/1
Content-Type: application/json-patch+json

[
  {
    "op": "remove",
    "path": "/Employees/1"
  }
]

More complicated scenarios include replacing Manger name and at the same time adding a new Employee. Check the below request which does this thing in one request only.

PATCH {{DailyWork_HostAddress}}/departments/1
Content-Type: application/json-patch+json

[
  {
    "op": "replace",
    "path": "/Manager",
    "value": "Jack Doe"
  },
  {
    "op": "add",
    "path": "/Employees/-",
    "value": {
      "name": "Yogi S",
      "joiningDate": "2026-08-04T00:00:00",
      "salary": 165000
    }
  }
]

Test in JSON Patch

JSON Patch supports the test operation, which verifies that the value of a specified property matches an expected value. If the values do not match, the operation fails and the server returns an error without applying the patch.

In the below patch request we test if the Department name is “Developement” only then replace manager to “Yogi S.”.

PATCH {{DailyWork_HostAddress}}/departments/1
Content-Type: application/json-patch+json

[
  {
    "op": "test",
    "path": "/Name",
    "value": "Development"
  },
  {
    "op": "replace",
    "path": "/Manager",
    "value": "Yogi S"
  }
]

Suppose we send the Name as “Testing”. See below request.

PATCH {{DailyWork_HostAddress}}/departments/1
Content-Type: application/json-patch+json

[
  {
    "op": "test",
    "path": "/Name",
    "value": "Testing"
  },
  {
    "op": "replace",
    "path": "/Manager",
    "value": "Yogi S"
  }
]

Since here the department name does not match so we get an error response.

{
  "type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "errors": {
    "Department": [
      "The current value 'Development' at path 'Name' is not equal to the test value 'Testing'."
    ]
  }
}

Certainly this is a helpful feature to prevent unwanted changes.

Conclusion

In this tutorial we learned how to use JSON PATCH in ASP.NET Core Minimal API. We also checked all the examples to understand it’s uses. If you find it useful then please share it on your social media account with your friends.

SHARE THIS ARTICLE

  • linkedin
  • reddit
yogihosting

ABOUT THE AUTHOR

I hope you enjoyed reading this tutorial. If it helped you then consider buying a cup of coffee for me. This will help me in writing more such good tutorials for the readers. Thank you. Buy Me A Coffee donate

Leave a Reply

Your email address will not be published. Required fields are marked *