GithubHelp home page GithubHelp logo

vite-plugin-vue-gql's Introduction

VQL

Clean up your Vue SFC Scripts by moving your graphql queries to their own block

NPM version

Why?

When writing Vue clients for GraphQL APIs, I've noticed scripts in Vue SFC files have become over-filled with GraphQL queries and had a need to organize the code better without taking away from what makes SFCs great: Having all the code for a single component organized and in one place.

Moving queries to their own files would then create multiple files for a single component, cluttering the project more and reducing productivity in having to write components spanning multiple files.

Enter Vue GQL! I wrote this Vite plugin to allow placing GraphQL queries related to a component directly within the component file without cluttering scripts, by placing them within their own specialized <gql> tags.

⚠️ This Plugin is still in Development and currently only works with the <script setup> format

Install

# Install Plugin
npm i -D vite-plugin-vue-gql

# Install Peer Dependicies
npm i @urql/vue graphql
// vite.config.ts

import { defineConfig } from 'vite'
import Vue from '@vitejs/plugin-vue'
import Vql from 'vite-plugin-vue-gql'

export default defineConfig({
  plugins: [
    Vue(), 
    Vql(),
  ],
})

If you are using typescript, make sure you include the following in your tsconfig.json

{
  "compilerOptions": {
    "types": [
      "vite-plugin-vue-gql/client"
    ]
  }
}

Usage

Instead of import your functions from @urql/vue you should now import them from the vql package.

import { useQuery, useMutation, useSubscription } from 'vql'

<gql> tags can have the following attributes, query(not required), mutation, subscription, and name. The first three attributes indicates what type of query it is while the name attribute allows you to have multiple queries in the same Vue SFC.

<!-- Query-->
<gql></gql>

<!-- Mutation -->
<gql mutation></gql>

<!-- Subscription -->
<gql subscription></gql>

<!-- Named GQL Block -->
<gql name="users"></gql>

Examples

Basic Usage

<script setup lang="ts">
import { useQuery } from 'vql'

const { data } = useQuery()
</script>

<template>
  <h1>{{ data.hello }}</h1>
</template>

<gql>
{
  hello
}
</gql>

Query with Variables

<script setup lang="ts">
import { ref } from 'vue'
import { useQuery } from 'vql'

const name = ref('Evan')
const { data } = useQuery({ variables: { name } })
</script>

<template>...</template>

<gql>
query($name: String!) {
  user(name: $name) {
    username
  }
}
</gql>

Named Query

<script setup lang="ts">
import { ref } from 'vue'
import { useQuery } from 'vql'

const name = ref('Evan')
const { data } = useQuery('users', { variables: { name } })
</script>

<template>...</template>

<gql name="users">
query($name: String!) {
  user(name: $name) {
    username
  }
}
</gql>

Mutations

<script setup lang="ts">
import { ref } from 'vue'
import { useMutation } from 'vql'

const { executeMutation } = useMutation()
</script>

<template>...</template>

<gql mutation>
mutation($name: String!) {
  createUser(name: $name) {
    username
  }
}
</gql>

Subscriptions

<script setup lang="ts">
import { ref } from 'vue'
import { useSubscription } from 'vql'

const isPaused = ref(false)
const handleSubscription = (messages = [], response) => {
  return [response.newMessages, ...messages]
}

const { data } = useSubscription({ from: 'Eren' }, { pause: isPaused }, handleSubscription)
</script>

<template>...</template>

<gql mutation>
subscription MessageSub($from: String!) {
  newMessages(from: $from) {
    id
    from
    text
  }
}
</gql>

Fragments

You can use fragments in your graphql queries, mutations, and subscriptions by specifying your .gql files that contain your fragments in the config.

// vite.config.ts
import { defineConfig } from 'vite'
import Vue from '@vitejs/plugin-vue'
import Vql from 'vite-plugin-vue-gql'

