DEV Community

Cover image for Introducing the new Solid Query client
Stefan  🚀
Stefan 🚀

Posted on • Originally published at wundergraph.com

Introducing the new Solid Query client

We're excited to announce that we now have official Solid.js support! 🎉. This new client is built on top of TanStack Solid Query, and brings all the good stuff from WunderGraph to the Solid.js ecosystem. Query, mutate and subscribe to your WunderGraph API fully typesafe in Solid.js.

The release is still in beta and we're looking for feedback on the API and how it works with Solid.js. If you have any feedback, please let us know by opening an issue on Github or talk to us directly on Discord.

Let's go through quickly how to set it up and how it works.

Installation

Install the Solid Query client:

npm install @wundergraph/solid-query @tanstack/solid-query
Enter fullscreen mode Exit fullscreen mode

Configuration

Before you can use the hooks, you need to modify your code generation to include the base typescript client.

// wundergraph.config.ts
configureWunderGraphApplication({
  // ... omitted for brevity
  codeGenerators: [
    {
      templates: [templates.typescript.client],
      // the location where you want to generate the client
      path: '../src/generated',
    },
  ],
})
Enter fullscreen mode Exit fullscreen mode

Now you can configure the hooks. Create a new file, for example src/lib/wundergraph.ts and add the following code:

import { createHooks } from '@wundergraph/solid-query'
import { createClient, Operations } from '../generated/client'

const client = createClient() // Typesafe WunderGraph client

export const {
  createQuery,
  createMutation,
  createSubscription,
  createFileUpload,
  useUser,
  useAuth,
  queryKey,
} = createHooks<Operations>(client)
Enter fullscreen mode Exit fullscreen mode

Queries

const weather = createQuery({
  operationName: 'Weather',
  input: {
    forCity: 'Berlin',
  },
})

weather.data
Enter fullscreen mode Exit fullscreen mode

Turn queries into live queries, live queries are refetched on a interval on the WunderGraph server.

const liveWeather = createQuery({
  operationName: 'Weather',
  input: {
    forCity: 'Berlin',
  },
  liveQuery: true,
})
Enter fullscreen mode Exit fullscreen mode

Subscriptions

Build realtime apps with subscriptions.

const subscription = createSubscription({
  operationName: 'Countdown',
  input: {
    from: 100,
  },
})
Enter fullscreen mode Exit fullscreen mode

Mutations

const mutation = createMutation({
  operationName: 'SetName',
})

mutation.mutate({ name: 'Eelco' })

// Async mutate
const result = await mutation.mutateAsync({ name: 'Eelco' })
Enter fullscreen mode Exit fullscreen mode

Invalidating queries

Let's say we have a query that fetches the current user's profile in one component and we have a form that updates the profile. We can add an onSuccess handler to the mutation that calls queryClient.invalidateQueries on the GetProfile query and trigger a refetch and update the internal React Query cache.

const Profile = () => {
  const query = createQuery({
    operationName: 'GetProfile',
  })

  return <div>{query.data?.getProfile.name}</div>
}

const FormComponent = () => {
  const queryClient = useQueryClient();

  const mutation = createMutation({
    operationName: 'UpdateProfile',
    onSuccess() {
      // invalidate the query
      queryClient.invalidateQueries(queryKey({ operationName: 'GetProfile' }));
    },
  })

  const onSubmit = (event) => {
    e.preventDefault();
    const data = new FormData(event.target);
    mutation.mutate(data)
  }

  return <form onSubmit={onSubmit}><input name="name" /><button type="submit">Save></button></form>
}
Enter fullscreen mode Exit fullscreen mode

Now we could even make this fully optimistic by updating the GetProfile cache instead and then refetching it, it would look something like this:

const FormComponent = () => {
  const queryClient = useQueryClient()

  const mutation = createMutation({
    operationName: 'UpdateProfile',
    onMutate: async (data) => {
      const key = queryKey({ operationName: 'GetProfile' })
      // Cancel any outgoing refetches
      // (so they don't overwrite our optimistic update)
      await queryClient.cancelQueries(key)

      // Snapshot the previous value
      const previousProfile = queryClient.getQueryData<Profile>(key)

      // Optimistically update to the new value
      if (previousProfile) {
        queryClient.setQueryData<Profile>(key, {
          ...previousProfile,
          ...data,
        })
      }

      return { previousProfile }
    },
    // If the mutation fails,
    // use the context returned from onMutate to roll back
    onError: (err, variables, context) => {
      if (context?.previousProfile) {
        queryClient.setQueryData<Profile>(
          queryKey({ operationName: 'GetProfile' }),
          context.previousProfile
        )
      }
    },
    // Always refetch after error or success:
    onSettled: () => {
      queryClient.invalidateQueries(queryKey({ operationName: 'GetProfile' }))
    },
  })

  const onSubmit = (event) => {
    e.preventDefault()
    const data = new FormData(event.target)
    mutation.mutate(data)
  }

  return (
    <form onSubmit={onSubmit}>
      <input name="name" />
      <button type="submit">Save</button>
    </form>
  )
}
Enter fullscreen mode Exit fullscreen mode

Check out the reference and example app below to learn more about the new Solid Query integration.

Resources

Summary

You can now leverage the power of WunderGraph with Solid.js, we are excited to see what you'll build with it.

Thanks go out to Tanstack for making this awesome async state management library. We will be releasing Svelte and Vue integrations soon, stay tuned!

We would love to know more about your experience with Solid.js. Do you use Vite, or Solid Start? What do you like about it?

Share it in the comments below or come join us on our Discord server.

Top comments (0)