Fetch And Send Data logo

Fetch And Send Data

OrganizationPopular
dotnet
fetch-and-send-data

Call APIs, load data into components, and handle the async lifecycle in Blazor. USE FOR fetching data from a backend, submitting data to an API, displaying loading/error states, registering HttpClient, building service abstractions for Auto/WebAssembly render modes. DO NOT USE for form validation (see collect-user-input), prerendering persistence (see support-prerendering), or project scaffolding (see create-blazor-project).

Overview

Publisherdotnet
Repositoryskills
Skill namefetch-and-send-data
Stars
5.4K
Forks
416
Bundled files
Instructions only
LicenseMIT
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • Self-contained

    Everything the model needs lives in the instructions — no extra files to sync.

  • Open source

    Published by dotnet on GitHub. Read the source before you install it.

Installation

Install the Fetch And Send Data AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/dotnet/skills.git /tmp/skills
mkdir -p .claude/skills
cp -r /tmp/skills/plugins/dotnet-blazor/skills/fetch-and-send-data .claude/skills/fetch-and-send-data
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Fetch And Send Data in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Fetch And Send Data on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Fetch And Send Data is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

Fetch and Send Data

Step 1 — Read AGENTS.md

Check Interactivity Mode and Scope:

ModeData access
None (Static SSR)Server-side: inject services/DbContext. Use [StreamRendering] for loading UX.
ServerServer-side: inject services/DbContext. Guard prerender with ??= + [PersistentState].
WebAssemblyBrowser-side: HttpClient only. No direct server access.
AutoBoth server and browser. Always go through an API.

Step 2 — Register HttpClient

Only needed when calling external APIs from Server, or always for WebAssembly/Auto. Server components accessing their own database should inject DbContext or a service directly.

csharp
// Named client — requires Microsoft.Extensions.Http NuGet
builder.Services.AddHttpClient("CatalogAPI", client =>
{
    client.BaseAddress = new Uri("https://api.example.com/");
});

// Typed client
builder.Services.AddHttpClient<CatalogClient>(client =>
    client.BaseAddress = new Uri("https://api.example.com/"));

For WebAssembly/Auto with prerendering, register in both server and .Client Program.cs.

Step 3 — Fetch Data

Simple load

razor
@page "/products"
@inject CatalogClient Catalog

@if (products is null)
{
    <p>Loading…</p>
}
else
{
    @foreach (var p in products)
    {
        <p>@p.Name — @p.Price.ToString("C")</p>
    }
}

@code {
    private Product[]? products;

    protected override async Task OnInitializedAsync()
    {
        products = await Catalog.GetProductsAsync();
    }
}

No error handling needed in the simplest case — wrap the component usage in <ErrorBoundary> at the parent/layout level to catch unhandled exceptions.

Static SSR — StreamRendering

Without [StreamRendering], the user sees nothing until OnInitializedAsync completes:

razor
@attribute [StreamRendering]

Only affects Static SSR. No effect on interactive components.

Prerendering guard

Prerendering calls OnInitializedAsync twice. Skip the duplicate:

csharp
[PersistentState] private Product[]? products;

protected override async Task OnInitializedAsync()
{
    products ??= await Catalog.GetProductsAsync();
}

See the support-prerendering skill for details.

Step 4 — Handle Errors

Use <ErrorBoundary> as the default error strategy. It provides a consistent error experience across all components without any per-component catch logic. Wrap component usage at the layout or parent level:

razor
<ErrorBoundary>
    <ChildContent>
        <ProductList />
    </ChildContent>
    <ErrorContent>
        <div class="alert alert-danger">Something went wrong. Please refresh.</div>
    </ErrorContent>
</ErrorBoundary>

Non-cancellation exceptions (HttpRequestException, etc.) propagate to ErrorBoundary automatically — no catch blocks needed in the component.

Cancellation is special

ComponentBase silently swallows all OperationCanceledException — both self-initiated (disposal, parameter change) and external (HttpClient timeout). ErrorBoundary never sees them. This means:

  • Self-cancellation → silently ignored. Correct behavior, no action needed.
  • External cancellation (timeout) → also silently swallowed. Component gets stuck in loading state. Usually acceptable — timeouts are rare.

When to add in-component error handling

Only add catch blocks when the component needs behavior ErrorBoundary can't provide — typically retries or timeout-specific messages. Even then, only catch what you need:

csharp
// Catch only external cancellation (timeouts) — everything else flows to ErrorBoundary
catch (OperationCanceledException ex) when (!cancellationToken.IsCancellationRequested)
{
    Logger.LogWarning(ex, "Request timed out for category {CategoryId}", CategoryId);
    error = "The request timed out. Please try again.";
}

If the component also needs to handle general errors with a retry button instead of letting ErrorBoundary take over:

csharp
catch (OperationCanceledException ex) when (!cancellationToken.IsCancellationRequested)
{
    Logger.LogWarning(ex, "Request timed out for category {CategoryId}", CategoryId);
    error = "The request timed out. Please try again.";
}
catch (Exception ex)
{
    Logger.LogError(ex, "Failed to load products for category {CategoryId}", CategoryId);
    error = "Unable to load products. Please try again.";
}

Rules

  • Never display exception.Message — it may contain PII, connection strings, or internal details. Use hardcoded user-friendly messages.
  • Always log through ILogger — the real exception goes to the logging pipeline.
  • Services must accept CancellationToken — pass it to every async call so work stops when the component cancels.

