Formatter SDKs, APIs & Language Wrappers Guide | Web Formatter Blog

Language SDKs & API Wrappers for Code Formatters
A developer's guide to integrating code formatting capabilities into applications across multiple programming languages.
Introduction
Code formatters have become essential tools in modern software development, ensuring consistent code style across projects and teams. While many developers are familiar with using formatters through command-line interfaces or IDE plugins, there's a growing need to integrate formatting capabilities directly into applications, development tools, and automated workflows.
This comprehensive guide explores the world of formatter SDKs (Software Development Kits), APIs (Application Programming Interfaces), and language wrappers. Whether you're building a development tool, creating a code editor, or automating your CI/CD pipeline, understanding how to programmatically access code formatters will help you create more powerful and integrated solutions.
Why Use Formatter SDKs & APIs?
Integrating code formatters through SDKs and APIs offers numerous advantages over simply calling command-line tools:
- Performance optimization: Direct integration avoids the overhead of spawning new processes
- Fine-grained control: Access to formatter internals allows for customized behavior
- Better error handling: Programmatic access provides detailed error information
- Cross-platform compatibility: SDKs abstract away platform-specific differences
- Seamless user experience: Format code without requiring users to install additional tools
- Batch processing: Format multiple files efficiently in a single operation
- Custom workflows: Integrate formatting into complex development pipelines
Popular Code Formatters
Before diving into integration details, let's review some of the most popular code formatters and their integration capabilities.
Prettier
Prettier has become the de facto standard for formatting JavaScript, TypeScript, CSS, HTML, and many other languages. It offers both a robust JavaScript API and a CLI tool.
Integration Options:
- JavaScript API: Direct integration in Node.js applications
- CLI: Command-line interface for scripts and automation
- REST API: Third-party services offering Prettier as a service
- Language Wrappers: Community-maintained wrappers for various languages
Basic JavaScript API example:
const prettier = require("prettier");
// Format code with specific options
async function formatCode(code) {
const formatted = await prettier.format(code, {
parser: "babel",
semi: false,
singleQuote: true,
trailingComma: "none"
});
return formatted;
}
// Usage
const code = `function hello() {
console.log("Hello world")
}`;
formatCode(code)
.then(formatted => console.log(formatted))
.catch(error => console.error(error));
Black (Python)
Black is an uncompromising Python code formatter that focuses on consistency with minimal configuration options.
Integration Options:
- Python API: Import and use directly in Python applications
- CLI: Command-line tool for scripts and automation
- Pre-commit hook: Git integration
- REST API: Third-party services wrapping Black functionality
Python API example:
import black
# Format a string of Python code
def format_python(code):
try:
formatted_code = black.format_str(code, mode=black.Mode())
return formatted_code
except Exception as e:
print(f"Error: {e}")
return code
# Example usage
python_code = """
def example_function(param1, param2, param3 = None):
result = param1+param2
if param3:
result += param3
return result
"""
formatted = format_python(python_code)
print(formatted)
gofmt (Go)
Go's built-in formatter enforces a standardized code style across all Go projects, making it a core part of the language ecosystem.
Integration Options:
- Go API: Native Go package for direct integration
- CLI: Command-line tool included with Go installation
- Language Bindings: FFI bindings for other languages
Go API example:
package main
import (
"fmt"
"strings"
"go/format"
)
// FormatGoCode formats Go code using go/format
func FormatGoCode(code string) (string, error) {
formatted, err := format.Source([]byte(code))
if err != nil {
return code, err
}
return string(formatted), nil
}
func main() {
code := `package main
import (
"fmt"
"strings"
)
func main( ) {
fmt.Println( "Hello, World!")
}
`
formatted, err := FormatGoCode(code)
if err != nil {
fmt.Printf("Error: %v\\n", err)
return
}
fmt.Println("Formatted code:")
fmt.Println(strings.Repeat("-", 30))
fmt.Println(formatted)
}
rustfmt (Rust)
Rustfmt is the official code formatter for Rust, ensuring consistent style across the Rust ecosystem.
Integration Options:
- Rust API: Native Rust crate for direct integration
- CLI: Command-line tool installed via Cargo
- Language Bindings: Limited bindings for other languages
Rust API example:
use rustfmt_nightly::{Config, Input, Session};
use std::path::Path;
fn main() {
// Create a default configuration
let config = Config::default();
// Create a formatting session
let mut session = Session::new(config, None);
// Format a file
let input = Input::File(Path::new("src/main.rs").to_path_buf());
session.format(input).unwrap();
// Get formatting issues
let report = session.report();
println!("{:?}", report);
}
ClangFormat (C/C++)
ClangFormat is a powerful formatter for C, C++, Objective-C, and other languages in the C family.
Integration Options:
- C++ API: LibFormat library for direct integration
- CLI: Command-line tool for scripts and automation
- Language Bindings: Bindings available for several languages
C++ API example (simplified):
#include "clang/Format/Format.h"
#include "llvm/Support/FileSystem.h"
#include
int main() {
// Source code to format
std::string code = "int main() { int x = 5; return 0; }";
// Format options
clang::format::FormatStyle style = clang::format::getLLVMStyle();
style.ColumnLimit = 80;
// Format the code
auto result = clang::format::reformat(style, code, {0, code.size()});
// Output the formatted code
std::cout << result << std::endl;
return 0;
}
SDK Integration Guide
Now let's explore how to integrate formatter SDKs into applications built with different programming languages.
JavaScript/TypeScript
JavaScript and TypeScript applications can easily integrate with most formatters, especially Prettier.
// Install: npm install prettier
import * as prettier from 'prettier';
async function formatCode(code, filePath) {
// Determine parser based on file extension
const parser = filePath.endsWith('.js') ? 'babel' :
filePath.endsWith('.ts') ? 'typescript' :
filePath.endsWith('.json') ? 'json' :
filePath.endsWith('.css') ? 'css' :
filePath.endsWith('.html') ? 'html' :
'babel'; // Default to babel
// Format the code
return prettier.format(code, {
parser,
printWidth: 80,
tabWidth: 2,
singleQuote: true,
trailingComma: 'es5',
bracketSpacing: true,
semi: true,
arrowParens: 'avoid',
});
}
// Example usage
formatCode("function hello() {return 'world'}", "example.js")
.then(result => console.log(result));
For more complex scenarios, you might want to create a formatting service:
// formatter-service.ts
import * as prettier from 'prettier';
import * as fs from 'fs/promises';
import * as path from 'path';
export class FormatterService {
private configCache: Map = new Map();
async formatFile(filePath: string): Promise {
const content = await fs.readFile(filePath, 'utf8');
const options = await this.getConfigForFile(filePath);
return prettier.format(content, {
...options,
filepath: filePath,
});
}
async formatCode(code: string, parser: prettier.BuiltInParserName): Promise {
return prettier.format(code, { parser });
}
private async getConfigForFile(filePath: string): Promise {
const dir = path.dirname(filePath);
if (this.configCache.has(dir)) {
return this.configCache.get(dir)!;
}
const options = await prettier.resolveConfig(filePath) || {};
this.configCache.set(dir, options);
return options;
}
}
Python
Python applications can integrate with Black, YAPF, autopep8, and other Python formatters.
# Install: pip install black
import black
from pathlib import Path
def format_python_code(code, line_length=88):
"""Format Python code using Black."""
try:
mode = black.Mode(
line_length=line_length,
string_normalization=True,
is_pyi=False,
)
formatted_code = black.format_str(code, mode=mode)
return {
"success": True,
"formatted_code": formatted_code
}
except Exception as e:
return {
"success": False,
"error": str(e)
}
def format_python_file(file_path, line_length=88):
"""Format a Python file using Black."""
path = Path(file_path)
try:
code = path.read_text()
result = format_python_code(code, line_length)
if result["success"]:
path.write_text(result["formatted_code"])
return result
except Exception as e:
return {
"success": False,
"error": str(e)
}
# Example usage
result = format_python_code("""
def hello(name):
return f"Hello, {name}!"
""")
print(result)
Java
Java applications can integrate with formatters like google-java-format or Eclipse's formatter.
// Add dependency: com.google.googlejavaformat:google-java-format:1.15.0
import com.google.googlejavaformat.java.Formatter;
import com.google.googlejavaformat.java.FormatterException;
import com.google.googlejavaformat.java.JavaFormatterOptions;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class CodeFormatter {
private final Formatter formatter;
public CodeFormatter() {
JavaFormatterOptions options = JavaFormatterOptions.builder()
.style(JavaFormatterOptions.Style.GOOGLE)
.build();
this.formatter = new Formatter(options);
}
public String formatCode(String code) throws FormatterException {
return formatter.formatSource(code);
}
public void formatFile(String filePath) throws IOException, FormatterException {
Path path = Paths.get(filePath);
String content = Files.readString(path);
String formatted = formatter.formatSource(content);
Files.writeString(path, formatted);
}
public static void main(String[] args) {
try {
CodeFormatter formatter = new CodeFormatter();
String code = "public class Example { public static void main(String[] args) { System.out.println(\"Hello\"); } }";
String formatted = formatter.formatCode(code);
System.out.println(formatted);
} catch (FormatterException e) {
e.printStackTrace();
}
}
}
Go
Go applications can use the standard library's go/format package to format Go code.
package main
import (
"fmt"
"go/format"
"io/ioutil"
"log"
"os"
)
// FormatGoCode formats Go source code
func FormatGoCode(code []byte) ([]byte, error) {
return format.Source(code)
}
// FormatGoFile formats a Go source file
func FormatGoFile(filePath string) error {
// Read the file
content, err := ioutil.ReadFile(filePath)
if err != nil {
return err
}
// Format the content
formatted, err := FormatGoCode(content)
if err != nil {
return err
}
// Write back to the file
return ioutil.WriteFile(filePath, formatted, 0644)
}
func main() {
if len(os.Args) < 2 {
fmt.Println("Usage: go run formatter.go ")
os.Exit(1)
}
filePath := os.Args[1]
err := FormatGoFile(filePath)
if err != nil {
log.Fatalf("Error formatting file: %v", err)
}
fmt.Printf("Successfully formatted %s\n", filePath)
}
REST API Integration
For scenarios where direct SDK integration isn't feasible, REST APIs provide a language-agnostic way to access formatting capabilities.
Common API Endpoints
Most formatter REST APIs follow similar patterns for their endpoints:
Endpoint | Method | Description |
---|---|---|
/api/format | POST | Format code with specified options |
/api/check | POST | Check if code needs formatting |
/api/formatters | GET | List available formatters and languages |
/api/options/:language | GET | Get available options for a specific language |
Authentication Methods
REST APIs typically use one of these authentication methods:
-
API Key: Passed in headers (e.g.,{" "}
X-API-Key
) or as a query parameter - OAuth 2.0: For more secure integrations, especially with user-specific permissions
- JWT: For stateless authentication with claims about the client
- Basic Auth: Simple username/password authentication (less common for APIs)
Rate Limits & Quotas
Most formatter APIs implement rate limiting to prevent abuse:
- Request-based limits: e.g., 100 requests per minute
- Volume-based quotas: e.g., 1MB of code per day
- Concurrency limits: e.g., maximum 5 concurrent requests
APIs typically include rate limit information in response headers:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1618884000
API Request Examples
Example of formatting JavaScript code using a REST API:
Language Wrappers
Language wrappers provide a native interface to formatters in languages they weren't originally designed for.
Creating Custom Wrappers
When creating a wrapper for a formatter, consider these approaches:
-
Process-based wrappers: Execute the formatter
as a subprocess and capture output
- Simplest approach but has performance overhead
- Works with any formatter that has a CLI
-
FFI (Foreign Function Interface): Directly call
formatter libraries from your language
- Better performance than process-based approach
- More complex to implement and maintain
- May require native bindings or compilation steps
- Not available for all language pairs
-
WebAssembly: Use WASM versions of formatters
for cross-platform compatibility
- Good balance of performance and portability
- Works well in browsers and Node.js environments
- Growing ecosystem with more formatters available
- May have limitations compared to native implementations
Best Practices & Common Pitfalls
When integrating code formatters into your applications, consider these best practices:
-
Performance considerations
- Use native libraries when possible instead of spawning processes
- Implement caching mechanisms for formatter configurations
- Consider asynchronous processing for large files
- Batch formatting operations when processing multiple files
- Better performance than process-based approach
-
Error handling
- Always handle formatting errors gracefully
- Provide meaningful error messages to users
- Consider fallback strategies when formatting fails
- Log errors for diagnostics and improvement
-
Configuration management
- Respect project-specific configuration files
- Provide sensible defaults when configurations are missing
- Allow overriding configurations programmatically
- Cache configurations for better performance
-
Security considerations
- Validate and sanitize input code
- Implement timeouts to prevent resource exhaustion
- Set limits on input code size
- Be careful with third-party formatters that could execute code
-
Testing
- Write unit tests for your formatting integration
- Include edge cases like empty files or files with syntax errors
- Test with various configuration options
- Benchmark performance with large codebases
Conclusion
Integrating code formatters into your applications can significantly improve code quality and developer experience. By following the examples and best practices in this guide, you can add formatting capabilities to your tools across various programming languages.
Whether you're building an IDE plugin, a CI tool, or a code review system, these SDKs and API wrappers provide a solid foundation for working with popular code formatters in a programmatic way.
As formatting tools evolve, remember to keep your integrations updated to benefit from the latest improvements and maintain compatibility with new language features.