export default defineConfig({
  plugins: [
    Vue(), 
    Vql({
      fragments: './src/fragments/**/*.gql',
    }),
  ],
})

Here is a general idea of what your fragments should look like

# src/fragments/albums.gql

fragment albumFields on Album {
  id
  name
  image
}

Finally you can use these fragments in your Vue SFC

<script setup lang="ts">
import { ref } from 'vue'
import { useQuery } from 'vql'

const name = ref('RADWIMPS')
const { data } = useQuery({ variables: { name } })
</script>

<template>...</template>

<gql>
query($name: String!) {
  queryArtists(byName: $name) {
    name
    image
    albums {
      ...albumFields
    }
  }
}
</gql>

Type Definitions

You can automatically generate typescript type definition files from you graphql schema by providing a link to your server or a path to your graphql schema.

import { defineConfig } from 'vite'
import Vue from '@vitejs/plugin-vue'
import Vql from 'vite-plugin-vue-gql'

export default defineConfig({
  plugins: [
    Vue(),
    Vql({
      schema: 'https://my-api.dev/graphql',
      dts: 'src/schema.d.ts',
    }),
  ],
})

Whenever the application is run, vite-plugin-vue-gql will download the schema from your server or open the provided gql schema and generate a typescript type definition file for your schema via GraphQL Code Generator

Roadmap

  • Add support for fragments
  • Investigate automatically generating queries from SFC templates

License

MIT License © 2021-PRESENT Jacob Clevenger

vite-plugin-vue-gql's People

Contributors

jojoxd avatar nathanchase avatar wheatjs avatar wobsoriano avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

vite-plugin-vue-gql's Issues

希望vql可以尽快支持最新的vite,以及其他依赖,可以的话希望进一步支持idea的自动缩进以及语法提示

vql是一个非常方便的gql工具,但是在"vite": "^5.2.0-beta.0"版本下,使用readme示例代码,会出现以下报错:

