Loading...
Loading...
Set up OpenAPI client for authenticated API calls in Umbraco backoffice (REQUIRED for custom APIs)
npx skill4agent add umbraco/umbraco-cms-backoffice-skills umbraco-openapi-clientfetch()[BackOfficeRoute]@hey-api/openapi-ts@hey-api/client-fetch// Composers/MyApiComposer.cs
using Asp.Versioning;
using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using Umbraco.Cms.Api.Common.OpenApi;
using Umbraco.Cms.Core.Composing;
using Umbraco.Cms.Web.Common.ApplicationBuilder;
namespace MyExtension.Composers;
public class MyApiComposer : IComposer
{
public void Compose(IUmbracoBuilder builder)
{
// Register the API and Swagger
builder.Services.AddSingleton<ISchemaIdHandler, MySchemaIdHandler>();
builder.Services.AddTransient<IConfigureOptions<SwaggerGenOptions>, MySwaggerGenOptions>();
builder.Services.Configure<UmbracoPipelineOptions>(options =>
{
options.AddFilter(new UmbracoPipelineFilter(Constants.ApiName)
{
SwaggerPath = $"/umbraco/swagger/{Constants.ApiName.ToLower()}/swagger.json",
SwaggerRoutePrefix = $"{Constants.ApiName.ToLower()}",
});
});
}
}
// Swagger schema ID handler
public class MySchemaIdHandler : SchemaIdHandler
{
public override bool CanHandle(Type type)
=> type.Namespace?.StartsWith("MyExtension") ?? false;
}
// Swagger generation options
public class MySwaggerGenOptions : IConfigureOptions<SwaggerGenOptions>
{
public void Configure(SwaggerGenOptions options)
{
options.SwaggerDoc(
Constants.ApiName,
new OpenApiInfo
{
Title = "My Extension API",
Version = "1.0",
});
}
}
// Constants
public static class Constants
{
public const string ApiName = "myextension";
}Client/package.json{
"scripts": {
"generate-client": "node scripts/generate-openapi.js https://localhost:44325/umbraco/swagger/myextension/swagger.json"
},
"devDependencies": {
"@hey-api/client-fetch": "^0.10.0",
"@hey-api/openapi-ts": "^0.66.7",
"chalk": "^5.4.1",
"node-fetch": "^3.3.2"
}
}Client/scripts/generate-openapi.jsimport fetch from "node-fetch";
import chalk from "chalk";
import { createClient, defaultPlugins } from "@hey-api/openapi-ts";
console.log(chalk.green("Generating OpenAPI client..."));
const swaggerUrl = process.argv[2];
if (swaggerUrl === undefined) {
console.error(chalk.red(`ERROR: Missing URL to OpenAPI spec`));
process.exit(1);
}
// Ignore self-signed certificates on localhost
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
console.log(`Fetching OpenAPI definition from ${chalk.yellow(swaggerUrl)}`);
fetch(swaggerUrl)
.then(async (response) => {
if (!response.ok) {
console.error(chalk.red(`ERROR: ${response.status} ${response.statusText}`));
return;
}
await createClient({
input: swaggerUrl,
output: "src/api",
plugins: [
...defaultPlugins,
{
name: "@hey-api/client-fetch",
bundle: true,
exportFromIndex: true,
throwOnError: true,
},
{
name: "@hey-api/typescript",
enums: "typescript",
},
{
name: "@hey-api/sdk",
asClass: true,
},
],
});
console.log(chalk.green("Client generated successfully!"));
})
.catch((error) => {
console.error(`ERROR: ${chalk.red(error.message)}`);
});// src/entrypoints/entrypoint.ts
import type { UmbEntryPointOnInit, UmbEntryPointOnUnload } from "@umbraco-cms/backoffice/extension-api";
import { UMB_AUTH_CONTEXT } from "@umbraco-cms/backoffice/auth";
import { client } from "../api/client.gen.js";
export const onInit: UmbEntryPointOnInit = (host, _extensionRegistry) => {
// CRITICAL: Configure the OpenAPI client with authentication
host.consumeContext(UMB_AUTH_CONTEXT, (authContext) => {
if (!authContext) return;
const config = authContext.getOpenApiConfiguration();
client.setConfig({
baseUrl: config.base,
credentials: config.credentials,
auth: config.token, // This provides the bearer token!
});
console.log("API client configured with auth");
});
};
export const onUnload: UmbEntryPointOnUnload = (_host, _extensionRegistry) => {
// Cleanup if needed
};npm run generate-client// In your workspace context, repository, or data source
import { MyExtensionService } from "../api/index.js";
// The client handles auth automatically!
const response = await MyExtensionService.getItems({
query: { skip: 0, take: 50 },
});
const item = await MyExtensionService.getItem({
path: { id: "some-guid" },
});
await MyExtensionService.createItem({
body: { name: "New Item", value: 123 },
});npm run generate-clientsrc/api/types.gen.tssdk.gen.tsclient.gen.tsindex.ts// This will get 401 Unauthorized!
const response = await fetch('/umbraco/myextension/api/v1/items');// Still fails - cookies don't work for Management API
const response = await fetch('/umbraco/myextension/api/v1/items', {
credentials: 'include'
});// Client is configured with bearer token in entry point
const response = await MyExtensionService.getItems();examples/notes-wiki/Client/examples/tree-example/Client/Composers/MyApiComposer.csClient/scripts/generate-openapi.jsClient/src/entrypoints/entrypoint.tsClient/src/api/