# RimWorld REST API > Auto-generated API documentation # API documentation # API Reference **Version**: 1.8.2\ **Endpoints total count**: 145 ### Строительство Контроллер **Builder Controller** предоставляет инструменты для программного построения и деконструкции. Он позволяет копировать, вставлять и создавать чертежи областей карты. ______________________________________________________________________ #### POST ```` ``` /api/v1/builder/copy ``` ```` Скопировать указанную область карты, включая здания, объекты и местность. Скопированные данные можно использовать для вставки через [`/api/v1/builder/paste`](#apiv1builderpaste). **Example:** ``` curl --request POST \ --url http://localhost:8765/api/v1/builder/copy \ --header 'content-type: application/json' \ --data '{ "from": { "x": 100, "z": 100 }, "to": { "x": 110, "z": 110 } }' ``` **Response:** ``` { "map_id": 0, "point_a": {"x": 10, "y": 0, "z": 10}, "point_b": {"x": 16, "y": 0, "z": 16} } ``` **Response:** ``` { "success":true, "data": { "width":7, "height":7, "floors": [ {"def_name":"Concrete","rel_x":1,"rel_z":1}, {"def_name":"Concrete","rel_x":3,"rel_z":2}], "buildings": [ {"def_name":"Wall","stuff_def_name":"WoodLog","rel_x":0,"rel_z":0,"rotation":0}, {"def_name":"Wall","stuff_def_name":"WoodLog","rel_x":3,"rel_z":0,"rotation":0}, {"def_name":"Wall","stuff_def_name":"WoodLog","rel_x":0,"rel_z":1,"rotation":0}, ] }, "errors": [], "warnings": [], "timestamp": "2025-12-11T19:15:20.9851409Z" } ``` ______________________________________________________________________ #### POST ```` ``` /api/v1/builder/paste ``` ```` Вставить ранее скопированную область в новое место на карте. **Example:** ``` curl --request POST \ --url http://localhost:8765/api/v1/builder/paste \ --header 'content-type: application/json' \ --data '{ "at": { "x": 150, "z": 150 } }' ``` **Request:** ``` { "map_id": 0, "position": {"x": 80, "y": 0, "z": 80}, "blueprint": { "width":7, "height":7, "floors": [ {"def_name":"Concrete","rel_x":1,"rel_z":1}, {"def_name":"Concrete","rel_x":3,"rel_z":2}], "buildings": [ {"def_name":"Wall","stuff_def_name":"WoodLog","rel_x":0,"rel_z":0,"rotation":0}, {"def_name":"Wall","stuff_def_name":"WoodLog","rel_x":3,"rel_z":0,"rotation":0}, {"def_name":"Wall","stuff_def_name":"WoodLog","rel_x":0,"rel_z":1,"rotation":0}, ] }, "clear_obstacles": true, } ``` **Response:** ``` { "success":true, "errors":[], "warnings":[], "timestamp":"2025-12-31T12:18:40.2019606Z" } ``` ______________________________________________________________________ #### POST ```` ``` /api/v1/builder/blueprint ``` ```` Вставляет постройку в виде чертежа, создавая задание для поселенцев. **Example:** ``` curl --request POST \ --url http://localhost:8765/api/v1/builder/blueprint \ --header 'content-type: application/json' \ --data '{ "at": { "x": 150, "z": 150 } }' ``` **Request:** ``` { "map_id": 0, "position": {"x": 80, "y": 0, "z": 80}, "blueprint": { "width":7, "height":7, "floors": [ {"def_name":"Concrete","rel_x":1,"rel_z":1}, {"def_name":"Concrete","rel_x":3,"rel_z":2}], "buildings": [ {"def_name":"Wall","stuff_def_name":"WoodLog","rel_x":0,"rel_z":0,"rotation":0}, {"def_name":"Wall","stuff_def_name":"WoodLog","rel_x":3,"rel_z":0,"rotation":0}, {"def_name":"Wall","stuff_def_name":"WoodLog","rel_x":0,"rel_z":1,"rotation":0}, ] }, } ``` **Response:** ``` { "success":true, "errors":[], "warnings":[], "timestamp":"2025-12-31T12:18:40.2019606Z" } ``` # Developers documentation # Creating Extensions This guide covers how to create RIMAPI extensions to add custom functionality, endpoints, and events to the API. Extensions allow other mods to seamlessly integrate with RIMAPI without modifying the core system. ## Extension Architecture Overview Extensions are discovered automatically by RIMAPI and integrated into the API lifecycle: ``` graph TB A[Mod Loads] --> B[ExtensionRegistry Scan] B --> C[Create Extension Instance] C --> D[Register Services] D --> E[Register Events] E --> F[Register Endpoints] F --> G[Ready for Requests] H[Game Events] --> I[Event Registry] I --> J[Extension Event Handlers] J --> K[SSE Broadcasting] L[HTTP Requests] --> M[Extension Endpoints] M --> N[Extension Services] N --> O[Game Integration] ``` ## Basic Extension Structure ### Core Interface All extensions must implement `IRimApiExtension`: ``` public interface IRimApiExtension { string ExtensionId { get; } string ExtensionName { get; } string Version { get; } void RegisterServices(IServiceCollection services); void RegisterEvents(IEventRegistry eventRegistry); void RegisterEndpoints(IExtensionRouter router); } ``` ### Minimal Extension Example TBE ## Extension Discovery RIMAPI automatically discovers extensions by scanning all loaded assemblies for types implementing `IRimApiExtension`. The discovery process: - Runs automatically when RIMAPI initializes - Scans all loaded RimWorld mod assemblies - Creates instances of found extensions - Isolates errors - one broken extension won't affect others - Logs all discovered extensions for debugging ## Service Registration ### Dependency Injection in Extensions Extensions can register their own services using RIMAPI's DI container: ``` public void RegisterServices(IServiceCollection services) { // Singleton - one instance for entire application services.AddSingleton(); // Transient - new instance for each resolution services.AddTransient(); } ``` ### Service Lifetime Guidelines | Lifetime | Use Case | Example | | ------------- | ---------------------------------------------------- | ------------------------------------ | | **Singleton** | Configuration, shared state, event publishers | `MyConfigService`, `EventAggregator` | | **Transient** | Business logic, stateless services, request handlers | `DataProcessor`, `ValidationService` | ### Accessing RIMAPI Core Services Extensions can consume core RIMAPI services through dependency injection: ``` public class MyService : IMyService { private readonly ISseService _sseService; private readonly IGameStateService _gameState; public MyService(ISseService sseService, IGameStateService gameState) { _sseService = sseService; _gameState = gameState; } public void DoSomething() { // Use core RIMAPI services var gameMode = _gameState.GetGameMode(); _sseService.PublishEvent("my-extension.action-completed", new { result = "success" }); } } ``` ## Event System Integration ### Registering Custom Events Extensions can define and publish their own SSE events: ``` public void RegisterEvents(IEventRegistry eventRegistry) { // Register custom events eventRegistry.RegisterEvent("my-extension.item-crafted"); eventRegistry.RegisterEvent("my-extension.quest-completed"); eventRegistry.RegisterEvent("my-extension.error-occurred"); } ``` ### Publishing Events Use the `IEventPublisher` to broadcast events to SSE clients: ``` public class MyCraftingService { private readonly IEventPublisher _eventPublisher; public MyCraftingService(IEventPublisher eventPublisher) { _eventPublisher = eventPublisher; } public void OnItemCrafted(Thing item, Pawn crafter) { _eventPublisher.Publish("my-extension.item-crafted", new { itemId = item.ThingID, itemName = item.Label, crafterName = crafter.Name.ToString(), crafterId = crafter.Id.ToString(), timestamp = DateTime.UtcNow }); } } ``` ### Listening to Game Events Extensions can hook into RimWorld events and publish them as SSE events: ``` public class MyGameEventService { private readonly IEventPublisher _eventPublisher; public MyGameEventService(IEventPublisher eventPublisher) { _eventPublisher = eventPublisher; } public void Initialize() { // Hook into RimWorld events Find.TickManager.TickManagerTick += OnGameTick; Find.Storyteller.incidentQueue.IncidentQueueTick += OnIncidentQueued; } private void OnGameTick() { // Publish periodic events if (Find.TickManager.TicksGame % 60 == 0) // Every 60 ticks { _eventPublisher.Publish("my-extension.game-tick", new { tick = Find.TickManager.TicksGame, time = DateTime.UtcNow }); } } } ``` ## Endpoint Registration TBE ### Controller-Based Endpoints For complex APIs, use controller classes with auto-routing: ``` // Controller will be automatically discovered and routed public class ItemsController { private readonly IItemsService _itemsService; public ItemsController(IItemsService itemsService) { _itemsService = itemsService; } [Get("/item")] public ApiResult GetByItemId(string id) { var item = _itemsService.GetItem(id); if (item == null) return ApiResult.NotFound($"Item {id} not found"); return ApiResult.Success(item); } } ``` ### Endpoint Namespacing All extension endpoints are automatically namespaced to prevent conflicts: Warning Your extension registers: `/items` Becomes available at: `/api/extensions/your-extension-id/items` Example URLs: **GET** `/api/extensions/my-crafting-mod/items` **POST** `/api/extensions/my-crafting-mod/items` **GET** `/api/extensions/my-crafting-mod/items/123` ## Complete Example: Crafting Extension ### Extension Definition ``` public class CraftingExtension : IRimApiExtension { public string ExtensionId => "crafting-mod"; public string ExtensionName => "Advanced Crafting API"; public string Version => "1.0.0"; public void RegisterServices(IServiceCollection services) { services.AddSingleton(); services.AddSingleton(); services.AddTransient(); } public void RegisterEvents(IEventRegistry eventRegistry) { eventRegistry.RegisterEvent("crafting.recipe-started"); eventRegistry.RegisterEvent("crafting.recipe-completed"); eventRegistry.RegisterEvent("crafting.recipe-failed"); eventRegistry.RegisterEvent("crafting.materials-low"); } public void RegisterEndpoints(IExtensionRouter router) { // Manual endpoint registration router.MapGet("/recipes", () => GetRecipes()); router.MapPost("/recipes/{defName}/craft", (string defName) => StartCrafting(defName)); // Controller-based endpoints will be auto-discovered } } ``` ### Supporting Services ``` public class CraftingService : ICraftingService { private readonly IEventPublisher _eventPublisher; private readonly IGameStateService _gameState; public CraftingService(IEventPublisher eventPublisher, IGameStateService gameState) { _eventPublisher = eventPublisher; _gameState = gameState; } public bool StartCrafting(string recipeDefName, string colonistId = null) { try { // Implementation logic here _eventPublisher.Publish("crafting.recipe-started", new { recipe = recipeDefName, colonist = colonistId, timestamp = DateTime.UtcNow }); return true; } catch (Exception ex) { _eventPublisher.Publish("crafting.recipe-failed", new { recipe = recipeDefName, error = ex.Message, timestamp = DateTime.UtcNow }); return false; } } } ``` ## Error Handling and Isolation ### Extension Error Isolation RIMAPI provides error isolation to prevent one broken extension from affecting others: - Exceptions in extension initialization are caught and logged - Failed service registration doesn't affect other extensions - Endpoint errors return proper HTTP status codes - Event publishing failures are logged but don't crash the system ### Proper Error Handling in Extensions ``` public class RobustCraftingService { public ApiResult StartCrafting(string recipeDefName) { try { if (string.IsNullOrEmpty(recipeDefName)) return ApiResult.BadRequest("Recipe definition name is required"); var recipeDef = DefDatabase.GetNamed(recipeDefName, false); if (recipeDef == null) return ApiResult.NotFound($"Recipe {recipeDefName} not found"); // Business logic here return ApiResult.Success(new CraftingResult { Success = true }); } catch (Exception ex) { LogApi.Error($"Crafting failed for {recipeDefName}: {ex}"); return ApiResult.Error($"Crafting failed: {ex.Message}"); } } } ``` ## Testing Your Extension ### Development Testing Checklist - Extension is discovered and registered by RIMAPI - Services are properly registered and resolvable - Events are registered and can be published - Endpoints respond correctly to HTTP requests - Error handling works as expected - No performance impact on the game - Works alongside other popular mods ### Versioning ``` public string Version => "1.2.3"; // Major.Minor.Patch // Update version when: // - Major: Breaking changes to API // - Minor: New features, backward compatible // - Patch: Bug fixes, no API changes ``` ## Next Steps - Explore [practical extension examples](https://ilyachichkov.github.io/RIMAPI/ru/developer_guide/examples/simple_extension.md) - Learn about [creating advanced endpoints](https://ilyachichkov.github.io/RIMAPI/ru/developer_guide/creating_endpoints.md) - Review [extension development best practices](https://ilyachichkov.github.io/RIMAPI/ru/developer_guide/best_practices.md) - Check the [auto-generated API reference](https://ilyachichkov.github.io/RIMAPI/ru/api.html) for available core services # Contributors documentation # RIMAPI Architecture Guide This document provides a comprehensive overview of RIMAPI's architecture, designed to help mod developers understand how the system works and how to extend it effectively. ## Core Architecture Principles RIMAPI is built around several key principles that guide its design: - **Main Thread Safety**: All HTTP processing happens on RimWorld's main thread via queuing to prevent game instability - **Extensibility First**: The core is designed to be extended by other mods through a well-defined extension system - **Modularity**: Components are separated by concern and communicate through interfaces - **Error Isolation**: One broken extension should not affect the core API or other extensions ## System Overview ``` graph TB A[HTTP Client] --> B[HttpListener] B --> C[Request Queue] C --> D[ApiServer] D --> E[Router] E --> F[Controller] F --> G[Services] G --> H[RimWorld API] I[Extension Mod] --> J[ExtensionRegistry] J --> F J --> G K[SSE Client] --> L[SseService] L --> M[Event System] M --> H ``` ## Core Components ### ApiServer The central orchestrator that manages the HTTP server and request processing lifecycle. - **Responsibilities**: HTTP listener management, request queue processing, lifecycle management - **Threading**: Uses async/await for I/O but processes requests on main thread during ticks - **Configuration**: Port binding, request throttling (10 requests/tick), CORS handling ### Dependency Injection Container A custom-built DI system that manages service lifetimes and dependencies. - **ServiceCollection**: Registers services with singleton/transient lifetimes - **ServiceProvider**: Resolves dependencies with constructor injection - **Lifetimes**: - **Singleton**: One instance for the entire application (e.g., ApiServer, SseService) - **Transient**: New instance for each resolution (e.g., most controllers) ### Router Handles HTTP request routing to appropriate controller methods. - **Attribute-Based**: Uses [Get], [Post], [Put], [Delete] attributes for route discovery - **Auto-Registration**: Automatically scans for and registers controller routes - **Pattern Matching**: Supports route parameters and pattern matching - **Extension Namespacing**: Prevents route conflicts between mods ### SseService Manages Server-Sent Events for real-time game updates. - **Connection Management**: Tracks active SSE connections - **Event Broadcasting**: Publishes game events to all connected clients - **Heartbeat**: Regular keep-alive messages to maintain connections - **Extension Support**: Other mods can publish custom events ## Request Lifecycle ``` sequenceDiagram participant C as HTTP Client participant L as HttpListener participant Q as Request Queue participant AS as ApiServer participant R as Router participant CTL as Controller participant S as Services participant RW as RimWorld C->>L: HTTP Request L->>Q: Enqueue Request note over Q: Wait for game tick loop Each Game Tick AS->>Q: Dequeue up to 10 requests Q->>AS: Batch of requests end AS->>R: Route request R->>CTL: Invoke controller method CTL->>S: Call service methods S->>RW: Access game data RW->>S: Return game state S->>CTL: Return data CTL->>R: Return ApiResult R->>AS: HTTP Response AS->>C: Send response ``` ## Extension System Architecture The extension system allows other mods to seamlessly integrate with RIMAPI. ### IRimApiExtension Interface ``` public interface IRimApiExtension { string Name { get; } string Version { get; } void RegisterServices(IServiceCollection services); void RegisterEndpoints(IEndpointRouteBuilder routeBuilder); void Initialize(IServiceProvider serviceProvider); } ``` ### Extension Discovery - **Reflection Scanning**: Automatically discovers types implementing IRimApiExtension - **Isolated Registration**: Each extension registers its own services and routes - **Error Handling**: Failures in one extension don't affect others ### Extension Lifetime ``` graph TD A[Mod Loaded] --> B[ExtensionRegistry Scan] B --> C[Create Extension Instance] C --> D[Register Services] D --> E[Register Endpoints] E --> F[Initialize] F --> G[Ready for Requests] ``` ## Service Layer Architecture The service layer abstracts RimWorld's internal API and provides clean, testable interfaces. ### Current Service Structure Internal - **ISseService**: Real-time event management - **IExtensionRegistry**: Extension discovery and management - **IDocumentationService**: API documentation generation Game - **IGameStateService**: Core game information (mode, storyteller, difficulty) - **IColonistService**: Pawn/colonist management and status - **IMapService**: Map information, terrain, and objects - **IResourceService**: Items, inventory, and resource management - **IResearchService**: Research progress and technology - **IIncidentService**: Events, quests, and incidents ## Data Flow Patterns ### REST API Flow ``` HTTP Request → Router → Controller → Service → RimWorld API → DTO → JSON Response ``` ### SSE Event Flow ``` Game Hook → Event Aggregator → SseService → SSE Client Extension Event → Event Aggregator → SseService → SSE Client ``` ### Extension Integration Flow ``` Extension Mod → IRimApiExtension → ServiceCollection → Router → Available in API ``` ## Error Handling Strategy - **Global Exception Handling**: All exceptions are caught and converted to standardized error responses - **Extension Isolation**: Try-catch blocks around extension method invocations - **Request Timeout Protection**: Maximum processing time per request to prevent hangs - **Circuit Breaker Pattern**: Extensions that repeatedly fail may be temporarily disabled ## Performance Considerations In Development - **Request Throttling**: Limits processing to 10 requests per game tick - **Lazy Initialization**: Heavy resources are initialized on first use - **DTO Optimization**: Data transfer objects minimize serialization overhead - **SSE Batching**: Multiple events may be batched in single messages - **Connection Pooling**: Efficient management of SSE connections ## Modding Integration Points Extensions can integrate at multiple levels: - **Service Layer**: Add new business logic services via DI - **Controller Layer**: Add new REST endpoints with auto-routing - **Event Layer**: Publish custom SSE events ## Next Steps - Learn how to [create your first extension](https://ilyachichkov.github.io/RIMAPI/ru/developer_guide/creating_extensions.html) - Learn how to [add new endpoints](https://ilyachichkov.github.io/RIMAPI/ru/contributors_guide/creating_endpoints.html) - Check the [auto-generated API reference](https://ilyachichkov.github.io/RIMAPI/ru/api.html) # Contributing to RIMAPI Thank you for your interest in contributing to RIMAPI! This guide provides everything you need to know to contribute code, documentation, or ideas to the project. Your contributions help make RIMAPI a more powerful tool for the RimWorld community. ## Ways to Contribute - **Code**: Implement new features, fix bugs, or improve performance. - **Documentation**: Enrich our guides, add examples, or fix typos. Every clarification helps. - **Testing**: Help us find and squash bugs by testing new features in-game. - **Feature Ideas**: Suggest new functionality or improvements by opening an issue. - **Community Support**: Assist other users in GitHub Discussions or on Discord. ## Getting Started: Your First Contribution ### Prerequisites - A licensed copy of RimWorld (1.5 or later). - An IDE for .NET development: - **Visual Studio**: Install the `.NET desktop development` workload. - **VSCode**: Install the `C# Dev Kit` extension (`ms-dotnettools.csdevkit`). - The **.NET Framework 4.8** Developer Pack (or the version targeted by the project). - **Git** for version control. ### Development Setup 1. **Fork & Clone**: - **Fork** the repository on GitHub to create your own copy. - **Clone** your fork to your local machine: ``` git clone https://github.com/Your-Username/RIMAPI.git cd RIMAPI ``` 1. **Configure RimWorld Path**: - The project needs to know where your RimWorld installation is to reference its assemblies. - Locate the `Source/Directory.Build.props` file. - Update the `` property to point to your RimWorld installation directory. 1. **Build the Project**: - Open the `Source/RimApi.sln` solution in your IDE. - Build the solution to ensure all dependencies are resolved and the project compiles correctly. 1. **Create a Feature Branch**: - Create a new branch for your changes. This keeps the `main` branch clean. ``` git checkout -b feature/your-awesome-feature ``` ## Creating a New Endpoint Adding a new endpoint is a great way to start contributing. We have a detailed, step-by-step guide for this. - **Read the [Creating Endpoints](https://ilyachichkov.github.io/RIMAPI/ru/contributors_guide/creating_endpoints.html) guide** to learn about the architecture and the process from DTO creation to controller implementation. ## Development Workflow ### Coding & Architecture Standards - **Follow Existing Patterns**: Strive for consistency with the existing codebase. - **Controllers are Thin**: Controllers should only parse requests and return responses. All business logic belongs in the **service layer**. - **Services Coordinate, Helpers Execute**: Services orchestrate the workflow, while **helpers** mostly contain reusable code. - **Use DTOs**: Never expose RimWorld's internal types in an API response. Map all data to Data Transfer Objects (DTOs) in the `Models` directory. ### Testing Your Changes Before submitting your contribution, please test it thoroughly: 1. The mod loads in RimWorld without errors. 1. The API server starts up correctly. 1. Your new endpoint responds with the correct data and status codes. 1. Run a quick check on a few existing endpoints to ensure you haven't introduced any regressions. 1. Error handling works as expected (e.g., providing an invalid ID). ### Updating Documentation Accurate documentation is as important as the code itself. - If you add or modify an endpoint, you **must** update the documentation in `docs/_endpoints_examples/examples.yml`. - If you add a major new feature, create or update a corresponding guide in the `docs/` directory. ## Submitting a Pull Request - **Push to Your Fork**: ``` git push origin feature/your-awesome-feature ``` - **Open a Pull Request**: - Go to the original RIMAPI repository on GitHub. - Click "New Pull Request" and select your feature branch. - Fill out the pull request template, explaining what your PR does and how you tested it. ## Getting Help - **GitHub Discussions**: For questions, ideas, and general feedback. - **GitHub Issues**: To report a bug or request a specific feature. - **Discord**: Join the official [RimWorld Modding Discord](https://discord.gg/Css9b9BgnM) for real-time help in the `#rimapi` channel. # Creating Endpoints This guide covers how to create REST API endpoints in RIMAPI. You'll learn the patterns for building new endpoints, from the controller to the service layer. ## Architecture Overview RIMAPI uses a layered architecture for handling HTTP requests. Understanding this flow is key to contributing effectively. ``` graph TB A[HTTP Request] --> B[ApiServer]; B --> C{Routing}; C -- Route Found --> D[Controller]; D --> E[Service Layer]; E --> F[Data Helpers]; F --> G[RimWorld API]; G -- Raw Data --> F; F -- Mapped DTOs --> E; E -- ApiResult --> D; D -- HTTP Response --> A; ``` 1. **ApiServer**: The lightweight HTTP server listens for incoming requests. 1. **Routing**: The server matches the request's path and HTTP method to a controller action decorated with `[Get("/path")]` or `[Post("/path")]` attributes. 1. **Controller**: The controller action is the entry point for your endpoint. It parses request parameters, calls the appropriate service, and returns a JSON response. 1. **Service Layer**: This layer contains the core business logic. It coordinates data retrieval and manipulation, often by calling one or more data helpers. 1. **Data Helpers**: Helpers encapsulate direct interactions with the RimWorld API (`Verse` and `RimWorld` namespaces), retrieving raw game data. 1. **DTO Mapping**: Helpers or services are responsible for mapping the raw game data into Data Transfer Objects (DTOs). This ensures a clean separation between the game's internal data structures and the API's public contract. 1. **ApiResult**: The service layer wraps the DTO in a standardized `ApiResult` object, which includes status information (`success`, `errors`, `warnings`). 1. **HTTP Response**: The controller serializes the `ApiResult` into a JSON string and sends it back to the client. ## Step-by-Step: Creating a New Endpoint Let's create a new `GET /api/v1/example` endpoint. ### 1. Define the DTO First, define the Data Transfer Object (DTO) that will represent the data your endpoint returns. Create a new class in an appropriate file under `Source/RIMAPI/RimworldRestApi/Models/`. **`Source/RIMAPI/RimworldRestApi/Models/ExampleDto.cs`** ``` namespace RIMAPI.Models { public class ExampleDto { public string Message { get; set; } public bool IsExample { get; set; } } } ``` ### 2. Create the Service Interface Define the contract for your service in the `Source/RIMAPI/RimworldRestApi/Services/` directory. **`Source/RIMAPI/RimworldRestApi/Services/IExampleService.cs`** ``` using RIMAPI.Core; using RIMAPI.Models; namespace RIMAPI.Services { public interface IExampleService { ApiResult GetExampleMessage(); } } ``` ### 3. Implement the Service Create the concrete implementation of the service. This is where your business logic lives. **`Source/RIMAPI/RimworldRestApi/Services/ExampleService.cs`** ``` using RIMAPI.Core; using RIMAPI.Models; namespace RIMAPI.Services { public class ExampleService : IExampleService { public ApiResult GetExampleMessage() { // In a real scenario, you would call a helper to get RimWorld data. var exampleData = new ExampleDto { Message = "This is a test from the service layer!", IsExample = true }; return ApiResult.Ok(exampleData); } } } ``` ### 4. Create the Controller Create a controller to expose the service logic as an HTTP endpoint. Place it in `Source/RIMAPI/RimworldRestApi/BaseControllers/`. **`Source/RIMAPI/RimworldRestApi/BaseControllers/ExampleController.cs`** ``` using System.Net; using System.Threading.Tasks; using RIMAPI.Core; using RIMAPI.Http; using RIMAPI.Services; namespace RIMAPI.Controllers { public class ExampleController : BaseController { private readonly IExampleService _exampleService; public ExampleController(IExampleService exampleService) { _exampleService = exampleService; } [Get("/api/v1/example")] [EndpointMetadata("An example endpoint to demonstrate functionality.")] public async Task GetExample(HttpListenerContext context) { var result = _exampleService.GetExampleMessage(); await context.SendJsonResponse(result); } } } ``` ### 5. Register Service for Dependency Injection For the `ExampleController` to receive an instance of `IExampleService`, you must register it in the dependency injection container. Open `Source/RIMAPI/RIMAPI_Mod.cs` and add your service to the `ConfigureServices` method: ``` private void ConfigureServices(IServiceCollection services) { // ... other services services.AddSingleton(); // ... other services } ``` ### 6. Document the Endpoint Finally, add an entry for your new endpoint in `docs/_endpoints_examples/examples.yml`. This makes it visible in the API documentation. ```` "/api/v1/example": desc: | An example endpoint that returns a sample message. curl: | **Example:** ```bash curl --request GET \ --url 'http://localhost:8765/api/v1/example' ``` request: "" response: | **Response:** ```json { "success": true, "data": { "Message": "This is a test from the service layer!", "IsExample": true }, "errors": [], "warnings": [], "timestamp": "2025-12-12T12:00:00.0000000Z" } ``` ```` ## Advanced Topics ### Handling Request Data #### Query Parameters Use `RequestParser` to safely extract parameters from the URL. ``` [Get("/api/v1/colonist")] public async Task GetColonist(HttpListenerContext context) { var pawnId = RequestParser.GetIntParameter(context, "id"); var result = _colonistService.GetColonist(pawnId); await context.SendJsonResponse(result); } ``` #### JSON Body in POST Requests For `[Post]` endpoints, you can read the request body and deserialize it into a DTO. ``` [Post("/api/v1/colonist/work-priority")] public async Task SetColonistWorkPriority(HttpListenerContext context) { var body = await context.Request.ReadBodyAsync(); var result = _colonistService.SetColonistWorkPriority(body); await context.SendJsonResponse(result); } ``` ### Caching Responses For data that doesn't change frequently, use the `ICachingService` to improve performance. ``` [Get("/api/v1/colonists")] public async Task GetColonists(HttpListenerContext context) { await _cachingService.CacheAwareResponseAsync( context, key: "/api/v1/colonists", dataFactory: () => Task.FromResult(_colonistService.GetColonists()), expiration: TimeSpan.FromSeconds(30) ); } ``` ## Testing Your Endpoints Once you've built your endpoint, you can test it using tools like [Hoppscotch](https://hoppscotch.io/), [Postman](https://www.postman.com/), or `curl`. ``` # Example with a query parameter curl "http://localhost:8765/api/v1/colonist?id=1020" # Example with a POST request and JSON body curl --request POST \ --url http://localhost:8765/api/v1/colonist/work-priority \ --header 'Content-Type: application/json' \ --data '{ "id": 1020, "work": "Cooking", "priority": 1 }' ``` ## Next Steps - Explore the existing controllers and services to see more examples. - Check the [auto-generated API reference](https://ilyachichkov.github.io/RIMAPI/ru/api.html) for existing endpoints.