File size: 3,368 Bytes
63522a5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# API Services Guide

## Overview

Services are the abstraction layer between frontend components and backend APIs. Local agent-server API access should use `@openhands/typescript-client` classes directly, with shared connection options from `src/api/agent-server-client-options.ts` for the active local backend host, session API key, and workspace defaults.

Cloud-specific APIs should use the cloud service modules/proxy helpers instead of local agent-server clients.

Each service is a plain object with async methods.

## Structure

Each service lives in its own directory:

```
src/api/
└── feature-service/
    β”œβ”€β”€ feature-service.api.ts    # Service methods
    └── feature.types.ts          # Types and interfaces
```

## Creating a Service

Use an object literal with named export. Use object destructuring for parameters to make calls self-documenting. Prefer typed `@openhands/typescript-client` classes over generic HTTP calls. If a needed endpoint is missing, add it to `@openhands/typescript-client` first.

```typescript
// feature-service/feature-service.api.ts
import { FeatureClient } from "@openhands/typescript-client/clients";
import { getAgentServerClientOptions } from "../agent-server-client-options";
import { Feature, CreateFeatureParams } from "./feature.types";

export const featureService = {
  getFeature: async ({ id }: { id: string }): Promise<Feature> => {
    return new FeatureClient(getAgentServerClientOptions()).getFeature(id);
  },

  createFeature: async (params: CreateFeatureParams): Promise<Feature> => {
    return new FeatureClient(getAgentServerClientOptions()).createFeature(params);
  },
};
```

### Types

Define app-specific types in a separate file within the same directory when the TypeScript client models are not sufficient:

```typescript
// feature-service/feature.types.ts
export interface Feature {
  id: string;
  name: string;
  description: string;
}

export interface CreateFeatureParams {
  name: string;
  description: string;
}
```

## Usage

> [!IMPORTANT]
> **Don't call services directly in components.** Wrap them in TanStack Query hooks.
>
> Why? TanStack Query provides:
>
> - **Caching** - Avoid redundant network requests
> - **Deduplication** - Multiple components requesting the same data share one request
> - **Loading/error states** - Built-in `isLoading`, `isError`, `data` states
> - **Background refetching** - Data stays fresh automatically
>
> Hooks location:
>
> - `src/hooks/query/` for data fetching (`useQuery`)
> - `src/hooks/mutation/` for writes/updates (`useMutation`)

```typescript
// src/hooks/query/use-feature.ts
import { useQuery } from "@tanstack/react-query";
import { featureService } from "#/api/feature-service/feature-service.api";

export const useFeature = (id: string) => {
  return useQuery({
    queryKey: ["feature", id],
    queryFn: () => featureService.getFeature({ id }),
  });
};
```

## Naming Conventions

| Item         | Convention               | Example                  |
| ------------ | ------------------------ | ------------------------ |
| Directory    | `feature-service/`       | `secrets-service/`       |
| Service file | `feature-service.api.ts` | `secrets-service.api.ts` |
| Types file   | `feature.types.ts`       | `secrets.types.ts`       |
| Export name  | `featureService`         | `secretsService`         |