Massimo API SDK Client and CLI

repository·main·Indexed 41 days ago

https://github.com/platformatic/massimo

An API SDK client and CLI tool that generates fully-typed TypeScript/JavaScript clients from OpenAPI 3.x or GraphQL specifications. It supports high-performance Node.js environments via Undici and lightweight browser environments via the Fetch API. Massimo includes a Fastify plugin for attaching generated clients to request objects and provides features such as type safety, request/response interceptors, and retry logic.

Tokens
10.1K
Snippets
30
Records
38
Agent score
51%

What's inside Massimo

  1. Understand the Massimo architecture

    main

    Massimo is split into two primary packages designed to help you generate and consume type-safe API clients:

    • massimo: The core client library used for generating and utilizing the API clients in your code.
    • massimo-cli: A command-line interface (CLI) tool used to automate the generation of client code from API specifications.

    Massimo supports OpenAPI 3.x and GraphQL schemas. It can work with any HTTP API that exposes one of these two formats.

  2. Features of generated Massimo clients

    main

    When you use Massimo to generate a client, the resulting client includes several built-in capabilities:

    • Type Safety: Full TypeScript support for both requests and responses.
    • Error Handling: Built-in validation and error handling.
    • Request/Response Interceptors: Middleware support to intercept and modify requests or responses.
    • Retry Logic: Configurable mechanisms to retry failed requests.
    • Telemetry: Automatic propagation of telemetry and tracing data.
  3. Generate a GraphQL Client via CLI

    main

    Use the massimo command with the --type graphql flag to generate a client from a GraphQL schema.

    From a URL:

    massimo http://api.example.com/graphql --name myclient --type graphql
    massimo http://api.example.com/graphql --name myclient --type graphql
  4. Install the Massimo CLI and Library

    main

    To use Massimo, you can install either the CLI tool for generating clients or the library for runtime usage.

    Install the CLI tool globally:

    npm install -g massimo-cli

    Install the library as a dependency:

    npm install massimo
    npm install -g massimo-cli
    npm install massimo
  5. Generate an OpenAPI or GraphQL client with massimo CLI

    main

    Use the massimo CLI to generate a Fastify plugin that exposes a client for a remote or local OpenAPI or GraphQL API. The generated client is available on the Fastify request object.

    By default, the command creates a folder named after the client (or the -f flag value) containing the client file. If a Platformatic configuration file is present, it will be automatically updated with a clients section.

    Common usage patterns:

    • Remote OpenAPI: Provide the URL to the schema.
    • Remote GraphQL: Provide the URL to the GraphQL endpoint.
    • Local File: Provide the path to a local schema file.
    • Types Only: Use the --types-only flag to generate only the .d.ts type definitions without the implementation.
    # Generate client from remote OpenAPI schema
    $ massimo http://example.com/to/schema/file -n myclient
    
    # Generate client from remote GraphQL API
    $ massimo http://example.com/graphql -n myclient
    
    # Generate client from a local schema file
    $ massimo path/to/schema -n myclient
    
    # Generate only TypeScript types
    $ massimo http://example.com/to/schema/file --name myclient --types-only
  6. Integrate Massimo with Fastify

    main

    Massimo provides a Fastify plugin to attach the generated client to the Fastify request object.

    Basic Setup:

    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"
    });
    
    server.post("/", async (request, reply) => {
      const res = await request.movies.createMovie({ title: "foo" });
      return res;
    });
    
    server.listen({ port: 3000 });

    TypeScript Support: To get type safety on request.movies, extend the FastifyRequest interface.

    Method A: Global extension

    declare module "fastify" {
      interface FastifyRequest {
        movies: MoviesClient;
      }
    }

    Method B: Local extension

    interface MoviesRequest extends FastifyRequest {
      movies: MoviesClient;
    }
    
    server.get("/movies", async (request: MoviesRequest, reply: FastifyReply) => {
      return request.movies.getMovies();
    });
    import fastify from "fastify";
    import pltClient from "massimo/fastify-plugin.js";
    
    const server = fastify();
    
    server.register(pltClient, {
      url: "http://example.com",
      type: "openapi",
    });
    
    server.post("/", async (request, reply) => {
      const res = await request.movies.createMovie({ title: "foo" });
      return res;
    });
    
    server.listen({ port: 3000 });
  7. Generate an OpenAPI Client via CLI

    main

    Use the massimo command to generate a fully-typed TypeScript/JavaScript client from an OpenAPI 3.x specification URL or local file.

    From a URL:

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

    From a local file:

    massimo ./openapi.json --name myclient
    massimo http://api.example.com/openapi.json --name myclient
  8. Configure dynamic headers in Massimo clients

    main

    Both OpenAPI and GraphQL clients support a getHeaders option. This allows you to provide an asynchronous function that computes headers (like authentication tokens) dynamically before every request.

    In the OpenAPI client, this function receives an options object containing the url, method, headers, and telemetryHeaders.

    const client = await buildOpenAPIClient({
      url: 'https://api.example.com',
      getHeaders: async ({ url, method }) => {
        const token = await fetchNewToken();
        return { 'Authorization': `Bearer ${token}` };
      }
    });
  9. Structure of a generated GraphQL client

    main

    When using Massimo to generate a GraphQL client, the resulting code consists of two main parts: TypeScript interfaces representing your schema types and an implementation function that initializes the client using buildGraphQLClient from the massimo package.

    Generated Interfaces

    • Type Interfaces: For every non-internal OBJECT type in your GraphQL schema (excluding Query, Mutation, Subscription, Boolean, and String), Massimo generates a corresponding TypeScript interface with optional properties.
    • GraphQLQueryOptions: An interface defining the shape of a query request:
      • query: string
      • headers: Record<string, string>
      • variables: Record<string, unknown>
    • GraphQLClient: The client interface providing the core method:
      • graphql<T>(options: GraphQLQueryOptions): PromiseLike<T>

    Generated Implementation

    Massimo generates a function named generate<Name>Client (where <Name> is your schema name capitalized and camelcased). This function:

    1. Accepts PlatformaticClientOptions.
    2. Uses buildGraphQLClient with type: 'graphql'.
    3. Automatically points to a local .schema.graphql file (e.g., mySchema.schema.graphql) relative to the generated file's directory.
    4. Supports both ESM (import/export) and CommonJS (require/module.exports) based on the provided moduleFormat.
  10. Integrate Massimo as a Fastify plugin

    main

    You can integrate Massimo into a Fastify application using its official plugin. The plugin automatically builds either a GraphQL or OpenAPI client and decorates the Fastify request object with the client instance, making it available for use in your route handlers.

    Configuration Options

    When registering the plugin, you provide an opts object:

    • type: Must be either 'openapi' or 'graphql'.
    • applicationId: (Optional) If provided without a url, the plugin constructs a default URL: http://${applicationId}.plt.local.
    • url: The base URL of the service.
    • name: (Optional) The name used to decorate the request object. Defaults to 'client'.
    • getHeaders: (Optional) A function used to inject headers into requests. If provided during plugin registration, it is used for all requests. If you need to update this dynamically, use the configure<Name> method.

    Usage in Route Handlers

    The client is attached to the request object using the name specified in the options. If no name is provided, use req.client.

    Dynamic Header Configuration

    If you need to change the getHeaders function after the plugin has been initialized, use the configure<Name> method on the Fastify application instance (where <Name> is your client name with the first letter capitalized).

    import Fastify from 'fastify'
    import massimoPlugin from '@platformatic/massimo/fastify-plugin'
    
    const app = Fastify()
    
    // Register the plugin
    await app.register(massimoPlugin, {
      type: 'graphql',
      applicationId: 'my-service',
      name: 'api'
    })
    
    // Use the client in a route
    app.get('/test', async (req, reply) => {
      // The client is available on req.api
      const data = await req.api.query('{ someQuery }')
      return data
    })
    
    // Update headers dynamically
    app.configureApi({
      getHeaders: (req, reply) => ({
        Authorization: `Bearer ${req.headers.authorization}`
      })
    })