serverpod_swagger 0.1.7
serverpod_swagger: ^0.1.7 copied to clipboard
A package to automatically generate and serve Swagger UI for a Serverpod backend.
Serverpod Swagger #
A package to automatically generate and serve Swagger UI for a Serverpod backend. This package makes it easy to add interactive API documentation to your Serverpod project.
Features #
- Automatically generates OpenAPI 3.0 specification from Serverpod protocol definitions
- Serves Swagger UI directly from your Serverpod server
- Provides an interactive interface for exploring and testing your API endpoints
- Supports all Serverpod data types and custom classes
- Intelligent HTTP method detection based on parameter types
- Comprehensive model parsing and schema generation
- Flexible authentication configuration
- Compatible with Serverpod 2.8.0
Requirements #
- Serverpod 2.8.0 or higher
- Dart 3.0.0 or higher
Installation #
Add the package to your pubspec.yaml
file:
dependencies:
serverpod_swagger: ^0.1.3
Then run:
dart pub get
Usage #
Direct Package Execution #
The recommended way to generate an OpenAPI specification for your Serverpod project is to run the following command from your server project root directory:
dart run serverpod_swagger:generate --base-url=http://localhost:8080
This will create an apispec.json
file in your project root directory. The --base-url
parameter specifies the base URL for your API endpoints.
Command-Line Arguments
The generator supports various command-line arguments to customize your OpenAPI specification:
Argument | Description | Example |
---|---|---|
--base-url |
Sets the base URL for your API server | --base-url=https://api.example.com |
--auth |
Specifies the authentication type (jwt, apikey, basic, oauth2) | --auth=jwt |
--auth-description |
Provides a custom description for the authentication scheme | --auth-description="JWT token from /auth endpoint" |
--secure-endpoints |
Comma-separated list of endpoints to secure | --secure-endpoints=users,posts/create |
--unsecure-endpoints |
Comma-separated list of endpoints to exclude from security | --unsecure-endpoints=health,status |
--secure-single-url |
Secures a specific URL endpoint | --secure-single-url=/jwtAuth/getCurrentUser |
--http-method |
Sets the HTTP method for a specific endpoint | --http-method=profile/user:post |
--update |
Updates an existing specification instead of regenerating | --update |
--unauth or --disable-auth |
Disables authentication while preserving configuration | --unauth |
--verbose |
Displays detailed information about the generation process | --verbose |
If you want to see detailed information about the specification generation process, you can use the --verbose
flag:
dart run serverpod_swagger:generate --base-url=http://localhost:8080 --verbose
The --verbose
flag will display additional information such as the path to the generated file, the number of endpoints included, and security schemes defined.
Updating Existing Specifications #
Instead of regenerating the entire OpenAPI specification from scratch each time, you can use the --update
flag to modify an existing specification file:
dart run serverpod_swagger:generate --update --http-method=greeting/hello:post
This allows you to make incremental changes to your API documentation without having to specify all parameters again.
Common update scenarios:
# Update HTTP method for an endpoint
dart run serverpod_swagger:generate --update --http-method=greeting/hello:post
# Update base URL
dart run serverpod_swagger:generate --update --base-url=https://api.example.com
# Update authentication settings
dart run serverpod_swagger:generate --update --auth=jwt
The update mode is particularly useful for large projects where regenerating the entire specification would be time-consuming.
See the detailed documentation for more information on when and how to use this feature.
Alternative: Using the Script #
Alternatively, you can run the script directly from the package:
dart run serverpod_swagger:generate
This will also create an apispec.json
file in your project root directory.
Adding Swagger UI to Your Server #
In your Serverpod server's server.dart
file, add the SwaggerUIRoute to your server's routes:
import 'dart:io';
import 'package:serverpod/serverpod.dart';
import 'package:serverpod_swagger/serverpod_swagger.dart';
Future<void> run(List<String> args) async {
// Create the server
final pod = Serverpod(
args,
Protocol(), // Your protocol class
Endpoints(), // Your endpoints class
// Optional configuration
);
// Get the project root directory
final projectRoot = Directory.current;
// Create a SwaggerUIRoute
final swaggerRoute = SwaggerUIRoute(projectRoot);
// Add the route to your server
// IMPORTANT: The path must end with a trailing slash
pod.webServer.addRoute(swaggerRoute, '/swagger*');
// Start the server
await pod.start();
}
After starting your server, you can access the Swagger UI at:
http://localhost:8082/swagger/
IMPORTANT: Note the trailing slash in the URL - this is required for the Swagger UI to work correctly.
How it works #
The package works by:
- Reading your Serverpod endpoints.dart file to understand your API structure
- Parsing your project's data models to create accurate OpenAPI schemas
- Converting the protocol definitions to OpenAPI 3.0 format
- Serving the Swagger UI interface with your API documentation
Model Parsing and Schema Generation #
The generator performs comprehensive parsing of your project's data models to create accurate OpenAPI schemas:
YAML Model Parsing
The generator parses Serverpod's YAML model files (.yaml
or .spy.yaml
) to extract:
- Class definitions and properties
- Field types and nullability
- Relationships between models
This information is used to create detailed OpenAPI schema definitions that accurately represent your data models.
Type Mapping
The generator maps Dart and YAML types to OpenAPI schema types:
Dart/YAML Type | OpenAPI Schema Type |
---|---|
String | string |
int | integer (int64) |
double | number (double) |
bool | boolean |
DateTime | string (date-time) |
ByteData | string (byte) |
Duration | string |
Uri | string (uri) |
List | array |
Map<K,V> | object |
Custom classes | $ref to schema |
Dependency Resolution
When the generator encounters references to models from other Serverpod modules:
- It identifies the module and class name
- Locates the model file in the appropriate package
- Parses the model and adds it to the schema definitions
This ensures that all referenced models are properly included in the OpenAPI specification.
Generating API Specification #
The package includes a script to generate the OpenAPI specification file (apispec.json
) from your Serverpod endpoints:
dart run serverpod_swagger:generate [--base-url=<your-api-base-url>]
Options:
--base-url
: Specifies the base URL for your API server. This is important for the "Try it out" feature in Swagger UI to work correctly. Example:--base-url=https://api.example.com
The script will:
- Parse your Serverpod endpoints from the generated code
- Create an OpenAPI 3.0 specification
- Save it as
apispec.json
in your project root
Endpoint Detection and Path Generation #
The generator automatically detects and processes your Serverpod endpoints to create OpenAPI paths:
Endpoint Class Detection
The generator scans your project's Dart files to find classes that extend Endpoint
. For each endpoint class:
- The class name is converted to camelCase and used as the endpoint name
- Public methods in the class are identified as API operations
- Method parameters are analyzed to determine request structure
- Return types are analyzed to determine response structure
Path Generation
For each endpoint method, the generator creates an OpenAPI path with:
- Path:
/{endpointName}/{methodName}
- Operation ID:
{endpointName}.{methodName}
- Tags: Based on the endpoint name for grouping in Swagger UI
- Parameters: Generated from method parameters (excluding Session)
- Request Body: Generated for POST methods
- Responses: Based on the method's return type
Special Method Handling
The generator automatically excludes certain special methods from the API documentation:
- Methods starting with underscore (
_
) - Standard Serverpod lifecycle methods:
initialize
,streamOpened
,streamClosed
,handleStreamMessage
This ensures that only your actual API methods are included in the documentation.
Best Practices and Tips #
Optimizing Your OpenAPI Generation #
-
Use Update Mode for Incremental Changes: When making small changes to your API, use the
--update
flag to avoid regenerating the entire specification. -
Leverage Automatic HTTP Method Detection: Let the generator determine the appropriate HTTP methods based on your parameter types, and only override when necessary.
-
Organize Endpoints with Security Groups: Use
--secure-endpoints
and--unsecure-endpoints
to create logical security groups rather than securing endpoints individually. -
Provide Meaningful Base URLs: Set the
--base-url
parameter to match your actual API server URL for better developer experience. -
Use Verbose Mode During Development: Enable the
--verbose
flag during development to get detailed information about the generation process.
Troubleshooting #
Common Issues #
-
"This localhost page can't be found" error:
- Make sure you're accessing the URL with a trailing slash:
/swagger/
not/swagger
- Verify that your server is running and the web server port is correct (usually 8082)
- Check server logs for any error messages related to file paths
- Make sure you're accessing the URL with a trailing slash:
-
Static files not found:
- If you see errors about missing static files in the logs, make sure the package is properly installed
- Try running
dart pub get
again to ensure all dependencies are correctly resolved
-
Empty API documentation:
- Verify that your protocol.yaml file exists and contains valid endpoint definitions
- Check that you're passing the correct project root directory to the SwaggerUIRoute constructor
-
"Try it out" feature not working:
- If the "Try it out" feature sends requests to the wrong host, regenerate your API spec with the
--base-url
parameter - Run
dart run serverpod_swagger:generate --base-url=http://localhost:8082
(adjust the URL to match your server)
- If the "Try it out" feature sends requests to the wrong host, regenerate your API spec with the
-
Missing Endpoints:
- If endpoints are missing from your specification, ensure that your endpoint classes properly extend
Endpoint
and that methods are public.
- If endpoints are missing from your specification, ensure that your endpoint classes properly extend
-
Incorrect HTTP Methods:
- If endpoints have incorrect HTTP methods, use the
--http-method
parameter to override the automatic detection.
- If endpoints have incorrect HTTP methods, use the
-
Authentication Issues:
- If authentication isn't working as expected, check that you've specified the correct
--auth
type and applied security to the right endpoints.
- If authentication isn't working as expected, check that you've specified the correct
-
Schema Problems:
- If schemas are incomplete or incorrect, ensure that your model files are properly formatted and that all dependencies are accessible.
Customization #
You can customize the OpenAPI specification by modifying the generate.dart
script or by providing command-line arguments:
Using Command-Line Arguments #
The simplest way to customize your API documentation is by using the --base-url
parameter when generating the specification:
dart run serverpod_swagger:generate --base-url=https://api.example.com
Authentication Support #
You can add authentication support to your API specification by using the --auth
parameter when running the generate.dart
script. The following authentication types are supported:
jwt
- JWT/Bearer authenticationapikey
- API Key authenticationbasic
- Basic authenticationoauth2
- OAuth2 authentication
Example:
dart run serverpod_swagger:generate --auth=jwt --base-url=https://api.example.com
You can also provide a custom description for the authentication scheme using the --auth-description
parameter:
dart run serverpod_swagger:generate --auth=jwt --auth-description="JWT token obtained from /auth endpoint" --base-url=https://api.example.com
Securing Specific Endpoints #
By default, when you enable authentication with --auth
, all endpoints will require authentication. If you want to secure only specific endpoints, you can use the --secure-endpoints
parameter with a comma-separated list of endpoints or methods to secure:
dart run serverpod_swagger:generate --auth=jwt --secure-endpoints=users,posts/create,comments/delete
This will only apply authentication requirements to the specified endpoints or methods, while leaving others unsecured.
Securing a Single URL Endpoint #
If you need to secure a specific URL endpoint (like an endpoint with authorization token in header), you can use the --secure-single-url
parameter:
dart run serverpod_swagger:generate --auth=jwt --secure-single-url=/jwtAuth/getCurrentUser --base-url=http://localhost:8080
This will only apply authentication requirements to the exact URL path specified, which is useful for endpoints that require authorization tokens in headers.
Customizing HTTP Methods #
By default, all endpoints are generated with the HTTP GET method. If you need to specify a different HTTP method for a particular endpoint, you can use the --http-method
parameter:
dart run serverpod_swagger:generate --http-method=profile/user:post --base-url=http://localhost:8080
This will set the HTTP method for the /profile/user
endpoint to POST instead of the default GET. You can specify any valid HTTP method (get, post, put, delete, patch, etc.).
You can also use multiple --http-method
parameters to set different methods for different endpoints:
dart run serverpod_swagger:generate --http-method=profile/user:post --http-method=users/create:put --base-url=http://localhost:8080
Automatic HTTP Method Detection
The generator intelligently determines the appropriate HTTP methods for your endpoints based on their parameter types:
-
POST Detection: Endpoints are automatically set as POST methods when they have:
- Parameters that are Maps
- Parameters with types containing 'Map', 'Post', or 'Request' in their names
- Any non-primitive parameter types
-
Parameter Handling:
- Complex type parameters are included in the JSON request body
- Primitive type parameters (string, int, bool, etc.) are included as query parameters for GET requests
- Map-type parameters are included ONLY in the request body, not as query parameters
-
Request Body Structure: Request bodies are structured with parameter names as keys (e.g.,
{"userPost": {...}}
) -
Override Capability: This automatic detection can be overridden by explicitly specifying a method using the
--http-method
parameter
For example, if you have an endpoint method like this:
Future<void> postUser(Session session, UserPost request) async {
// Implementation
}
The generator will automatically:
- Set this as a POST method
- Include the
request
parameter in the JSON request body - Exclude the
request
parameter from query parameters - Create a proper OpenAPI specification with a structured request body schema
Dynamic Property Generation
The generator intelligently creates structured request body schemas based on parameter types:
- For parameters with types containing 'User', it generates properties like 'name', 'email', and 'age'
- For parameters with types containing 'Post', it generates properties like 'title', 'content', and 'tags'
- For parameters with types containing 'Request', it generates generic properties like 'data' and 'options'
- For other Map types, it generates default properties like 'id' and 'data'
This dynamic property generation makes your API documentation more informative and accurate, showing the expected structure of request bodies based on parameter types.
Nullability and Required Fields
The generator respects Dart's nullability system:
- Non-nullable parameters are marked as required in the OpenAPI specification
- Nullable parameters (with
?
suffix) are optional - The generator automatically marks non-nullable Map parameters as required in the request body
This ensures that your API documentation correctly reflects your endpoint's requirements.
See the detailed documentation for more information.
Explicitly Unsecuring Endpoints #
If you want to secure most endpoints but explicitly exclude some from requiring authentication, you can use the --unsecure-endpoints
parameter with a comma-separated list of endpoints or methods to exclude:
dart run serverpod_swagger:generate --auth=jwt --unsecure-endpoints=health,status,public/posts
This is useful when you want to secure most of your API but have a few public endpoints.
Note: If both --secure-endpoints
and --unsecure-endpoints
are provided, --unsecure-endpoints
takes precedence. This means that if an endpoint is listed in both parameters, it will be unsecured.
Globally Disabling Authentication #
If you need to temporarily disable authentication for all endpoints while preserving your authentication configuration, you can use the --unauth
or --disable-auth
flag:
dart run serverpod_swagger:generate --auth=jwt --unauth
This will include the authentication scheme definition in the OpenAPI specification but won't apply security requirements to any endpoints. This is useful for testing or development environments where you want to disable authentication without removing the configuration.
Using Authentication in Your Serverpod Project #
To use the authentication features in your Serverpod project, follow these steps:
1. Generate the OpenAPI Specification with Authentication #
First, generate your OpenAPI specification with the desired authentication configuration. You can do this in two ways:
Option A: Using the package directly from your server project
# From your server project root directory
dart run serverpod_swagger:generate --auth=jwt --base-url=https://api.example.com
Option B: Using the script from the package
# From your project root directory
dart run serverpod_swagger:generate --auth=jwt --base-url=https://api.example.com
Both methods will create an apispec.json
file in your project root directory with JWT authentication enabled for all endpoints.
2. Add the SwaggerUIRoute to Your Serverpod Server #
In your server's main file (typically bin/server.dart
), add the SwaggerUIRoute to your Serverpod server:
import 'dart:io';
import 'package:serverpod/serverpod.dart';
import 'package:serverpod_swagger/serverpod_swagger.dart';
// Your existing server setup code...
void main(List<String> args) async {
// Create your Serverpod server
final pod = Serverpod(
args,
Protocol(),
Endpoints(),
// Your server configuration...
);
// Get the project root directory
final projectRoot = Directory(Directory.current.path);
// Create a SwaggerUIRoute
final swaggerRoute = SwaggerUIRoute(projectRoot);
// Add the route to your web server
pod.webServer.addRoute(swaggerRoute, '/swagger/');
// Start the server
await pod.start();
}
3. Access the Swagger UI #
Once your server is running, you can access the Swagger UI at:
http://your-server-host:web-server-port/swagger/
For example, with default settings:
http://localhost:8082/swagger/
4. Authentication Options for Different Environments #
You can create different OpenAPI specifications for different environments. Here are examples using the direct package execution method:
Development Environment
dart run serverpod_swagger:generate --auth=jwt --unauth --base-url=http://localhost:8080
Testing Environment with Specific Endpoints Secured
dart run serverpod_swagger:generate --auth=jwt --secure-endpoints=users,posts --base-url=https://test-api.example.com
Production Environment
dart run serverpod_swagger:generate --auth=jwt --unsecure-endpoints=health,status --base-url=https://api.example.com
5. Automating Specification Generation #
You can add a script to your project to automate the generation of the OpenAPI specification for different environments. For example, create a scripts/generate_api_docs.dart
file:
import 'dart:io';
void main(List<String> args) async {
final environment = args.isNotEmpty ? args[0] : 'dev';
switch (environment) {
case 'prod':
await Process.run('dart', [
'run',
'serverpod_swagger:generate',
'--auth=jwt',
'--base-url=https://api.example.com',
]);
break;
case 'test':
await Process.run('dart', [
'run',
'serverpod_swagger:generate',
'--auth=jwt',
'--secure-endpoints=users,posts',
'--base-url=https://test-api.example.com',
]);
break;
case 'dev':
default:
await Process.run('dart', [
'run',
'serverpod_swagger:generate',
'--auth=jwt',
'--unauth',
'--base-url=http://localhost:8080',
]);
break;
}
print('Generated OpenAPI specification for $environment environment');
}
Then run it with:
dart scripts/generate_api_docs.dart prod
This approach allows you to generate the OpenAPI specification directly from your server project without needing to access the package's bin directory.
Modifying the OpenAPI Generation #
For more advanced customization, you can modify the generateOpenApiMap
function in the generate.dart
script to include additional information:
// Example of how you might customize the OpenAPI generation
Map<String, dynamic> generateOpenApiMap(SwaggerSpec spec, {String? baseUrl}) {
final openApiMap = {
'openapi': '3.0.0',
'info': {
'title': 'My Custom API Title',
'version': '2.0.0',
'description': 'Detailed documentation for my API'
},
// ... rest of the implementation
};
// ... add servers section if baseUrl is provided
return openApiMap;
}
Creating a Custom Generator #
For even more control, you can create a custom generator script that imports the necessary components from the serverpod_swagger
package. Here's an example of a custom generator script:
// custom_generator.dart
import 'dart:convert';
import 'dart:io';
import 'package:serverpod_swagger/src/services/parser.dart'; // Import the parser
void main(List<String> args) async {
// Parse your command line arguments here
String? baseUrl;
// ... other argument parsing
// Create your own SwaggerSpec or use the existing parser to generate one
final spec = SwaggerSpec(); // You'll need to populate this
// Call the customized version of generateOpenApiMap
final openApiJson = generateOpenApiMap(
spec,
baseUrl: baseUrl,
// Add your customizations here
// For example, customize the info section:
customInfo: {
'title': 'My Custom API',
'version': '2.0.0',
'description': 'My detailed API documentation'
},
);
// Write the output
final outputFile = File('apispec.json');
final prettyJson = JsonEncoder.withIndent(' ').convert(openApiJson);
outputFile.writeAsStringSync(prettyJson);
}
// Your customized version of generateOpenApiMap
Map<String, dynamic> generateOpenApiMap(
SwaggerSpec spec, {
String? baseUrl,
String? authType,
String? authDescription,
List<String>? securedEndpoints,
List<String>? unsecuredEndpoints,
String? secureSingleUrl,
bool disableAuthGlobally = false,
Map<String, String>? customHttpMethods,
Map<String, dynamic>? customInfo,
}) {
final paths = <String, dynamic>{};
// ... copy the implementation from parser.dart
// Customize the OpenAPI map
final openApiMap = {
'openapi': '3.0.0',
'info': customInfo ?? {'title': 'Serverpod API', 'version': '1.0.0'},
'paths': paths
};
// ... rest of the implementation
return openApiMap;
}
Conclusion #
The Serverpod Swagger generator provides a powerful and flexible way to create OpenAPI specifications for your Serverpod applications. By leveraging the various command-line arguments and features described in this documentation, you can create comprehensive API documentation that accurately reflects your endpoints, models, and security requirements.
Contributing #
Contributions are welcome! Please feel free to submit a Pull Request.