npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@qualisero/openapi-endpoint

v0.12.0

Published

Type-safe OpenAPI integration for Vue Query

Readme

Vue OpenAPI Query

npm version CI License: MIT npm bundle size

Turns your openapi.json into typesafe API composables using Vue Query (TanStack Query): guaranteeing that your frontend and backend share the same contract.

Overview

Let's you get TanStack Vue Query composables that enforce consistency (name of endpoints, typing) with your API's openapi.json file:

const { data, isLoading } = api.useQuery(OperationId.getPet, { petId: '123' })

const createPetMutation = api.useMutation(OperationId.createPet)
createPetMutation.mutate({ data: { name: 'Fluffy', species: 'cat' } })

Installation

npm install @qualisero/openapi-endpoint

Code Generation

This package includes a command-line tool to generate TypeScript types and operation definitions from your OpenAPI specification:

# Generate from local file
npx @qualisero/openapi-endpoint ./api/openapi.json ./src/generated

# Generate from remote URL
npx @qualisero/openapi-endpoint https://api.example.com/openapi.json ./src/api

This will generate two files in your specified output directory:

  • openapi-types.ts - TypeScript type definitions for your API
  • api-operations.ts - Streamlined operation definitions combining metadata and types

Usage

1. Initialize the package

// api/init.ts
import { useOpenApi } from '@qualisero/openapi-endpoint'
import axios from 'axios'

// Import your generated operations (includes both metadata and types)
import { OperationId, openApiOperations, type OpenApiOperations } from './generated/api-operations'

// Create axios instance
const axiosInstance = axios.create({
  baseURL: 'https://api.example.com',
})

// Initialize the package with the streamlined operations
const api = useOpenApi<OpenApiOperations>({
  operations: openApiOperations,
  axios: axiosInstance,
})

// Export for use in other parts of your application
export { api, OperationId }

2. Use the API in your components

// In your Vue components
import { api, OperationId } from './api/init'

// Use queries for GET operations
const { data: pets, isLoading } = api.useQuery(OperationId.listPets)
const { data: pet } = api.useQuery(OperationId.getPet, { petId: '123' })

// Use mutations for POST/PUT/PATCH/DELETE operations
const createPetMutation = api.useMutation(OperationId.createPet)

// Execute mutations
await createPetMutation.mutateAsync({
  data: { name: 'Fluffy', species: 'cat' },
})

Advanced Usage

Reactive Query Parameters

The library supports type-safe, reactive query parameters that automatically trigger refetches when their values change:

import { ref, computed } from 'vue'
import { api, OperationId } from './api/init'

// Static query parameters
const { data: pets } = api.useQuery(OperationId.listPets, {
  queryParams: { limit: 10 },
})
// Results in: GET /pets?limit=10

// Reactive query parameters with computed
const limit = ref(10)
const status = ref<'available' | 'pending' | 'sold'>('available')

const petsQuery = api.useQuery(OperationId.listPets, {
  queryParams: computed(() => ({
    limit: limit.value,
    status: status.value,
  })),
})

// When limit or status changes, query automatically refetches
limit.value = 20
status.value = 'pending'
// Query refetches with: GET /pets?limit=20&status=pending

// Combine with path parameters
const userPetsQuery = api.useQuery(
  OperationId.listUserPets,
  computed(() => ({ userId: userId.value })),
  {
    queryParams: computed(() => ({
      includeArchived: includeArchived.value,
    })),
  },
)
// Results in: GET /users/user-123/pets?includeArchived=false

Key Features:

  • Type-safe: Query parameters are typed based on your OpenAPI specification
  • Reactive: Supports ref, computed, and function-based values
  • Automatic refetch: Changes to query params trigger automatic refetch via TanStack Query's key mechanism
  • Backward compatible: Works alongside existing axiosOptions.params

Automatic Operation Type Detection with api.useEndpoint

The api.useEndpoint method automatically detects whether an operation is a query (GET/HEAD/OPTIONS) or mutation (POST/PUT/PATCH/DELETE) based on the HTTP method defined in your OpenAPI specification:

import { ref, computed } from 'vue'
import { api, OperationId } from './api/init'

// Automatically becomes a query for GET operations
const listEndpoint = api.useEndpoint(OperationId.listPets)
// TypeScript knows this has query properties like .data, .isLoading, .refetch()

// Automatically becomes a mutation for POST operations
const createEndpoint = api.useEndpoint(OperationId.createPet)
// TypeScript knows this has mutation properties like .mutate(), .mutateAsync()

