dorval
Version:
CLI tool for generating Dart/Flutter API clients from OpenAPI specifications
535 lines (419 loc) ⢠13.2 kB
Markdown
[](https://www.npmjs.com/package/dorval)
[](https://opensource.org/licenses/MIT)
CLI tool for generating type-safe Dart/Flutter API clients from OpenAPI specifications.
- šÆ **Type-safe API clients** - Generate strongly-typed Dart code from OpenAPI specs
- āļø **Freezed models** - Immutable data classes with copyWith, equality, and more
- š **JSON serialization** - Built-in fromJson/toJson with json_serializable
- š **Multiple HTTP clients** - Support for Dio, HTTP, Chopper, and Retrofit
- š **Full OpenAPI 3.0 support** - Handle complex schemas, references, and more
- š **Watch mode** - Auto-regenerate on spec changes during development
- šØ **Highly configurable** - Control every aspect of code generation
- ā” **Fast generation** - Optimized for large APIs
```bash
npm install -g dorval
npx dorval generate -i ./openapi.yaml -o ./lib/api
npm install --save-dev dorval
```
```bash
dorval generate -i ./openapi.yaml -o ./lib/api
dorval generate -i https://petstore.swagger.io/v2/swagger.json -o ./lib/api
dorval generate -i ./spec.yaml -o ./lib/api --client dio
```
Create a `dorval.config.ts` (or `.js`, `.json`):
```typescript
export default {
petstore: {
input: './petstore.yaml',
output: {
target: './lib/api',
mode: 'split',
client: 'dio',
override: {
generator: {
freezed: true,
jsonSerializable: true,
nullSafety: true
},
methodNaming: 'methodPath'
}
}
}
};
```
Then run:
```bash
dorval generate
dorval generate -c ./custom.config.ts
```
```typescript
export default {
apiName: { // You can have multiple APIs in one config
// INPUT OPTIONS
input: './path/to/openapi.yaml', // Local file, URL, or OpenAPI object
// OUTPUT OPTIONS
output: {
target: './lib/generated/api', // Output directory
mode: 'split', // File organization
// 'single' - All code in one file
// 'split' - Separate models and services (default)
// 'tags' - Group by OpenAPI tags
client: 'dio', // HTTP client library
// 'dio' - Feature-rich, supports interceptors (default)
// 'http' - Lightweight, built-in Dart package
// 'chopper' - Code generation based
// 'retrofit' - Annotation-based (experimental)
override: {
// Generator options
generator: {
freezed: true, // Generate Freezed models (default: true)
jsonSerializable: true, // Add JSON serialization (default: true)
nullSafety: true, // Enable null safety (default: true)
partFiles: true, // Generate part files (default: true)
equatable: false // Add Equatable support (default: false)
},
// Method naming strategy
methodNaming: 'operationId', // How to name service methods
// 'operationId' - Use OpenAPI operationId (default)
// 'methodPath' - Generate from HTTP method + path
// Dio-specific options
dio: {
baseUrl: 'https://api.example.com', // Override base URL
interceptors: ['AuthInterceptor'] // Custom interceptors
}
}
},
// POST-GENERATION HOOKS
hooks: {
afterAllFilesWrite: 'dart format .' // Commands to run after generation
// Can also be an array: ['dart format .', 'flutter pub get']
}
}
};
```
```typescript
export default {
// User API
userApi: {
input: './specs/user-api.yaml',
output: {
target: './lib/api/user',
client: 'dio'
}
},
// Admin API with different settings
adminApi: {
input: './specs/admin-api.yaml',
output: {
target: './lib/api/admin',
client: 'dio',
override: {
methodNaming: 'operationId',
generator: {
freezed: true,
equatable: true // Admin API uses Equatable
}
}
}
},
// Public API from URL
publicApi: {
input: 'https://api.example.com/public/openapi.json',
output: {
target: './lib/api/public',
mode: 'tags' // Group by tags
}
}
};
```
Generate Dart API client from OpenAPI specification.
```bash
dorval generate [options]
```
**Options:**
- `-i, --input <path>` - Path or URL to OpenAPI specification
- `-o, --output <path>` - Output directory for generated code
- `-c, --config <path>` - Path to configuration file
- `--client <type>` - HTTP client type (dio|http|chopper|retrofit)
- `-h, --help` - Display help
- `-V, --version` - Display version
**Examples:**
```bash
# Simple generation
dorval generate -i api.yaml -o ./lib
# With specific client
dorval generate -i api.yaml -o ./lib --client dio
# Using config file
dorval generate -c ./my-config.js
# From URL
dorval generate -i https://api.example.com/openapi.json -o ./lib
```
### watch
Watch OpenAPI spec for changes and regenerate automatically.
```bash
dorval watch [options]
```
**Options:**
- `-c, --config <path>` - Path to configuration file (required)
**Example:**
```bash
# Watch for changes
dorval watch -c ./dorval.config.ts
```
## Method Naming Strategies
Control how generated method names look with the `methodNaming` option:
### operationId (default)
Uses the `operationId` field from your OpenAPI specification:
```yaml
# OpenAPI spec
paths:
/pets/{id}:
get:
operationId: showPetById
Future<Pet> showPetById(String id);
```
Generates descriptive names from HTTP method and path:
```yaml
paths:
/pets/{id}:
get: ...
/users/{userId}/settings:
post: ...
/v1/locations/{locationId}/settings:
put: ...
Future<Pet> getPetsById(String id);
Future<Settings> postUsersByUserIdSettings(String userId, SettingsDto body);
Future<void> putV1LocationsLocationIdSettings(String locationId, SettingsDto body);
```
```
lib/api/
āāā api_client.dart
āāā api_config.dart
āāā models/
ā āāā user.f.dart
ā āāā user.f.freezed.dart
ā āāā user.f.g.dart
ā āāā params/
ā ā āāā get_users_params.f.dart
ā ā āāā index.dart
ā āāā headers/
ā ā āāā get_users_headers.f.dart
ā ā āāā index.dart
ā āāā index.dart
āāā services/
āāā users_service.dart
āāā api_exception.dart
āāā index.dart
```
```json
{
"scripts": {
"generate": "dorval generate",
"generate:watch": "dorval watch -c dorval.config.ts",
"prebuild": "npm run generate"
}
}
```
```yaml
- name: Generate API Client
run: |
npm install -g dorval
dorval generate -c ./dorval.config.ts
generate-api:
script:
- npx dorval generate -i $API_SPEC_URL -o ./lib/api
```
```typescript
// dorval.config.ts
export default {
api: {
input: process.env.API_SPEC_URL || './openapi.yaml',
output: {
target: './lib/api',
override: {
dio: {
baseUrl: process.env.API_BASE_URL || 'https://api.example.com'
}
}
}
}
};
```
After generating the API client, set up your Flutter project:
```yaml
dependencies:
dio: ^5.0.0
freezed_annotation: ^2.4.1
json_annotation: ^4.8.1
dev_dependencies:
build_runner: ^2.4.0
freezed: ^2.4.0
json_serializable: ^6.7.0
```
```bash
flutter pub get
flutter pub run build_runner build --delete-conflicting-outputs
```
```dart
import 'package:dio/dio.dart';
import 'api/api_client.dart';
import 'api/services/users_service.dart';
import 'api/models/user.f.dart';
void main() async {
// Initialize client
final apiClient = ApiClient(
dio: Dio(),
baseUrl: 'https://api.example.com',
);
// Create service
final usersService = UsersService(apiClient);
// Make type-safe API calls
final List<User> users = await usersService.getUsers(
limit: 10,
offset: 0,
);
// Handle errors
try {
final user = await usersService.getUserById('123');
print('User name: ${user.name}');
} on ApiException catch (e) {
print('API Error: ${e.message}');
print('Status Code: ${e.statusCode}');
}
}
```
```dart
// lib/interceptors/auth_interceptor.dart
class AuthInterceptor extends Interceptor {
@override
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
options.headers['Authorization'] = 'Bearer $token';
super.onRequest(options, handler);
}
}
// Use in your app
final dio = Dio()..interceptors.add(AuthInterceptor());
final apiClient = ApiClient(dio: dio);
```
```dart
try {
final result = await service.someApiCall();
} on ApiException catch (e) {
switch (e.statusCode) {
case 401:
// Handle unauthorized
break;
case 404:
// Handle not found
break;
default:
// Handle other errors
}
}
```
```dart
// test/mocks/mock_users_service.dart
class MockUsersService implements UsersService {
@override
Future<List<User>> getUsers({int? limit, int? offset}) async {
return [
User(id: '1', name: 'Test User'),
];
}
}
```
**"Missing loader for extension 'orval.config.mjs'" error**
- Use `.ts`, `.js`, or `.json` config files instead
- Or use command line options: `dorval generate -i spec.yaml -o ./lib`
**Generated methods return `Map<String, dynamic>` instead of models**
- Ensure your OpenAPI spec uses `$ref` for response schemas
- Check that models are defined in `components/schemas`
**Duplicate method names in services**
- Use unique `operationId` values in your OpenAPI spec
- Or switch to `methodNaming: 'methodPath'` for automatic unique names
**Import errors in generated Dart code**
- Run `flutter pub get` after generation
- Run `flutter pub run build_runner build`
- Ensure all dependencies are in `pubspec.yaml`
**"Cannot find module '@dorval/core'" error**
- Run `npm install` in your project
- Ensure `@dorval/core` is installed as a dependency
Set environment variable for verbose output:
```bash
DEBUG=dorval* dorval generate -c dorval.config.ts
```
| Feature | dorval | OpenAPI Generator | Swagger Codegen |
|---------|--------|-------------------|-----------------|
| Dart/Flutter Focus | ā
Native | ā ļø Generic | ā ļø Generic |
| Freezed Support | ā
Built-in | ā Manual | ā Manual |
| TypeScript Config | ā
Yes | ā Java/CLI | ā Java/CLI |
| Watch Mode | ā
Yes | ā No | ā No |
| Method Naming Control | ā
Yes | ā No | ā No |
| NPM Package | ā
Yes | ā Docker/JAR | ā Docker/JAR |
| Bundle Size | ā
Small | ā Large | ā Large |
1. Install dorval: `npm install -g dorval`
2. Create `dorval.config.ts` with your settings
3. Run `dorval generate`
4. Update imports in your Dart code
1. Convert your config to dorval format
2. Replace JAR execution with `dorval generate`
3. Update generated file imports
We welcome contributions! See [CONTRIBUTING.md](https://github.com/qwlong/dorval/blob/master/CONTRIBUTING.md).
- š [Documentation](https://github.com/qwlong/dorval#readme)
- š [Report Issues](https://github.com/qwlong/dorval/issues)
- š¬ [Discussions](https://github.com/qwlong/dorval/discussions)
- š§ [Email Support](mailto:support@dorval.dev)
MIT Ā© 2025
- [GitHub Repository](https://github.com/qwlong/dorval)
- [NPM Package](https://www.npmjs.com/package/dorval)
- [Core Library](https://www.npmjs.com/package/@dorval/core)
- [Petstore Example](https://github.com/qwlong/dorval/tree/master/samples/petstore)
- [Changelog](https://github.com/qwlong/dorval/blob/master/CHANGELOG.md)