Skip to content

Event Service

This guide provides step-by-step instructions for creating a new APIM API to publish events to Azure Service Bus Topics using the SAIF platform templates.

Aspire Publish Alternative

Starting in Forge 3.3, if your project uses an Aspire AppHost, you can register event services with builder.AddEventService("events") from the SAIF.Platform.Aspire.Hosting package and generate pipeline YAML automatically. See Aspire Publish for details.

๐Ÿ“š Useful Resources

๐Ÿ“‹ Prerequisites

  • โœ… SAIF CLI and TypeSpec installed and updated
  • โœ… Visual Studio Code with TypeSpec extension
  • โœ… Access to SAIF Azure DevOps organization
  • โœ… Permissions for creating pipelines and Azure resources

โšก Step 1: Create Your Project

To create a new event service project, run the following command in your project root directory:

saif new saif-event-service

๐Ÿ”ง Command Options

You can provide all parameters upfront to skip the interactive prompts:

saif new saif-event-service --name <your-app-name> --owner <owner-name>

Parameters:

  • --name: Your application name (will be used in project naming)
  • --owner: The team or individual responsible for the service

๐Ÿ’ก Example

saif new saif-event-service --name myapp --owner Platform

This creates an event service with:

  • Project ID: events-myapp (automatically generated from "events" + name)
  • Application Name: myapp
  • Owner: Platform

โœ… Success Indicators

After running the command, you should see:

  • โœ… New project folder created with your app name
  • โœ… Files generated successfully message
  • โœ… No error messages in the terminal

๐ŸŽฏ What Happens Next?

  1. Navigate into your new project directory: cd <your-project-name>
  2. Verify the project structure matches the layout below
  3. Proceed to Step 2 to configure security keys
๐Ÿ“ Click to see detailed project structure
your-project/
โ”œโ”€โ”€ .azdo/                              # Azure Pipelines configuration
โ”‚   โ”œโ”€โ”€ azure-pipelines-event.yml       # Main deployment pipeline
โ”‚   โ”œโ”€โ”€ azure-pipelines-event-pr.yml    # Pull request validation pipeline
โ”‚   โ””โ”€โ”€ vars/                           # Pipeline variables
โ”œโ”€โ”€ infra/                              # Infrastructure as Code
โ”‚   โ”œโ”€โ”€ bootstrap/                      # Environment bootstrapping
โ”‚   โ””โ”€โ”€ event/                          # Event service infrastructure
โ”‚       โ””โ”€โ”€ openapi/                    # Generated OpenAPI specifications
โ”œโ”€โ”€ src/                                # Source code
โ”‚   โ””โ”€โ”€ SAIF.[AppName].TypeSpec/        # TypeSpec project for API definitions
โ””โ”€โ”€ .gitignore
**Key Components:** - **๐Ÿ”„ `.azdo`**: Contains all Azure Pipelines for PR validation and deployment - **๐Ÿ—๏ธ `infra`**: Contains Terraform configurations for APIM API, Service Bus Topics, and OpenAPI specs - **๐Ÿ’ป `src`**: Contains TypeSpec project for defining API operations and event models

๐Ÿ—๏ธ Step 2: Configure Security Keys

๐Ÿ”‘ What You Need to Provide

Your event service API requires subscription key authentication to protect your Service Bus topics. You need to bring a primary key for each environment (TEST, QA, UAT, PROD) that will serve as the authentication token for consuming applications.

โš ๏ธ Required: You must provide primary keys for each environment before deployment, or the infrastructure deployment will fail.

If you need to generate secure primary keys, use Keeper's password generator. Be sure it only contains alphanumerics (hyphens and underscores are allowed, but the pipeline is sensitive to symbols). It is recommended to use a key length of at least 32 characters, maximum 256.

  1. Configure Azure DevOps Variable Group:
  2. Navigate to Azure DevOps โ†’ Pipelines โ†’ Library
  3. There should already be a group created by the SAIF CLI named after your project ID (e.g., events-myapp), with the environment-specific variables pre-defined.
  4. Add a different primary key for each environment.

โš ๏ธ Critical: If you rename or use a different variable group, its name must match the secretsVariableGroupName value in your .azdo/vars/event-service.yml file.

โœ… Success Indicators

After configuring security keys, you should have:

  • โœ… Variable group created in Azure DevOps Library
  • โœ… Primary keys added for TEST, QA, UAT, and PROD environments (marked as secret)

๐ŸŽฏ What Happens Next?

  1. Your keys are stored securely in Azure DevOps for the deployment pipeline to use
  2. Proceed to Step 3 to define your event structure with TypeSpec

๐Ÿ“ Note: All SAIF event service APIs use mandatory subscription key authentication. Every API call must include a valid subscription key in the Ocp-Apim-Subscription-Key header.

๐Ÿ’ป Step 3: Define Your Events with TypeSpec

๐ŸŽฏ What is TypeSpec?

TypeSpec is a language for defining APIs and data models. It will automatically generate the OpenAPI specifications that configure your event service. Think of it as describing what your events look like so the system can create the right APIs.

๐Ÿ“‚ Open Your TypeSpec Project

cd src/SAIF.[YourAppName].TypeSpec
code .  # Opens in Visual Studio Code