// Use the endpoints according to their detected type
const petData = listEndpoint.data // Query data
await createEndpoint.mutateAsync({ data: { name: 'Fluffy' } }) // Mutation execution

Automatic Cache Management and Refetching

By default, mutations automatically:

  1. Update cache for matching queries with returned data
  2. Invalidate them to trigger a reload
  3. Invalidate matching list endpoints
// Default behavior: automatic cache management
const createPet = api.useMutation(OperationId.createPet)
// No additional configuration needed - cache management is automatic

// Manual control over cache invalidation
const updatePet = api.useMutation(OperationId.updatePet, {
  dontInvalidate: true, // Disable automatic invalidation
  dontUpdateCache: true, // Disable automatic cache updates
  invalidateOperations: [OperationId.listPets], // Manually specify operations to invalidate
})

// Refetch specific endpoints after mutation
const petListQuery = api.useQuery(OperationId.listPets)
const createPetWithRefetch = api.useMutation(OperationId.createPet, {
  refetchEndpoints: [petListQuery], // Manually refetch these endpoints
})

File Upload Support with Multipart/Form-Data

The library supports file uploads through endpoints that accept multipart/form-data content type. For these endpoints, you can pass either a FormData object or the schema-defined object structure:

// Example file upload endpoint usage
async function uploadPetPicture(petId: string, file: File) {
  const formData = new FormData()
  formData.append('file', file)

  const uploadMutation = api.useMutation(OperationId.uploadPetPic, { petId })

  return uploadMutation.mutateAsync({
    data: formData,
    axiosOptions: {
      headers: {
        'Content-Type': 'multipart/form-data',
      },
    },
  })
}

// Alternative: using the object structure (if your API supports binary strings)
async function uploadPetPictureAsString(petId: string, binaryData: string) {
  const uploadMutation = api.useMutation(OperationId.uploadPetPic, { petId })

  return uploadMutation.mutateAsync({
    data: {
      file: binaryData, // Binary data as string
    },
  })
}

// Complete example with error handling and cache invalidation
async function handleFileUpload(event: Event, petId: string) {
  const files = (event.target as HTMLInputElement).files
  if (!files || files.length === 0) return

  const file = files[0]
  const formData = new FormData()
  formData.append('file', file)

  const uploadMutation = api.useMutation(
    OperationId.uploadPetPic,
    { petId },
    {
      invalidateOperations: [OperationId.getPet, OperationId.listPets],
      onSuccess: (data) => {
        console.log('Upload successful:', data)
      },
      onError: (error) => {
        console.error('Upload failed:', error)
      },
    },
  )

  try {
    await uploadMutation.mutateAsync({ data: formData })
  } catch (error) {
    console.error('Upload error:', error)
  }
}

Reactive Enabling/Disabling Based on Path Parameters

One powerful feature is chaining queries where one query provides the parameters for another:

import { ref, computed } from 'vue'

// First query to get user information
const userQuery = api.useQuery(OperationId.getUser, { userId: 123 })

// Second query that depends on the first query's result
const userPetsQuery = api.useQuery(
  OperationId.listUserPets,
  computed(() => ({
    userId: userQuery.data.value?.id, // Chain: use ID from first query
  })),
)

// Reactive parameter example
const selectedPetId = ref<string | undefined>(undefined)

// Query automatically enables/disables based on parameter availability
const petQuery = api.useQuery(
  OperationId.getPet,
  computed(() => ({ petId: selectedPetId.value })),
)

// Query is automatically disabled when petId is null/undefined
console.log(petQuery.isEnabled.value) // false when selectedPetId.value is null

// Enable the query by setting the parameter
selectedPetId.value = '123'
console.log(petQuery.isEnabled.value) // true when selectedPetId.value is set

// Complex conditional enabling
const userId = ref<string>('user1')
const shouldFetchPets = ref(true)

const userPetsQuery = api.useQuery(
  OperationId.listUserPets,
  computed(() => ({ userId: userId.value })),
  {
    enabled: computed(
      () => shouldFetchPets.value, // Additional business logic
    ),
  },
)

Reactive Query Parameters with Refs

import { ref } from 'vue'

// Use reactive query parameters
const limit = ref(10)
const petsQuery = api.useQuery(OperationId.listPets, {
  queryParams: { limit: limit.value },
})

// When limit changes, the query automatically refetches
limit.value = 20 // Query refetches with new parameter
// Results in: GET /pets?limit=20

API Documentation

Full API documentation is available at https://qualisero.github.io/openapi-endpoint/. The documentation includes detailed information about all methods, types, and configuration options.

Contributing

Contributions are welcome! Please read our contributing guidelines and ensure all tests pass.

License

MIT