Step 5 — Parameter-Driven Reloading

When data depends on a route or query parameter that changes (e.g., navigating between /products/1 and /products/2), use OnParametersSetAsync with a guard to skip reloads for parameters that don't affect data.

Pattern: cancel-and-reload with stale data overlay

razor
@page "/products/{CategoryId:int}"
@implements IAsyncDisposable
@inject ProductService ProductService
@inject ILogger<Products> Logger

@if (error is not null)
{
    <div class="alert alert-danger">
        <p>@error</p>
        <button @onclick="LoadAsync">Retry</button>
    </div>
}
else if (products is null)
{
    <p>Loading…</p>
}
else
{
    @if (isLoading)
    {
        <p><em>Refreshing…</em></p>
    }
    @foreach (var p in products)
    {
        <p>@p.Name — @p.Price.ToString("C")</p>
    }
}

@code {
    [Parameter] public int CategoryId { get; set; }
    [SupplyParameterFromQuery] public string? ViewMode { get; set; } // UI-only

    private CancellationTokenSource? cts;
    private int? loadedCategoryId;
    private List<Product>? products;
    private bool isLoading;
    private string? error;

    protected override async Task OnParametersSetAsync()
    {
        if (CategoryId == loadedCategoryId)
        {
            return; // Only ViewMode changed — no reload
        }

        loadedCategoryId = CategoryId;
        await LoadAsync();
    }

    private async Task LoadAsync()
    {
        if (cts is not null)
        {
            await cts.CancelAsync();
            cts.Dispose();
        }

        cts = new CancellationTokenSource();
        var cancellationToken = cts.Token; // Capture locally before await

        error = null;
        isLoading = true;

        try
        {
            var result = await ProductService.GetByCategoryAsync(CategoryId, cancellationToken);
            products = result;
        }
        catch (OperationCanceledException ex) when (!cancellationToken.IsCancellationRequested)
        {
            Logger.LogWarning(ex, "Timed out loading category {CategoryId}", CategoryId);
            error = "The request timed out. Please try again.";
        }
        finally
        {
            isLoading = false;
        }
    }

    public async ValueTask DisposeAsync()
    {
        if (cts is not null)
        {
            await cts.CancelAsync();
            cts.Dispose();
        }
    }
}

Key details:

  • Guard with tracked value: loadedCategoryId skips reloads when only UI parameters change.
  • Capture the token locally before the await — the CTS field may be replaced by a concurrent parameter change.
  • Don't null out products on subsequent loads — keep existing data visible with an isLoading overlay.
  • IAsyncDisposable cancels pending work when the user navigates away.

Step 6 — Send Data

csharp
var response = await http.PostAsJsonAsync("products", newProduct);
response.EnsureSuccessStatusCode();

var response = await http.PutAsJsonAsync($"products/{id}", updated);
response.EnsureSuccessStatusCode();

var response = await http.DeleteAsync($"products/{id}");
response.EnsureSuccessStatusCode();

Disable the submit button while saving to prevent duplicate requests. Show a saving indicator.

Step 7 — Service Abstraction for Auto or WebAssembly with Prerendering

When components run in both server and browser (Auto mode, or WebAssembly with prerendering), abstract data access behind an abstract base class:

csharp
public abstract class ProductServiceBase
{
    public abstract Task<Product[]> GetAllAsync(CancellationToken ct = default);
}

// Server — direct database access
public class ServerProductService(AppDbContext db) : ProductServiceBase
{
    public override async Task<Product[]> GetAllAsync(CancellationToken ct = default) =>
        await db.Products.ToArrayAsync(ct);
}

// Client — calls API
public class ClientProductService(HttpClient http) : ProductServiceBase
{
    public override async Task<Product[]> GetAllAsync(CancellationToken ct = default) =>
        await http.GetFromJsonAsync<Product[]>("api/products", ct) ?? [];
}

Register the appropriate implementation in each project's Program.cs. Components inject the abstract base class.

Don'ts

  • Don't call APIs in constructors — use OnInitializedAsync.
  • Don't use OnParametersSetAsync unless data depends on a changing parameter. Use OnInitializedAsync for initial loads.
  • Don't inject DbContext in WebAssembly/Auto components — no database in the browser.
  • Don't call your own server via HttpClient — inject the service directly.
  • Don't display exception.Message to users — PII risk. Log it, show a generic message.
  • Don't catch OperationCanceledException for self-cancellationComponentBase handles it.

Frequently asked questions

What does the Fetch And Send Data AI skill do?

Call APIs, load data into components, and handle the async lifecycle in Blazor. USE FOR fetching data from a backend, submitting data to an API, displaying loading/error states, registering HttpClient, building service abstractions for Auto/WebAssembly render modes. DO NOT USE for form validation (see collect-user-input), prerendering persistence (see support-prerendering), or project scaffolding (see create-blazor-project).

Why use Fetch And Send Data on TypingMind?

Because you install it once and use it with any model. Fetch And Send Data is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Fetch And Send Data in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/dotnet/skills/tree/main/plugins/dotnet-blazor/skills/fetch-and-send-data. TypingMind reads its SKILL.md and installs it as a skill you can enable per chat.

Which AI models can use Fetch And Send Data?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Fetch And Send Data?

As many as you like. As long as a model supports skills, you can use Fetch And Send Data with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Fetch And Send Data AI skill free?

Yes. It is published on GitHub by dotnet under the MIT license. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