failed to load config from .\your_project\vite.config.ts
error when starting dev server:
TypeError: vql is not a function
    at file:///./your_project/vite.config.ts.timestamp-1710340576425-20181727eccf3.mjs:12:5
    at ModuleJob.run (node:internal/modules/esm/module_job:218:25)
    at async ModuleLoader.import (node:internal/modules/esm/loader:329:24)
    at async loadConfigFromBundledFile (file:///./your_project/node_modules/vite/dist/node/chunks/dep-BxRAmRCV.js:68343:21)
    at async loadConfigFromFile (file:///./your_project/node_modules/vite/dist/node/chunks/dep-BxRAmRCV.js:68196:28)
    at async resolveConfig (file:///./your_project/node_modules/vite/dist/node/chunks/dep-BxRAmRCV.js:67806:28)
    at async _createServer (file:///./your_project/node_modules/vite/dist/node/chunks/dep-BxRAmRCV.js:64420:20)
    at async CAC.<anonymous> (file:///./your_project/node_modules/vite/dist/node/cli.js:762:24)

// vite.config.ts:

import { fileURLToPath, URL } from 'node:url'

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import VueDevTools from 'vite-plugin-vue-devtools'

// Vql
import vql from "vite-plugin-vue-gql"

export default defineConfig({
  plugins: [
    vue(),
    vql(),
    vueJsx(),
    VueDevTools(),
  ],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url))
    }
  }
})

Cannot use await on useQuery

I found that I could use await with urql useQuery like this:
const { data, error } = await useQuery({query: IPquery, variables: {uname: username}})

But when using vql with this syntax:
const { error, data } = await useQuery({ uname });

Throws a fetch error and no contents returned.
Verified that removing the await on vql useQuery it does work.

Make rollup plugin

This plugin can probably be extracted out into just a rollup-plugin. Since the name is already vite-plugin-vue-gql this plugin can just expose the vite and rollup plugin

Fragment Recursion & Doubling Up

When using fragments, they don't seem to work properly with recursion(using fragments in fragments), and they also apply the same fragment multiple times to a query causing numerous errors with anything but the basic fragment use.

Example query:

query(
    $after: String,
    $take: Int = 10,
    $id: String,
    $username: String,
    $parent: String,
    $replies: Boolean! = false) {
    posts(
        after: $after,
        take: $take,
        id: $id,
        username: $username,
        parent: $parent)
    {
        next
        hasNextPage
        nodes {
            ...PostFragment
            parent {
                ...PostFragment
            }
            children @include(if: $replies) {
                ...PostFragment
                parent {
                    ...PostFragment
                }
                children {
                    ...PostFragment
                    parent {
                        ...PostFragment
                    }
                }
            }
        }
    }
}

These would produce a result similar to this:

fragment PostFragment on Post {  
  id
  text
  created
  level
  childCount
  isMyPost
  likes
  dislikes
  liked
  disliked
  externalMedia {
    id
    images {
      original {
        width
        height
        mp4
        __typename
      }
      __typename
    }
    __typename
  }
  media {
    ...MediaFragment
    __typename
  }
  author {
    ...UserFragment
    __typename
  }
}
 
fragment PostFragment on Post {
  id
  text
  created
  level
  childCount
  isMyPost
  likes
  dislikes
  liked
  disliked
  externalMedia {
    id
    images {
      original {
        width
        height
        mp4
        __typename
      }
      __typename
    }
    __typename
  }
  media {
    ...MediaFragment
    __typename
  }
  author {
    ...UserFragment
    __typename
  }
}
 
fragment PostFragment on Post {
  id
  text
  created
  level
  childCount
  isMyPost
  likes
  dislikes
  liked
  disliked
  externalMedia {
    id
    images {
      original {
        width
        height
        mp4
        __typename
      }
      __typename
    }
    __typename
  }
  media {
    ...MediaFragment
    __typename
  }
  author {
    ...UserFragment
    __typename
  }
}
 
fragment PostFragment on Post {
  id
  text
  created
  level
  childCount
  isMyPost
  likes
  dislikes
  liked
  disliked
  externalMedia {
    id
    images {
      original {
        width
        height
        mp4
        __typename
      }
      __typename
    }
    __typename
  }
  media {
    ...MediaFragment
    __typename
  }
  author {
    ...UserFragment
    __typename
  }
}
 
fragment PostFragment on Post {
  id
  text
  created
  level
  childCount
  isMyPost
  likes
  dislikes
  liked
  disliked
  externalMedia {
    id
    images {
      original {
        width
        height
        mp4
        __typename
      }
      __typename
    }
    __typename
  }
  media {
    ...MediaFragment
    __typename
  }
  author {
    ...UserFragment
    __typename
  }
}
 
fragment PostFragment on Post {
  id
  text
  created
  level
  childCount
  isMyPost
  likes
  dislikes
  liked
  disliked
  externalMedia {
    id
    images {
      original {
        width
        height
        mp4
        __typename
      }
      __typename
    }
    __typename
  }
  media {
    ...MediaFragment
    __typename
  }
  author {
    ...UserFragment
    __typename
  }
}
 
query ($after: String, $take: Int = 10, $id: String, $username: String, $parent: String, $replies: Boolean! = false) {
  posts(after: $after, take: $take, id: $id, username: $username, parent: $parent) {
    next
    hasNextPage
    nodes {
      ...PostFragment
      parent {
        ...PostFragment
        __typename
      }
      children @include(if: $replies) {
        ...PostFragment
        parent {
          ...PostFragment
          __typename
        }
        children {
          ...PostFragment
          parent {
            ...PostFragment
            __typename
          }
          __typename
        }
        __typename
      }
      __typename
    }
    __typename
  }
}

Each fragment should only be included once, and sub fragments should be included.

Question

Hi @wheatjs,

First of all, this is a awesome plugin. Thanks for it.
I have just a question, i don't know if it can be a feature request or not:
Is there any simple way to use another graphql provider with your plugin? Like villus or apollo

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.