Skip to content

platformatic/massimo

Massimo Logo

Massimo

npm version License

Massimo is an API SDK client and CLI tool for creating fully-typed clients for remote OpenAPI or GraphQL APIs. Generate TypeScript/JavaScript clients with automatic type inference, authentication support, and seamless integration with popular frontend frameworks.

Why "Massimo"? The name is inspired by Massimo Troisi, the beloved Italian actor from the film "Il Postino" (The Postman). Just as the postman in the movie delivered messages and connected people, Massimo delivers API connections and bridges the gap between services, making communication between applications as effortless and poetic as Troisi's unforgettable performance.

🚀 Features

  • OpenAPI Client Generation: Create fully-typed clients from OpenAPI 3.x specifications
  • GraphQL Client Generation: Generate clients from GraphQL schemas with type support
  • TypeScript First: Full TypeScript support with automatic type generation
  • Framework Agnostic: Works with any Node.js application or frontend framework
  • Authentication Support: Built-in support for headers, tokens, and custom authentication
  • Fastify Plugin: Ready-to-use Fastify plugin for seamless integration

📦 Installation

CLI Tool

npm install -g massimo-cli

Library

npm install massimo

🛠️ Quick Start

Generate an OpenAPI Client

massimo http://api.example.com/openapi.json --name myclient

Generate a GraphQL Client

massimo http://api.example.com/graphql --name myclient --type graphql

Use the Generated Client

JavaScript/TypeScript (Node.js, Undici-based):

The undici-based client is preferred for Node.js environments when you need:

  • Maximum performance - Undici is the fastest HTTP/1.1 client for Node.js
  • Advanced connection management with pooling, keep-alive, and pipelining
  • HTTP/2 support with full capabilities
  • Node.js optimized runtime (bundled with Node.js 18+)
  • Advanced features like interceptors, custom dispatchers, and WebSocket support
  • Efficient streaming with pipeline and stream methods for large payloads
  • Comprehensive error types and network-level error handling
// Generate Node.js client
// massimo http://api.example.com/openapi.json --name myclient

import myClient from "./myclient/myclient.mjs";

const client = await myClient({
  url: "https://api.example.com",
});

// OpenAPI
const users = await client.getUsers();
const newUser = await client.createUser({ name: "John Doe" });

// GraphQL
const result = await client.graphql({
  query: "query { users { id name } }",
});

Frontend Client (Browser, Fetch-based):

The fetch-based client is preferred for browser environments and when you need:

  • Browser compatibility with native Fetch API (Undici is Node.js-only)
  • Zero dependencies for minimal bundle size
  • Isomorphic code that runs in browsers and Node.js
  • Maximum compatibility across all JavaScript runtimes
  • Simple HTTP requests without advanced configuration
  • Publishing to npm with broadest runtime support
  • Independence from specific undici version features
// Generate frontend client
// massimo http://api.example.com/openapi.json --frontend --name myclient

// Option 1: Named operations
import { setBaseUrl, getUsers, createUser } from "./myclient/api.mjs";

setBaseUrl("https://api.example.com");

// Make type-safe API calls
const users = await getUsers({});
const newUser = await createUser({
  name: "Jane Doe",
  email: "[email protected]",
});

// Option 2: Factory method
import buildClient from "./myclient/api.mjs";

const client = buildClient("https://api.example.com");

// Set default headers for authentication
client.setDefaultHeaders({
  Authorization: "Bearer token",
});

const users = await client.getUsers({});
const user = await client.getUserById({ id: "123" });

Fastify Plugin:

import fastify from "fastify";
import pltClient from "massimo/fastify-plugin.js";

const server = fastify();

server.register(pltClient, {
  url: "http://example.com",
  type: "openapi", // or "graphql"
});

// OpenAPI
server.post("/", async (request, reply) => {
  const res = await request.movies.createMovie({ title: "foo" });
  return res;
});

server.listen({ port: 3000 });

Note that you would need to install massimo as a dependency.

TypeScript with Fastify Plugin:

Massimo generates full TypeScript support for Fastify. To add types information to your plugin, you can either extend the FastifyRequest interface globally or locally.

import { type MoviesClient } from "./movies/movies.ts";
import fastify, { type FastifyRequest } from "fastify";
import pltClient from "massimo/fastify-plugin.js";

const server = fastify();
server.register(pltClient, {
  url: "http://example.com",
  type: "openapi", // or "graphql"
});

// Method A: extend the interface globally
declare module "fastify" {
  interface FastifyRequest {
    movies: MoviesClient;
  }
}

server.get("/movies", async (request: FastifyRequest, reply: FastifyReply) => {
  return request.movies.getMovies();
});

// Method B: use a local request extension
interface MoviesRequest extends FastifyRequest {
  movies: MoviesClient;
}

server.get("/movies", async (request: MoviesRequest, reply: FastifyReply) => {
  return request.movies.getMovies();
});

📚 Documentation

CLI Commands

# Generate client from URL
massimo <url> --name <client-name>

# Generate from local file
massimo ./openapi.json --name myclient

# Generate only TypeScript types
massimo <url> --name myclient --types-only

# Specify client type (auto-detected by default)
massimo <url> --name myclient --type openapi
massimo <url> --name myclient --type graphql

# Custom output folder
massimo <url> --name myclient --folder ./clients

# Specify module format (valid values: esm, cjs)
massimo <url> --name myclient --module esm
massimo <url> --name myclient --module cjs

# Force TypeScript declaration file extensions (.d.mts/.d.cts)
massimo <url> --name myclient --type-extension

