Call ASP.NET Core Web API and Minimal API using fetch() -- Part 1

In the previous article we learned how to invoke ASP.NET Core MVC actions using JavaScript fetch(). Now it's time to see how Web APIs and Minimal APIs can be invoked using fetch().

By now you are familiar with the general usage of fetch() for making GET, POST, PUT, and DELETE requests. The process remains the same even for Web APIs and Minimal APIs but there are a few changes. For example, endpoint URLs will now point to Web API or Minimal API.

To see these changes in action, open the same project that we created in the previous parts of this series.

We will add a Web API controller in the project. To do so, add ApiControllers folder to the project and then add an API Controller named CustomersController into it.

Now inject AppDbContext into the controller's constructor as shown below:

private AppDbContext db = null;

public CustomersController(AppDbContext db)
{
    this.db = db;
}    

This code should be familiar to you by now and hence is not discussed in detail again.

There will be in all five actions in the API controller - GetAsync(), GetAsync(id), PostAsync(), PutAsync(), and DeleteAsync(). The GetAsync() actions are shown below:

[HttpGet]
public async Task>IActionResult< GetAsync()
{
    List>Customer< data = 
    await db.Customers.ToListAsync();
    return Ok(data); //200
}


[HttpGet("{id}")]
public async Task>IActionResult< GetAsync(string id)
{
    Customer emp = 
    await db.Customers.FindAsync(id);
    return Ok(emp); //200
}

The above code uses async / await to create the GET actions. Although this code is quite similar to the code written the MVC example, notice that it returns data by wrapping into Ok() methods. The Ok() method sends the data to the client along with HTTP status code 200 (in the MVC example we used JsonResult to send the data back to the client).

The PostAsync() action INSERTs a new Customer into the database and is shown below:

[HttpPost]
public async Task>IActionResult< 
    PostAsync([FromBody] Customer cust)
{
    await db.Customers.AddAsync(cust);
    await db.SaveChangesAsync();
    return CreatedAtAction("Get", new 
    { id = cust.CustomerID }, cust); //201
}    

Here, we return HTTP status code 201 indicating that a resource has been successfully created on the server. We also set the location HTTP header indicating the URL at which the newly created resource can be found. This is done using CreatedAtAction() method. A sample location HTTP header upon a successful insert operation is shown below:

The PutAsync() action deals with UPDATE operation and is shown below:

[HttpPut("{id}")]
public async Task>IActionResult<
PutAsync(string id, [FromBody] Customer cust)
{
    db.Customers.Update(cust);
    await db.SaveChangesAsync();
    return NoContent(); //204
}

In this case we return HTTP status code 204 indicating that there is no content to be sent back to the client. This is done using NoContent() method.

Finally, we add DeleteAsync() action that deals with DELETE operations.

[HttpDelete("{id}")]
public async Task>IActionResult<
DeleteAsync(string id)
{
    Customer cust = await db.Customers.FindAsync(id);
    db.Customers.Remove(cust);
    await db.SaveChangesAsync();
    return NoContent(); //204
}

Here also we return 204 status code to the client after deleting a customer.

This completes the Web API controller. Now we will add Minimal APIs to Program.cs that handle the GET, POST, PUT, and DELETE requests. If you are not familiar with Minimal APIs consider reading my article series on the subject here.

Open Program.cs file and add the five Map* calls just below builder.Build() call as shown below:

app.MapGet("minapi/Customers", 
async (AppDbContext db) => {
    List>Customer< data = await 
        db.Customers.ToListAsync();
    return Results.Ok(data);
});

app.MapGet("minapi/Customers/{id}", 
async (AppDbContext db, string id) => {
    Customer emp = await 
    db.Customers.FindAsync(id);
    return Results.Ok(emp);
});

app.MapPost("minapi/Customers", 
async (AppDbContext db, Customer cust) => {
    await db.Customers.AddAsync(cust);
    await db.SaveChangesAsync();
    return Results.Created
    ($"/minapi/Customers/{cust.CustomerID}", cust);
});

app.MapPut("minapi/Customers", 
async (AppDbContext db, string id, 
Customer cust) => {
    db.Customers.Update(cust);
    await db.SaveChangesAsync();
    return Results.NoContent();
});

app.MapDelete("minapi/Customers/{id}", 
async (AppDbContext db, string id) => {
    Customer cust = await 
    db.Customers.FindAsync(id);
    db.Customers.Remove(cust);
    await db.SaveChangesAsync();
    return Results.NoContent();
});

As far as the CRUD operations are concerned, the minimal API code is quite similar to the Web API code.

Notice that each Map* method consists of endpoint URL and the handler function. The endpoint URL is kept different than Web API controller (CustomersController) to avoid any ambiguity at runtime. Also note that AppDbContext is injected in each endpoint handler since there is no constructor as such. Moreover, the return statement makes use of Results static methods such as Ok(), Created(), and NoContent().

This completes the Customers Web API and minimal API. The following figures show a sample run of the GET requests.

Now that our Web API and minimal API are complete, we can make fetch() calls to invoke them. That's the subject of the next part of this article series.

That's it for now! Keep coding!!


Bipin Joshi is an independent software consultant and trainer by profession specializing in Microsoft web development technologies. Having embraced the Yoga way of life he is also a meditation teacher and spiritual guide to his students. He is a prolific author and writes regularly about software development and yoga on his websites. He is programming, meditating, writing, and teaching for over 27 years. To know more about his ASP.NET online courses go here. More details about his Kriya and Meditation online course are available here.

Posted On : 27 March 2023