Skip to content

Commit 0dfb892

Browse files
authored
Merge pull request #12 from API-200/sdk
Typescript SDK
2 parents a37818a + 5b6d577 commit 0dfb892

File tree

17 files changed

+2002
-48
lines changed

17 files changed

+2002
-48
lines changed

packages/dashboard/src/app/(layout)/services/[id]/endpoints/[endpointId]/components/CodeExample.tsx

Lines changed: 83 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import { FC, useState } from "react"
44
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
5-
import { Card, CardContent } from "@/components/ui/card"
5+
import {Card, CardContent, CardDescription, CardTitle} from "@/components/ui/card"
66
import { Button } from "@/components/ui/button"
77
import { Copy, CopyCheck } from "lucide-react"
88
import {getCodeExample, Language, Method} from "@/utils/getCodeExample";
@@ -12,10 +12,10 @@ interface ApiExampleProps {
1212
method: Method
1313
}
1414

15-
1615
export const ApiExample: FC<ApiExampleProps> = ({ url, method }) => {
1716
const [language, setLanguage] = useState<Language>("js")
1817
const [isCopied, setIsCopied] = useState(false)
18+
const [isSdkCopied, setIsSdkCopied] = useState(false)
1919

2020
const handleCopy = async () => {
2121
try {
@@ -27,38 +27,88 @@ export const ApiExample: FC<ApiExampleProps> = ({ url, method }) => {
2727
}
2828
}
2929

30+
const handleCopyInstallCommand = async () => {
31+
try {
32+
await navigator.clipboard.writeText("npx api200-generate-sdk -t YOUR_API_TOKEN")
33+
setIsSdkCopied(true)
34+
setTimeout(() => setIsSdkCopied(false), 2000)
35+
} catch (err) {
36+
console.error('Failed to copy install command:', err)
37+
}
38+
}
39+
3040
return (
31-
<Card className={`w-full bg-gray-100 shadow-none`}>
32-
<CardContent className="p-4">
33-
<div className="flex justify-between items-center mb-4">
34-
<Select value={language} onValueChange={(value) => setLanguage(value as Language)}>
35-
<SelectTrigger className="w-[120px] bg-white h-8 text-xs">
36-
<SelectValue placeholder="Select language" />
37-
</SelectTrigger>
38-
<SelectContent>
39-
<SelectItem value="js">JavaScript</SelectItem>
40-
<SelectItem value="python">Python</SelectItem>
41-
<SelectItem value="php">PHP</SelectItem>
42-
<SelectItem value="go">Go</SelectItem>
43-
<SelectItem value="rust">Rust</SelectItem>
44-
<SelectItem value="java">Java</SelectItem>
45-
<SelectItem value="csharp">C#</SelectItem>
46-
<SelectItem value="curl">cURL</SelectItem>
47-
</SelectContent>
48-
</Select>
49-
<Button variant="outline" size="sm" onClick={handleCopy} className="h-8 w-8 p-0">
50-
{isCopied ? (
51-
<CopyCheck className="h-4 w-4 text-green-500" />
52-
) : (
53-
<Copy className="h-4 w-4" />
54-
)}
55-
</Button>
56-
</div>
57-
<pre className="text-sm overflow-x-auto">
58-
<code>{getCodeExample(language, url, method)}</code>
59-
</pre>
60-
</CardContent>
61-
</Card>
41+
<div className="w-full space-y-4">
42+
{/* SDK Installation Instructions for JavaScript */}
43+
{language === "js" && (
44+
<Card className="w-full bg-blue-50 border-blue-200 shadow-none">
45+
<CardContent className="p-4">
46+
<div className="flex justify-between items-start mb-3">
47+
<div>
48+
<CardTitle className="text-sm text-blue-900">
49+
📦 SDK Installation
50+
</CardTitle>
51+
<CardDescription className="text-xs text-blue-700">
52+
Generate your personalized SDK with your API token
53+
</CardDescription>
54+
</div>
55+
<Button
56+
variant="outline"
57+
size="sm"
58+
onClick={handleCopyInstallCommand}
59+
className="h-8 w-8 p-0 border-blue-300 hover:bg-blue-100"
60+
>
61+
{isSdkCopied? <CopyCheck className="h-3 w-3 text-blue-600"/> :<Copy className="h-3 w-3 text-blue-600" />}
62+
63+
</Button>
64+
</div>
65+
<div className="bg-white rounded border border-blue-200 p-3">
66+
<code className="text-sm text-blue-800 font-mono">
67+
npx api200-generate-sdk -t YOUR_API_TOKEN
68+
</code>
69+
</div>
70+
</CardContent>
71+
</Card>
72+
)}
73+
74+
{/* Main Code Example */}
75+
<Card className="w-full bg-gray-100 shadow-none">
76+
<CardContent className="p-4">
77+
<div className="flex justify-between items-center mb-4">
78+
<Select value={language} onValueChange={(value) => setLanguage(value as Language)}>
79+
<SelectTrigger className="w-[160px] bg-white h-8 text-xs">
80+
<SelectValue placeholder="Select language" />
81+
</SelectTrigger>
82+
<SelectContent>
83+
<SelectItem value="js">
84+
<div className="flex items-center gap-2">
85+
<span>JavaScript</span>
86+
<span className="text-xs bg-blue-100 text-blue-800 px-1.5 py-0.5 rounded">SDK</span>
87+
</div>
88+
</SelectItem>
89+
<SelectItem value="python">Python</SelectItem>
90+
<SelectItem value="php">PHP</SelectItem>
91+
<SelectItem value="go">Go</SelectItem>
92+
<SelectItem value="rust">Rust</SelectItem>
93+
<SelectItem value="java">Java</SelectItem>
94+
<SelectItem value="csharp">C#</SelectItem>
95+
<SelectItem value="curl">cURL</SelectItem>
96+
</SelectContent>
97+
</Select>
98+
<Button variant="outline" size="sm" onClick={handleCopy} className="h-8 w-8 p-0">
99+
{isCopied ? (
100+
<CopyCheck className="h-4 w-4 text-green-500" />
101+
) : (
102+
<Copy className="h-4 w-4" />
103+
)}
104+
</Button>
105+
</div>
106+
<pre className="text-sm overflow-x-auto">
107+
<code>{getCodeExample(language, url, method)}</code>
108+
</pre>
109+
</CardContent>
110+
</Card>
111+
</div>
62112
)
63113
}
64114

packages/dashboard/src/utils/getCodeExample.ts

Lines changed: 70 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,83 @@
1-
21
export type Method = "GET" | "POST" | "PUT" | "DELETE" | "PATCH"
32
export type Language = "js" | "python" | "curl" | "php" | "go" | "rust" | "java" | "csharp"
43

4+
// Helper function to generate SDK method name (matching your SDK generator logic)
5+
function generateSDKMethodName(method: string, path: string): string {
6+
return `${method.toLowerCase()}_${path.replace(/^\//, '').replace(/\//g, '_')}`.replace(/{([^}]+)}/g, 'by_$1');
7+
}
8+
9+
// Helper function to convert service name to camelCase (matching your SDK generator logic)
10+
function toCamelCase(str: string): string {
11+
return str.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
12+
}
13+
14+
// Helper function to extract service name and endpoint path from URL
15+
function parseApiUrl(url: string): { serviceName: string; endpointPath: string } {
16+
// Extract from URL like: https://eu.api200.co/api/users/profile/{id}
17+
const urlParts = url.split('/api/');
18+
if (urlParts.length < 2) {
19+
return { serviceName: 'service', endpointPath: '/endpoint' };
20+
}
21+
22+
const pathParts = urlParts[1].split('/');
23+
const serviceName = pathParts[0] || 'service';
24+
const endpointPath = '/' + pathParts.slice(1).join('/');
25+
26+
return { serviceName, endpointPath };
27+
}
28+
29+
// Helper function to generate parameter example based on URL path
30+
function generateSDKParams(url: string, method: Method): string {
31+
const pathParams = url.match(/{([^}]+)}/g);
32+
const hasRequestBody = ['POST', 'PUT', 'PATCH'].includes(method);
33+
34+
if (!pathParams && !hasRequestBody) {
35+
return '';
36+
}
537