๐Ÿ—๏ธ Understanding the TypeSpec Template

Here's what a complete TypeSpec service looks like with explanations:

import "@typespec/http";
import "@typespec/rest";
import "@typespec/openapi";
import "@typespec/openapi3";
import "@typespec/versioning";
import "@saif/platform-typespec";

using TypeSpec.Http;
using TypeSpec.Rest;
using SAIF.Platform;

@service({
  title: "Your Event Service",        // ๐Ÿ‘ˆ This appears in your API documentation
  version: "1.0"                    // ๐Ÿ‘ˆ Version your API for consumers
})
@useAuth(ApiKeyAuth<ApiKeyLocation.header, "Ocp-Apim-Subscription-Key">)  // ๐Ÿ‘ˆ Requires subscription key
namespace YourEventService;

// ๐Ÿ‘‡ Automatically creates REST endpoints
@autoRoute
interface UserCreatedTopic extends Topic<UserCreatedEvent> {}

// ๐Ÿ‘‡ Define your event structure - customize this for your use case
model UserCreatedEvent is Event<"UserCreated"> {
  userId: string;          // Required field
  email: string;           // Required field
  firstName?: string;      // Optional field (note the ?)
  lastName?: string;       // Optional field
}

Key Concepts:

  • ๐ŸŽฏ Each event type maps to one Service Bus topic (e.g. UserCreatedTopic โ†’ UserCreated topic)
  • ๐Ÿ” The @useAuth decorator makes subscription keys mandatory for all operations
  • โšก The @autoRoute decorator creates REST endpoints from your interface

๐Ÿ”„ Build and Validate

npm install    # Install dependencies
npm run build  # Validate and generate OpenAPI specs

โœ… Success Indicators

After building your TypeSpec definitions, you should see:

  • โœ… No compilation errors in the terminal
  • โœ… OpenAPI files generated in the openapi/ folder
  • โœ… TypeSpec validation passes without warnings

๐ŸŽฏ What Happens Next?

  1. Your TypeSpec is compiled into OpenAPI specifications
  2. These specs define your API structure for the deployment pipeline
  3. Proceed to Step 4 to deploy your event service

๐Ÿš€ Step 4: Deploy Your Event Service

๐ŸŽฏ What This Step Does

The deployment pipeline will take your TypeSpec definitions and security keys to create a fully functional event service in Azure, including the API endpoints and Service Bus topics.

  1. Navigate to Azure DevOps Pipelines
  2. Run the events-<project-name> pipeline (e.g., events-myapp)

The pipeline will:

  • โœ… Compile TypeSpec and generate OpenAPI specifications
  • โœ… Deploy OpenAPI specifications to a dedicated OpenAPI storage account
  • โœ… Deploy APIM API with auto-generated operations
  • โœ… Create Service Bus Topics for event publishing
  • โœ… Configure security policies and activate subscription keys

โœ… Success Indicators

A successful deployment should show:

  • โœ… All pipeline stages completed without errors
  • โœ… Green checkmarks for each deployment environment
  • โœ… No failed tasks in the pipeline logs

๐ŸŒ Access Your OpenAPI Specifications

After deployment, specifications are available at:

https://openapi.saif.com/<project-id>/<environment>/openapi.v<version-number>.yaml

๐Ÿงช Test Your API

curl -X POST "https://app-int-<environment>.saif.com/api/event/<api-name>/<topic-name>" \
  -H "Ocp-Apim-Subscription-Key: your-primary-key" \
  -H "Content-Type: application/json" \
  -H "X-Event-Type: UserCreated" \
  -d '{<your-request-body>}'

๐Ÿ’ก Example

curl -X POST "https://app-int-test.saif.com/api/event/events-myapp/UserCreated" \
  -H "Ocp-Apim-Subscription-Key: 1234-45678" \
  -H "Content-Type: application/json" \
  -H "X-Event-Type: UserCreated" \
  -d '{"userId": "123", "email": "user@example.com"}'

๐ŸŽฏ What Happens Next?

  1. Your event service is live and ready to receive requests from the API
  2. Share your OpenAPI specs with teams that need to consume your events
  3. Monitor your Service Bus topics for incoming events
  4. Distribute subscription keys securely to authorized consuming teams

๐Ÿ“Š Visual Flow

TypeSpec Definition โ†’ OpenAPI Specs โ†’ APIM API โ†’ Service Bus Topics โ†’ Event Consumers
     (Step 3)           (Pipeline)     (Azure)         (Azure)         (Your Teams)

๐Ÿ”ง Troubleshooting

โŒ Deployment fails with "primary_key not found"

  • Solution: Add primary_key secret to Azure DevOps variable group
  • Check: Variable group name exactly matches secretsVariableGroupName in .azdo/vars/event-service.yml
  • Verify: Use environment-specific naming: [TEST]primary_key, ..., [PROD]primary_key

โŒ API calls return 401 Unauthorized

  • Solution: Include Ocp-Apim-Subscription-Key header with correct subscription key value

โŒ API calls return 403 Forbidden

  • Solution: Verify Service Bus topic exists and is not disabled
  • Check: Ensure topic has not been disabled due to quota limits or administrative action

โŒ TypeSpec compilation errors

  • Solution: Run npm install and verify TypeSpec syntax follows SAIF platform conventions