User Guide: - Enhanced req_user_guide.md with full request editor documentation - Created collections_guide.md for request management - Created importing_guide.md for cURL/Postman/Insomnia/HAR/OpenAPI imports - Created code_generation_guide.md for code gen feature - Updated README.md with all guide links Developer Guide: - Created architecture.md with project architecture overview - Created adding_codegen.md with step-by-step codegen guide - Updated README.md with new guide links and openapi.md reference Co-authored-by: animator <615622+animator@users.noreply.github.com>
7.8 KiB
Adding a New Code Generator
This guide explains how to add support for a new programming language or library to API Dash's code generation system.
Overview
API Dash generates runnable code for API requests in 30+ language/library combinations. Each generator is a Dart class that uses Jinja templates to produce code strings from request models.
Step 1: Add the Enum Value
Add a new entry to the CodegenLanguage enum in lib/consts.dart:
enum CodegenLanguage {
// ... existing entries ...
myLanguageLib("MyLanguage (mylib)", "mylanguage", "ext"),
}
The three parameters are:
| Parameter | Description | Example |
|---|---|---|
label |
Display name shown in the UI dropdown | "Python (requests)" |
codeHighlightLang |
Language identifier for syntax highlighting | "python" |
ext |
File extension for the generated code | "py" |
Step 2: Create the Generator Class
Create a new file under lib/codegen/. Follow the existing directory convention — one folder per language, one file per library variant:
lib/codegen/
└── mylanguage/
└── mylib.dart
Implementation Pattern
Every generator class follows the same interface:
import 'package:apidash_core/apidash_core.dart';
import 'package:jinja/jinja.dart' as jj;
class MyLanguageMyLibCodeGen {
// 1. Define Jinja templates as class fields
final String kTemplateStart = """import mylib
url = '{{url}}'
""";
final String kTemplateParams = """
params = {{params}}
""";
final String kTemplateHeaders = """
headers = {{headers}}
""";
final String kTemplateBody = """
body = {{body}}
""";
final String kTemplateEnd = """
response = mylib.{{method}}(url{{params}}{{headers}}{{body}})
print(response.status_code)
print(response.text)
""";
// 2. Implement the getCode method
String? getCode(
HttpRequestModel requestModel, {
String? boundary,
}) {
try {
String result = "";
// Parse and validate the URI
var rec = getValidRequestUri(
requestModel.url,
requestModel.enabledParams,
);
Uri? uri = rec.$1;
if (uri != null) {
// Render the start template
var templateStart = jj.Template(kTemplateStart);
result += templateStart.render({
"url": stripUriParams(uri),
});
// Add query parameters if present
if (requestModel.enabledParamsMap.isNotEmpty) {
var templateParams = jj.Template(kTemplateParams);
result += templateParams.render({
"params": requestModel.enabledParamsMap.toString(),
});
}
// Add headers if present
if (requestModel.enabledHeadersMap.isNotEmpty) {
var templateHeaders = jj.Template(kTemplateHeaders);
result += templateHeaders.render({
"headers": requestModel.enabledHeadersMap.toString(),
});
}
// Add body if present
String? body = requestModel.body;
if (body != null && body.isNotEmpty) {
var templateBody = jj.Template(kTemplateBody);
result += templateBody.render({
"body": body,
});
}
// Render the final template
var templateEnd = jj.Template(kTemplateEnd);
result += templateEnd.render({
"method": requestModel.method.name.toLowerCase(),
"params": requestModel.enabledParamsMap.isNotEmpty
? ", params=params"
: "",
"headers": requestModel.enabledHeadersMap.isNotEmpty
? ", headers=headers"
: "",
"body": (body != null && body.isNotEmpty) ? ", body=body" : "",
});
}
return result;
} catch (e) {
return null;
}
}
}
Key APIs Available
| Function | Source | Purpose |
|---|---|---|
getValidRequestUri(url, enabledParams) |
apidash_core |
Parses URL and query params, returns (Uri?, String?) |
stripUriParams(uri) |
apidash_core |
Returns URL string without query parameters |
kJsonEncoder.convert(data) |
apidash_core |
Converts data to formatted JSON string |
jsonToPyDict(json) |
lib/codegen/codegen_utils.dart |
Converts JSON to Python dict syntax (useful for Python-like languages) |
Request Model Properties
The HttpRequestModel provides:
| Property | Type | Description |
|---|---|---|
url |
String |
Request URL |
method |
HTTPVerb |
HTTP method (get, post, put, patch, delete, head, options) |
enabledParams |
List<NameValueModel>? |
Active query parameters |
enabledParamsMap |
Map<String, String> |
Query parameters as a map |
enabledHeaders |
List<NameValueModel>? |
Active headers |
enabledHeadersMap |
Map<String, String> |
Headers as a map |
body |
String? |
Request body content |
bodyContentType |
ContentType |
Body content type (json, text, formdata, etc.) |
formData |
List<FormDataModel>? |
Form data entries |
hasBody |
bool |
Whether the request has a body |
hasFormData |
bool |
Whether the request uses form data |
Step 3: Register the Generator
Add the new generator to the switch statement in lib/codegen/codegen.dart:
- Import the new file:
import 'mylanguage/mylib.dart';
- Add the case to the
getCodemethod:
case CodegenLanguage.myLanguageLib:
return MyLanguageMyLibCodeGen().getCode(rM, boundary: boundary);
Step 4: Add Tests
Create a test file at test/codegen/mylanguage_mylib_codegen_test.dart:
import 'package:apidash/codegen/codegen.dart';
import 'package:apidash/consts.dart';
import 'package:apidash_core/apidash_core.dart';
import 'package:test/test.dart';
import '../models/request_models.dart';
void main() {
final codeGen = Codegen();
group('MyLanguage MyLib Code Generation', () {
test('GET 1', () {
const expectedCode = r"""import mylib
url = 'https://api.apidash.dev'
response = mylib.get(url)
print(response.status_code)
print(response.text)
""";
expect(
codeGen.getCode(
CodegenLanguage.myLanguageLib,
requestModelGet1,
SupportedUriSchemes.https,
),
expectedCode,
);
});
// Add more tests for POST, PUT, DELETE, headers, params, body, etc.
});
}
Test Models
Shared test request models are defined in test/models/request_models.dart. Use the existing models (e.g., requestModelGet1, requestModelPost1) to test your generator against standard scenarios.
Running Tests
# Run your specific test file
flutter test test/codegen/mylanguage_mylib_codegen_test.dart
# Run all codegen tests
flutter test test/codegen/
Step 5: Verify
- Run the app locally:
flutter run - Create a request with various configurations (headers, params, body, auth).
- Open the code generation panel and select your new language.
- Verify the generated code is correct and runnable.
- Run the full test suite:
flutter test
Checklist
- Added
CodegenLanguageenum value inlib/consts.dart - Created generator class in
lib/codegen/<language>/<library>.dart - Registered the generator in
lib/codegen/codegen.dart - Handles GET requests
- Handles POST/PUT/PATCH/DELETE with JSON body
- Handles query parameters
- Handles custom headers
- Handles form data (URL-encoded and multipart)
- Returns
nullon error (never throws) - Added tests in
test/codegen/ - All existing tests still pass
Reference Implementations
For well-structured examples, look at these existing generators:
- Simple:
lib/codegen/others/curl.dart— Straightforward template usage. - Moderate:
lib/codegen/python/requests.dart— Full feature set with conditional imports and body type handling. - Complex:
lib/codegen/rust/reqwest.dart— Multiple template variants for different body types and auth methods.