38+
const params: string[] = [];
39+
40+
// Add path parameters
41+
if (pathParams) {
42+
pathParams.forEach(param => {
43+
const paramName = param.replace(/[{}]/g, '');
44+
params.push(` ${paramName}: "your_${paramName}_value"`);
45+
});
46+
}
47+
48+
// Add request body for POST, PUT, PATCH
49+
if (hasRequestBody) {
50+
params.push(` requestBody: {
51+
// Your request data here
52+
name: "example",
53+
value: "data"
54+
}`);
55+
}
56+
57+
return params.length > 0 ? `{\n${params.join(',\n')}\n }` : '';
58+
}
659

760
export const getCodeExample = (language: Language, url: string, method: Method): string => {
861
const headers = '{"x-api-key": "YOUR_API_KEY"}'
962

10-
const examples = {
11-
js: `const fetchData = async () => {
12-
try {
13-
const response = await fetch("${url}", {
14-
method: "${method}",
15-
headers: ${headers}
16-
});
17-
const data = await response.json();
18-
console.log(data);
19-
} catch (error) {
20-
console.error('Error:', error);
21-
}
63+
// For JavaScript, show SDK usage instead of fetch
64+
if (language === 'js') {
65+
const { serviceName, endpointPath } = parseApiUrl(url);
66+
const camelCaseServiceName = toCamelCase(serviceName);
67+
const methodName = generateSDKMethodName(method, endpointPath);
68+
const params = generateSDKParams(url, method);
69+
70+
return `
71+
import api200 from '@lib/api200';
72+
73+
const fetchData = async () => {
74+
const { data, error } = await api200.${camelCaseServiceName}.${methodName}.${method.toLowerCase()}(${params ? params : ''});
2275
};
2376
24-
fetchData();`,
77+
fetchData();`;
78+
}
2579

80+
const examples = {
2681
python: `import requests
2782
2883
url = "${url}"
@@ -138,5 +193,5 @@ class Program
138193
}`
139194
}
140195

141-
return examples[language] || examples.js
196+
return examples[language]
142197
}