Module Format Detection

Massimo automatically detects and generates the appropriate module format (ESM or CommonJS) for your clients:

Auto-Detection (Default Behavior)

When no --module flag is specified, Massimo:

  1. Searches for the nearest package.json starting from the output directory and walking up the directory tree
  2. If package.json is found, checks the type field:
    • If "type": "module" → Generates ESM files (.mjs, .d.mts)
    • If "type" is missing or any other value → Generates CommonJS files (.cjs, .d.ts)
  3. If no package.json is found → Defaults to ESM files (.mjs, .d.mts)
  4. Generated files:
    • ESM: client.mjs + client.d.mts + package.json with "type": "module"
    • CommonJS: client.cjs + client.d.ts + package.json without "type" field

Explicit Module Format

When using the --module flag:

# Force ESM generation
massimo <url> --name myclient --module esm

# Force CommonJS generation  
massimo <url> --name myclient --module cjs

This overrides any auto-detection and generates files in the specified format.

Generated File Extensions

Module Format Implementation Types Package.json
ESM .mjs .d.mts "type": "module"
CommonJS .cjs .d.cts No "type" field

TypeScript Declaration File Extensions

Massimo uses intelligent logic to determine the appropriate TypeScript declaration file extension (.d.ts, .d.mts, or .d.cts):

Type Extension Determination Rules

  1. When --type-extension is specified:

    • Always generates module-specific extensions
    • ESM format → .d.mts
    • CommonJS format → .d.cts
  2. When --type-extension is NOT specified:

    • If --module flag is provided:
      • Compares the specified module format with the parent package.json type
      • If they match → generates .d.ts (universal compatibility)
      • If they differ → generates module-specific extension (.d.mts or .d.cts)
    • If no --module flag is provided:
      • Always generates .d.ts for maximum compatibility
  3. Parent Package.json Detection:

    • Searches up the directory tree from the output location
    • Stops at the first package.json found
    • ESM parent: "type": "module"
    • CommonJS parent: no type field or "type": "commonjs"

Examples

# Always use module-specific extensions
massimo <url> --name myclient --type-extension

# Generate .d.ts when module format matches parent package.json
massimo <url> --name myclient --module esm  # In ESM project → .d.ts
massimo <url> --name myclient --module cjs  # In CommonJS project → .d.ts

# Generate module-specific when formats differ
massimo <url> --name myclient --module esm  # In CommonJS project → .d.mts
massimo <url> --name myclient --module cjs  # In ESM project → .d.cts

# Auto-detect everything (no module flag) → always .d.ts  
massimo <url> --name myclient  # → .d.ts (maximum compatibility)

# Explicit module generates module-specific when no parent package.json
massimo <url> --name myclient --module esm  # No parent pkg → .d.mts
massimo <url> --name myclient --module cjs  # No parent pkg → .d.cts

Client Options

interface ClientOptions {
  url: string; // Base URL for the API
  headers?: Record<string, string>; // Default headers
  timeout?: number; // Request timeout in ms
  throwOnError?: boolean; // Throw on HTTP errors
}

const client = await generateMyClient({
  url: "https://api.example.com",
  headers: {
    Authorization: "Bearer token",
  },
  timeout: 5000,
  throwOnError: true,
});

Authentication

Configure authentication headers dynamically:

// In a Fastify plugin
app.configureMyClient({
  async getHeaders(req, reply) {
    return {
      Authorization: `Bearer ${req.user.token}`,
      "X-User-ID": req.user.id,
    };
  },
});

TypeScript Support

Massimo generates full TypeScript definitions:

// Generated types for OpenAPI
interface GetUsersResponse {
  id: string;
  name: string;
  email: string;
}

interface CreateUserRequest {
  name: string;
  email: string;
}

interface Client {
  getUsers(): Promise<GetUsersResponse[]>;
  createUser(req: CreateUserRequest): Promise<GetUsersResponse>;
}

// Generated types for GraphQL
interface User {
  id?: string;
  name?: string;
  email?: string;
}

interface GraphQLClient {
  graphql<T>(options: {
    query: string;
    variables?: Record<string, unknown>;
    headers?: Record<string, string>;
  }): Promise<T>;
}

🏗️ Architecture

Massimo consists of two main packages:

  • massimo: Core client library for generating and using API clients
  • massimo-cli: Command-line tool for generating client code

Supported APIs

  • OpenAPI 3.x: Full support for OpenAPI specifications with automatic client generation
  • GraphQL: Support for GraphQL schemas with type generation
  • Any HTTP API: As long as it exposes OpenAPI or GraphQL schemas

Generated Client Features

  • Type Safety: Full TypeScript support with request/response typing
  • Error Handling: Built-in error handling and validation
  • Request/Response Interceptors: Middleware support for modifying requests/responses
  • Retry Logic: Configurable retry mechanisms
  • Telemetry: Automatic telemetry and tracing propagation

🤝 Contributing

We welcome contributions! Please see our Contributing Guide for details.

Development Setup

git clone https://github.com/platformatic/massimo.git
cd massimo
pnpm install
pnpm test

Running Tests

# Run all tests
pnpm test

# Run tests for specific package
cd packages/massimo
npm test

📄 License

Licensed under Apache 2.0.

🔗 Links


Made with ❤️ by the Platformatic team.

About

Massimo is an API SDK client and CLI tool for creating clients for remote OpenAPI or GraphQL APIs

Resources

License

Code of conduct

Contributing

Stars

Watchers

Forks

Packages

No packages published

Contributors 5