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 🙏

© 2026 – Pkg Stats / Ryan Hefner

openapi-vue-query

v0.0.5

Published

Fast, type-safe @tanstack/vue-query client to work with your OpenAPI schema.

Readme

openapi-vue-query

A type-safe and lightweight (1kb) wrapper around @tanstack/vue-query for consuming OpenAPI schemas in Vue.js applications.

Built on top of openapi-fetch and openapi-typescript, providing:

  • 100% type-safe - No typos in URLs, params, request bodies, or responses
  • Zero runtime overhead - Types are generated at build time
  • Automatic type inference - No manual typing of your API
  • Eliminates any types that hide bugs
  • Eliminates as type overrides that can hide bugs
  • Vue 3 Composition API support with full reactivity
  • All @tanstack/vue-query features - Caching, background updates, optimistic updates, etc.

🚀 Quick Start

Installation

npm i openapi-vue-query openapi-fetch
npm i -D openapi-typescript typescript

Generate Types

Generate TypeScript types from your OpenAPI schema:

npx openapi-typescript ./path/to/api/v1.yaml -o ./src/lib/api/v1.d.ts

Basic Usage

<script setup lang="ts">
import createFetchClient from "openapi-fetch";
import createClient from "openapi-vue-query";
import type { paths } from "./lib/api/v1"; // generated by openapi-typescript

// Create clients
const fetchClient = createFetchClient<paths>({
  baseUrl: "https://api.example.com/v1",
});
const $api = createClient(fetchClient);

// Use in your components
const { data, error, isPending } = $api.useQuery(
  "get",
  "/blogposts/{post_id}",
  {
    params: {
      path: { post_id: 5 },
    },
  },
);
</script>

<template>
  <div>
    <template v-if="isPending"> Loading... </template>
    <template v-else-if="error"> Error: {{ error.message }} </template>
    <template v-else>
      <h1>{{ data?.title }}</h1>
      <p>{{ data?.content }}</p>
    </template>
  </div>
</template>

🔧 API Reference

useQuery

For fetching data. Wraps @tanstack/vue-query's useQuery.

<script setup lang="ts">
const { data, error, isPending, refetch } = $api.useQuery(
  "get",
  "/users/{id}",
  {
    params: { path: { id: "123" } },
  },
  {
    // Standard vue-query options
    enabled: computed(() => !!userId.value),
    staleTime: 5 * 60 * 1000, // 5 minutes
  },
);
</script>

useMutation

For creating, updating, or deleting data. Wraps @tanstack/vue-query's useMutation.

<script setup lang="ts">
const createPost = $api.useMutation("post", "/posts", {
  onSuccess: () => {
    // Invalidate and refetch posts
    queryClient.invalidateQueries({ queryKey: ["get", "/posts"] });
  },
});

const handleSubmit = () => {
  createPost.mutate({
    body: {
      title: "New Post",
      content: "Post content...",
    },
  });
};
</script>

<template>
  <button @click="handleSubmit" :disabled="createPost.isPending">
    {{ createPost.isPending ? "Creating..." : "Create Post" }}
  </button>
</template>

useInfiniteQuery

For paginated data. Wraps @tanstack/vue-query's useInfiniteQuery.

<script setup lang="ts">
const { data, fetchNextPage, hasNextPage, isFetchingNextPage } =
  $api.useInfiniteQuery(
    "get",
    "/posts",
    {
      params: {
        query: { limit: 10 },
      },
    },
    {
      getNextPageParam: (lastPage, allPages) => {
        return lastPage.hasMore ? allPages.length : undefined;
      },
      pageParamName: "page", // Query parameter name for pagination
    },
  );
</script>

<template>
  <div>
    <div v-for="page in data?.pages" :key="page.page">
      <article v-for="post in page.posts" :key="post.id">
        <h2>{{ post.title }}</h2>
        <p>{{ post.excerpt }}</p>
      </article>
    </div>

    <button
      v-if="hasNextPage"
      @click="fetchNextPage"
      :disabled="isFetchingNextPage"
    >
      {{ isFetchingNextPage ? "Loading..." : "Load More" }}
    </button>
  </div>
</template>

queryOptions

For creating reusable query configurations.

// composables/usePostQuery.ts
export function usePostQuery(postId: Ref<string>) {
  return $api.queryOptions("get", "/posts/{id}", {
    params: { path: { id: postId } },
  });
}

// In component
const postQuery = usePostQuery(postId);
const { data } = useQuery(postQuery);

🎯 Advanced Usage

With Composables

Create reusable API composables:

// composables/useAuth.ts
export function useAuth() {
  const login = $api.useMutation("post", "/auth/login");
  const logout = $api.useMutation("post", "/auth/logout");

  const { data: user } = $api.useQuery("get", "/auth/me", undefined, {
    enabled: computed(() => !!getToken()),
  });

  return {
    user: readonly(user),
    login: login.mutate,
    logout: logout.mutate,
    isLoading: computed(() => login.isPending || logout.isPending),
  };
}

Error Handling

<script setup lang="ts">
const { data, error } = $api.useQuery("get", "/posts/{id}", {
  params: { path: { id: "123" } },
});

// Error is fully typed based on your OpenAPI schema
watchEffect(() => {
  if (error.value) {
    if (error.value.status === 404) {
      console.log("Post not found");
    } else if (error.value.status === 401) {
      console.log("Unauthorized");
    }
  }
});
</script>

Custom Fetch Client Configuration

import createFetchClient from "openapi-fetch";
import createClient from "openapi-vue-query";

const fetchClient = createFetchClient<paths>({
  baseUrl: "https://api.example.com/v1",
  headers: {
    "User-Agent": "MyApp/1.0",
  },
});

// Add auth middleware
fetchClient.use({
  onRequest({ request }) {
    const token = getToken();
    if (token) {
      request.headers.set("Authorization", `Bearer ${token}`);
    }
  },
  onResponse({ response }) {
    if (response.status === 401) {
      // Handle unauthorized
      logout();
    }
  },
});

export const $api = createClient(fetchClient);

🤝 Integration with Vue Query

openapi-vue-query is fully compatible with @tanstack/vue-query. You can use all vue-query features:

<script setup lang="ts">
import { useQueryClient } from "@tanstack/vue-query";

const queryClient = useQueryClient();

// Prefetch data
onMounted(() => {
  queryClient.prefetchQuery($api.queryOptions("get", "/posts"));
});

// Optimistic updates
const updatePost = $api.useMutation("put", "/posts/{id}", {
  onMutate: async (variables) => {
    await queryClient.cancelQueries({
      queryKey: ["get", "/posts", variables.params.path.id],
    });

    const previousPost = queryClient.getQueryData([
      "get",
      "/posts",
      variables.params.path.id,
    ]);

    queryClient.setQueryData(["get", "/posts", variables.params.path.id], {
      ...previousPost,
      ...variables.body,
    });

    return { previousPost };
  },
  onError: (error, variables, context) => {
    if (context?.previousPost) {
      queryClient.setQueryData(
        ["get", "/posts", variables.params.path.id],
        context.previousPost,
      );
    }
  },
});
</script>

📚 Examples

🔗 Related Projects

📖 Documentation

For detailed documentation and examples, visit: https://openapi-ts-vue.dev/openapi-vue-query/

🐛 Issues & Feedback

Found a bug or have a feature request? Please open an issue on GitHub.

📄 License

MIT License. See LICENSE for details.