packages/js-sdk/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
node_modules
2+
dist
3+
.env

packages/js-sdk/README.md

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
# API200 SDK Generator
2+
3+
A TypeScript SDK generator that automatically creates a fully-typed client library for your API200 services. Generate type-safe API clients with zero configuration and full IDE support.
4+
5+
## Features
6+
7+
- **Automatic Type Generation**: Creates TypeScript interfaces from your API schemas
8+
- **Full Type Safety**: Compile-time error checking for API calls
9+
- **Zero Configuration**: Works out of the box with sensible defaults
10+
- **Path & Query Parameters**: Handles both path variables and query strings
11+
- **Request Body Support**: Type-safe request body handling for POST/PUT/PATCH
12+
- **Error Handling**: Structured error responses with status codes
13+
- **IDE Support**: Full IntelliSense and autocomplete support
14+
15+
## Prerequisites
16+
17+
1. **Register with API200**: Sign up at [API200](https://api200.co) and set up your API proxy platform
18+
2. **Import/Create Services**: Add your API services to the API200 platform
19+
3. **Get API Token**: Obtain your user token from the API200 dashboard
20+
21+
## Installation
22+
23+
Generate your SDK using npx (no installation required):
24+
25+
```bash
26+
npx api200-generate-sdk -t YOUR_API_TOKEN
27+
```
28+
29+
## Command Options
30+
31+
| Option | Short | Description | Default |
32+
|--------|-------|-------------|---------|
33+
| `--token` | `-t` | **Required.** Your API200 user token ||
34+
| `--base-url` | `-u` | Base API URL | `https://eu.api200.co/api` |
35+
| `--output` | `-o` | Output directory | `./src/lib/api200` or `./lib/api200` |
36+
37+
### Examples
38+
39+
```bash
40+
# Basic usage
41+
npx api200-generate-sdk -t your_token_here
42+
43+
# Custom output directory
44+
npx api200-generate-sdk -t your_token_here -o ./src/api
45+
46+
# Different API region
47+
npx api200-generate-sdk -t your_token_here -u https://us.api200.co/api
48+
```
49+
50+
## Generated Structure
51+
52+
The SDK generator creates the following file structure:
53+
54+
```
55+
lib/api200/
56+
├── index.ts # Main export file with configured client
57+
├── api200.ts # Core client and request handling
58+
├── types.ts # TypeScript type definitions
59+
└── [service-name].ts # Individual service files
60+
```
61+
62+
## Usage Example
63+
64+
### Usage
65+
66+
```typescript
67+
import api200 from './lib/api200';
68+
69+
// GET request with path parameter
70+
const { data, error } = await api200.users.getUserById.get({
71+
id: "123"
72+
});
73+
74+
if (error) {
75+
console.error('API Error:', error.message);
76+
} else {
77+
console.log('User data:', data);
78+
}
79+
```
80+
81+
### Method Naming Convention
82+
83+
Methods are generated using the pattern: `[httpMethod][EndpointPath]`
84+
85+
Examples:
86+
- `GET /users/{id}``getUserById.get()`
87+
- `POST /users``createUser.post()`
88+
- `PUT /users/{id}``updateUserById.put()`
89+
- `DELETE /orders/{orderId}``deleteOrderByOrderId.delete()`
90+
91+
## Error Handling
92+
93+
All API methods return a consistent response structure:
94+
95+
```typescript
96+
interface API200Response<T> {
97+
data: T | null;
98+
error: API200Error | null;
99+
}
100+
101+
interface API200Error {
102+
message: string;
103+
status?: number;
104+
details?: any;
105+
}
106+
```
107+
108+
## Types
109+
110+
The generator creates comprehensive TypeScript types for all your APIs:
111+
112+
## Updating Your SDK
113+
114+
When you add new services or modify existing ones in API200:
115+
116+
1. **Regenerate the SDK**:
117+
```bash
118+
npx api200-generate-sdk -t YOUR_API_TOKEN
119+
```
120+
121+
2. **The generator will**:
122+
- Fetch the latest service definitions
123+
- Update all type definitions
124+
- Add new service methods
125+
- Preserve your existing configuration
126+
127+
3. **No breaking changes**: Existing code continues to work while new features become available
128+
129+
## Support
130+
131+
- [GitHub](https://github.com/API-200/api200/discussions)

0 commit comments

Comments
 (0)