Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
davidortinau avatar

Maui Rest Api

  • 44 installs
  • 163 repo stars
  • Updated July 6, 2026
  • davidortinau/maui-skills

Consumes REST APIs in .NET MAUI apps with HttpClient and System.Text.Json, DI registration, a service pattern, full CRUD, and error handling.

About

Guides consuming REST APIs in .NET MAUI apps using HttpClient with System.Text.Json, DI registration, a service interface/implementation pattern, full CRUD and error handling. A developer uses it when calling backend REST endpoints from a MAUI app.

  • HttpClient with System.Text.Json and DI registration
  • Service interface pattern with full CRUD and error handling

Maui Rest Api by the numbers

  • 44 all-time installs (skills.sh)
  • Ranked #612 of 1,039 Mobile Development skills by installs in the Skillselion catalog
  • Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/davidortinau/maui-skills --skill maui-rest-api

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs44
repo stars163
Last updatedJuly 6, 2026
Repositorydavidortinau/maui-skills

What it does

Consumes REST APIs in .NET MAUI apps with HttpClient and System.Text.Json, DI registration, a service pattern, full CRUD, and error handling.

Files

SKILL.mdMarkdownGitHub ↗

REST API Consumption — Gotchas & Best Practices

Common Mistakes

1. Creating HttpClient per request

// ❌ Creates socket exhaustion — each instance opens a new connection
public async Task<List<Item>> GetItemsAsync()
{
    using var client = new HttpClient();
    var response = await client.GetAsync("https://api.example.com/items");
    // ...
}

// ✅ Register once in DI, inject everywhere
builder.Services.AddSingleton(sp => new HttpClient
{
    BaseAddress = new Uri("https://api.example.com")
});

2. Blocking with .Result or .Wait()

// ❌ Deadlocks on the UI thread
var items = _apiService.GetItemsAsync().Result;

// ✅ Always use async/await
var items = await _apiService.GetItemsAsync();

3. Deserializing before checking status

// ❌ Tries to deserialize error HTML/JSON as your model
var content = await response.Content.ReadAsStringAsync();
var items = JsonSerializer.Deserialize<List<Item>>(content, _jsonOptions);

// ✅ Check status first
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
var items = JsonSerializer.Deserialize<List<Item>>(content, _jsonOptions) ?? [];

4. Hardcoding BaseAddress in service methods

// ❌ Absolute URIs in every method — hard to change, easy to typo
await _httpClient.GetAsync("https://api.example.com/api/items");

// ✅ Set BaseAddress in DI, use relative URIs in methods
await _httpClient.GetAsync("api/items");

5. Missing error handling for network failures

// ❌ Crashes on network timeout, DNS failure, etc.
var items = await _apiService.GetItemsAsync();

// ✅ Catch both network and deserialization errors
try
{
    var items = await _apiService.GetItemsAsync();
}
catch (HttpRequestException ex) { /* network or HTTP error */ }
catch (JsonException ex) { /* malformed response */ }

Platform Pitfalls

⚠️ Clear-text HTTP blocked on emulators/simulators

Local dev servers on http:// are blocked by default. Configure exceptions:

  • Android: needs network_security_config.xml with cleartextTrafficPermitted="true" for 10.0.2.2
  • iOS/Mac Catalyst: needs NSAllowsLocalNetworking in Info.plist

⚠️ Android emulator uses 10.0.2.2 for localhost

The Android emulator maps 10.0.2.2 to the host machine. localhost refers to the emulator itself.

// ❌ On Android emulator, this hits the emulator, not your dev machine
new Uri("http://localhost:5000")

// ✅ Use the emulator's host loopback address
new Uri("http://10.0.2.2:5000")

iOS simulators use localhost directly.

⚠️ Inconsistent JSON casing

APIs typically use camelCase; C# properties are PascalCase. Without JsonSerializerOptions, deserialization silently returns default values.

// ❌ Properties stay null/default — no error thrown
JsonSerializer.Deserialize<Item>(content);

// ✅ Configure casing policy
private static readonly JsonSerializerOptions _jsonOptions = new()
{
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
    PropertyNameCaseInsensitive = true
};

Decision Framework

ScenarioError handling approach
Failure is unexpected (auth'd endpoints)EnsureSuccessStatusCode() — throws HttpRequestException
Need to branch on status codesCheck IsSuccessStatusCode or response.StatusCode
Network may be unreliable (mobile)Wrap in try/catch for HttpRequestException
Response format may varyAlso catch JsonException

Checklist

  • [ ] HttpClient registered as singleton or via IHttpClientFactory — never created per-request
  • [ ] BaseAddress set in DI; service methods use relative URIs
  • [ ] JsonSerializerOptions with CamelCase policy applied consistently
  • [ ] IsSuccessStatusCode or EnsureSuccessStatusCode() checked before deserializing
  • [ ] try/catch for HttpRequestException and JsonException in ViewModel calls
  • [ ] All API calls use async/await — no .Result or .Wait()
  • [ ] Service interface pattern used so ViewModels depend on abstractions
  • [ ] Android clear-text config for local dev (10.0.2.2)
  • [ ] iOS NSAllowsLocalNetworking for local dev

Related skills

Mobile Developmentfrontendintegrations

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.