feathers-pinia

Connect your Feathers API to the elegant data store for Vue

Stars
52
Committers
19

Bot releases are hidden (Show)

feathers-pinia - `useService` now has a `setModel` method. `ModelFn` arg renamed to `Model`

Published by marshallswain almost 2 years ago

  • The ModelFn arg for useService has been renamed to Model.
  • You no longer have to pass the Model when calling useService. The return value of useService now has a setService method, so you can provide the model that way.
import type { Tasks, TasksData, TasksQuery } from '../feathers-schema-tasks'
import { type ModelInstance, useFeathersModel, useInstanceDefaults, useService, feathersPiniaHooks } from '../../src'
import { api } from '../feathers'
import { createPinia, defineStore } from 'pinia'

const pinia = createPinia()
const service = api.service('tasks')

const modelFn = (data: ModelInstance<Tasks>) => {
  const withDefaults = useInstanceDefaults({ description: '', isComplete: false }, data)
  return withDefaults
}
const Task = useFeathersModel<Tasks, TasksData, TasksQuery, typeof modelFn>(
  { name: 'Task', idField: '_id', service },
  modelFn,
)

// passing the Model into `useService` overwrites the model's feathers methods to proxy through the store.
const useTaskStore = defineStore('tasks', () => {
  const serviceUtils = useService<Tasks, TasksData, TasksQuery, typeof Task>({
    service,
    idField: '_id',
    // Model doesn't have to be passed if you're going to call `Model.setStore`
  })

  return { ...serviceUtils }
})
const taskStore = useTaskStore(pinia)
Task.setStore(taskStore) // Calling setStore also calls `store.setModel`

api.service('tasks').hooks({ around: { all: [...feathersPiniaHooks(Task)] } })
feathers-pinia - Adds `FeathersPiniaService` type

Published by marshallswain almost 2 years ago

The service types don't line up perfectly between The Feathers API's createClient return value and the ClientService used in FeathersPinia. The FeathersPiniaService makes it simple to cast the service to the value we want:

import type { FeathersPiniaService } from 'feathers-pinia'
import type { Messages, MessagesQuery } from 'my-feathers-api

const service = feathersClient.service('messages') as FeathersPiniaService<Messages, MessagesQuery>
export type FeathersPiniaService<M extends AnyData, Q extends AnyData> = ClientService<
  FeathersInstance<M, Q>,
  M,
  Params<Q>
> &
  ServiceAddons

See https://github.com/marshallswain/feathers-pinia/commit/2ca94b9aae66116fad5de2463d6adeb7f52638eb

feathers-pinia - Initial 2.0 pre-release

Published by marshallswain almost 2 years ago

What's New in 2.0

Feathers-Pinia 2.0 is a huge update with some great new features. This page will go over some of the highlights.

Huge Performance Boost 🚀

Feathers-Pinia is SO MUCH faster than its predecessor. You'll see massive benefits from the faster reactive types under
the hood of Pinia and Vue 3. But we've gone a step further and fine-tuned and tested Feathers-Pinia to never perform
extra work. Some of the biggest improvements are:

  • No unnecessary stack frames happen under the hood. We stand firmly against wasted CPU cycles!
  • As from the beginning, you still have full control over adding instances to the store with new User().addToStore().
  • For the features that require objects to be in the store (for example, useClones) feathers-pinia will implicitly
    add items to the store when needed.

Composition API Stores 🎉

Feathers-Pinia has been completely rewritten as a set of Composition API utilities for creating Pinia
setup stores. The advantages include a better TypeScript
experience, cleaner customization, and fewer limitations.

useService 🎁

The new useService utility takes the place of the Feathers-Pinia defineStore utility (not to be confused Pinia's
defineStore utility) and gives you a starting point for defining your own setup store for each service. The object
returned from calling useService has the same shape as service stores from older versions.

The useService utility only requires a Feathers service and not the full Feathers client instance, anymore. It also
requires the use of a Model Function, which is covered further down this page and not shown in this example. This
example shows the creation and instantiation of a setup store with userService:

import { defineStore, createPinia } from 'pinia'

const pinia = createPinia()

// Create a tasks store
export const useTaskStore = defineStore('tasks', () => {
  const serviceUtils = useService<TaskInstance, TasksData, TasksQuery, typeof modelFn>({
    service,
    idField: '_id',
    Model: Task, // see the section on Model Functions
  })

  return { ...serviceUtils }
})

const taskStore = useTaskStore(pinia)

To customize a setup store, you declare additional variables, computed properties, and functions inside of the call to
defineStore.

useAuth 🎁

Create ultra-flexible setup stores with the new useAuth utility:

// src/store/store.auth.ts
import { defineStore, acceptHMRUpdate } from 'pinia'
import { useAuth } from 'feathers-pinia'

export const useAuthStore = defineStore('auth', () => {
  const { userStore } = useUserStore()
  const { $api } = useFeathers()

  const auth = useAuth({
    api: $api,
    userStore,
  })

  auth.reAuthenticate()

  return auth
})

Feathers v5 Dove TS Support 🎉

The new utilities in Feathers-Pinia 2.0 bring support for the new TypeScript enhancements in Feathers v5 Dove. Now you
can directly import the types from your backend and use them in your Feathers-Pinia frontend. The types integrate
directly into the new Model Functions, as well.

Model Functions, not Classes 🔮

Data modeling is one of the most-loved features in Feathers-Pinia. In Feathers-Pinia 2.0, we replace Model Classes
with Model Functions. The developer experience just go so much better! You just create a function that receives an
object, performs modifications to it, then returns it. There are two utilities for wrapping Model Functions:
useFeathersModel and useBaseModel.

useFeathersModel 🎁

The useFeathersModel utility is most similar to the old BaseModel class from FeathersVuex and previous versions of
Feathers-Pinia. You get the full Feathers service experience. Feathers Models have Feathers-related methods, like
find, count, get, etc., directly on the model interface.

import type { Tasks, TasksData, TasksQuery } from 'my-feathers-api'
import { type ModelInstance, useFeathersModel, useInstanceDefaults } from 'feathers-pinia'
import { api } from '../feathers'

const modelFn = (data: ModelInstance<Tasks>) => {
  const withDefaults = useInstanceDefaults({ description: '', isComplete: false }, data)
  return withDefaults
}
const Task = useFeathersModel<Tasks, TasksData, TasksQuery, typeof modelFn>(
  { name: 'Task', idField: '_id', service },
  modelFn,
)

Models now come with a lightweight, built-in store. This means that they work without a Pinia store, but you give up the
Pinia devtools compatibility. To upgrade to a full Pinia store, use the setStore method to provide the instantiated
pinia store:

// upgrading the Task Model's store to be a Pinia store
Task.setStore(taskStore)

To create a model "instance" you just call the function WITHOUT the new operator:

const task = Task({ description: 'Do the dishes' })

useBaseModel 🎁

The useBaseModel utility gives you all of the BaseModel functionality without the Feathers parts. This means you can
work with non-service data using the same APIs. Base model functions also come with a built-in store, and you can even
perform queries on the data with store getters. Also, useBaseModel instances come with clone, commit and reset
methods.

import type { Tasks, TasksData, TasksQuery } from 'my-feathers-api'
import { type ModelInstance, useBaseModel, useInstanceDefaults } from 'feathers-pinia'

const modelFn = (data: ModelInstance<Tasks>) => {
  const withDefaults = useInstanceDefaults({ description: '', isComplete: false }, data)
  return withDefaults
}
const Task = useBaseModel<Tasks, TasksQuery, typeof modelFn>({ name: 'Task', idField: '_id' }, modelFn)

BaseModel functions also have a store, which can be upgraded to a full Pinia store using the setStore method:

// upgrading the Task Model's store to be a Pinia store
Task.setStore(taskStore)

To create a model "instance" you just call the function WITHOUT the new operator:

const task = Task({ description: 'Do the dishes' })

useFeathersInstance 🎁

There's also a useFeathersInstance utility which you can use with useBaseModel. It's used inside of your Model
function to update a model instance to support Feathers-related methods.

Feathers Client Hooks 🪝

Feathers-Pinia now fully integrates with the Feathers Client through a new set of feathersPiniaHooks. The majority of
the store logic was moved into the hooks, so you get the same experience whether you use the Feathers client, the Model
Functions, or the store methods. The hooks are required in order for Feathers-connected Models or stores to work.

The above example builds on the Model function that was created in the previous useFeathersModel example. For an
all-in-one example, see one of the setup pages.

import { feathersPiniaHooks } from 'feathers-pinia'
import { api } from '../feathers'

/* setup the Model function as shown earlier */

// Pass the model function to the utility in the `around all` hooks.
api.service('tasks').hooks({ around: { all: [...feathersPiniaHooks(Task)] } })

Support SQL $like Operators 🎁

The most-requested feature has finally landed: built-in support for SQL LIKE. This means the queries made to the store
will match the queries made to your SQL-backed API. This brings querying features up to parity with the built-in MongoDB
support which uses the $regex key.

These are the newly-supported SQL operators:

  • $like and $notLike for case-sensitive matches
  • $ilike, $iLike, and $notILike for case-insensitive matches

Let's have a look at them in action. First, assume that we have the following messages in the store:

[
  { "id": 1, "text": "Moose" },
  { "id": 2, "text": "moose" },
  { "id": 3, "text": "Goose" },
  { "id": 4, "text": "Loose" },
]

Now see the fancy new query operators in action:

import { useMessages } from '../stores/messages'
const messageStore = useMessages

// $like
const { data: data1 } = messageStore.findInStore({
  query: { text: { $like: '%Mo%' } }
})
expect(data1.map((m) => m.id)).toEqual([1])

// $notLike
const { data: data2 } = messageStore.findInStore({
  query: { text: { $notLike: '%Mo%' } }
})
expect(data2.map((m) => m.id)).toEqual([2, 3, 4])

// $ilike
const { data: data3 } = messageStore.findInStore({
  query: { text: { $ilike: '%Mo%' } }
})
expect(data3.map((m) => m.id)).toEqual([1, 2])

// $iLike
const { data: data4 } = messageStore.findInStore({
  query: { text: { $iLike: '%Mo%' } }
})
expect(data4.map((m) => m.id)).toEqual([1, 2])

// $notILike
const { data: data5 } = messageStore.findInStore({
  query: { text: { $notILike: '%Mo%' } }
})
expect(data5.map((m) => m.id)).toEqual([3, 4])

These new operators support queries made with SQL-backed adapters like the official, core SQL service adapter in
Feathers v5 Dove:

These adapters will also work:

If you use any of the above database adapters, give the new query operators a try! Enjoy your new superpowers!

Built-in Patch Diffing 🎁

Reactive Model Instances ➕

Thanks to the Model Function API, all Model instances are now always reactive, even when not added to the store.

import { Task } from '../models/task'

const task = Task({ description: 'Bind me to a template. I am ready.' })

Read more about Model Instances.

No instance.update() method ➖

The rarely-used update method has been removed from the instance interface. Use the patch method, instead, to take
advantage of patch diffing and partial updates. You can still replace an entire object by just sending all of the data
through patch. The Model Functions and Feathers-connected stores continue to have an update method, which an also
be used.

Handle Associations

Two new utilities make it easier to add relationships between records without depending on associations in-memory. You can setup associations in both directions between models.

associateFind 🎁

The associateFind utility allows you to define one-to-many relationships on your Model functions. The makeParams property allows you to specify the query that defines the relationship. This example is truncated. For a full example, see the "Start a Project" pages.

import type { Users } from 'my-feathers-api'
import { type ModelInstance, useInstanceDefaults, associateFind } from 'feathers-pinia'
import { Message } from './message'

const modelFn = (data: ModelInstance<Users>) => {
  const withDefaults = useInstanceDefaults({ email: '', password: '' }, data)
  const withMessages = associateFind(withDefaults, 'messages', {
    Model: Message,
    makeParams: (data) => ({ query: { userId: data.id } }),
    handleSetInstance(message) {
      message.userId = data.id
    },
  })
  return withMessages
}

The handleSetInstance function allows you to write data to the messages property and make sure each record becomes
properly associated.

Read more about associateFind

associateGet 🎁

The associateGet utility allows you to define one-to-one relationships on your Model functions. The getId property
allows you to specify the id to use to get the related data.

import type { Messages } from 'my-feathers-api'
import { type ModelInstance, useInstanceDefaults, associateGet } from 'feathers-pinia'
import { User } from './user'

const modelFn = (data: ModelInstance<Messages>) => {
  const withDefaults = useInstanceDefaults({ text: '', userId: null }, data)
  const withUser = associateGet(withDefaults, 'user', {
    Model: User,
    getId: (data) => data.messageId
  })
  return withUser
}

Read more about associateGet

New useFind API 🎁

The useFind API has been completely rewritten from scratch. A couple of its best features include

  • Intelligent Fall-Through Caching - Like SWR, but way smarter.
  • Pagination Support - Built in, sharing the same logic with usePagination.

See all of the features on the useFind page.

See the component example of server-side pagination on the useFind page.

useFindWatched API ⚠️

To make migration to the new useFind API easier, the old useFind API has been renamed and is now called useFindWatched.

Learn more about the old API on the useFindWatched page.

See the new API on the useFind page.

Store API Improvements

The useFind utility -- for implementing fall-through-cached find requests -- is now available directly on the store, further reducing boilerplate.

store.useFind

With the old way, you have to import useFind and provide the model to it from the instantiated store.

import { useFind } from 'feathers-pinia'
import { useTutorials } from '../store/tutorials'

const tutorialStore = useTutorials()
const tutorialsParams = computed(() => {
  return { query: {}, }
})
const { items: tutorials } = useFind({ model: tutorialStore.Model, params: tutorialsParams })

In the new way, there's no need to import useFind. Call it as a method on the store and don't pass model

import { useTutorials } from '../store/tutorials'

const tutorialStore = useTutorials()
const tutorialsParams = computed(() => {
  return { query: {}, }
})
const { items: tutorials } = tutorialStore.useFind({ params: tutorialsParams })

Just think of all of the extra time you'll have instead of having to write those 1.5 lines of code over and over again! 😁

store.useGet

The useGet utility -- for implementing fall-through-cached get requests -- is now available directly on the store, further reducing boilerplate.

Smaller Package Size 🎉

When it comes to npm bundles, smaller package size is a good thing.

Optimized Vite Build 💪

The overall bundle size has been reduced from around 20kb to 12kb, gzipped. This was done through

  • Replacing hefty dependencies, like lodash's debounce, with smaller equivalents, like just-debounce.
  • Optimizing the Vite build to externalize modules.

Here is the previous output from npx vite-bundle-visualizer to compare. All of the modules highlighted, below, were able to be removed from the package, resulting in a much leaner build:

Optimized Vite Build

LZW Storage is Out ➖

Prior to this version, Feathers-Pinia included a localStorage plugin that used LZW compression. It came with the benefit of doubling the amount of data you could put in storage. The downside was that it made the bundle size big, so we removed it. It will be published as an independent package at a later date.

Our LocalStorage adapter remains part of the package and is so fast that it makes Single Page Apps feel like they're doing Server Side Rendering. If you haven't tried it, yet, it's easy to setup and it's worth it!

Lots of Little Improvments

feathers-pinia - 🎁 New `useAuth` utility for creating Pinia setup stores

Published by marshallswain almost 2 years ago

useAuth Utility

You can now create setup stores for auth. It's very flexible. See details on the use-auth page.

// src/store/store.auth.ts
import { defineStore, acceptHMRUpdate } from 'pinia'
import { useAuth } from 'feathers-pinia'

export const useAuthStore = defineStore('auth', () => {
  const { userStore } = useUserStore()
  const { $api } = useFeathers()

  const auth = useAuth({
    api: $api,
    userStore,
  })

  auth.reAuthenticate()

  return auth
})

if (import.meta.hot) {
  import.meta.hot.accept(acceptHMRUpdate(useAuthStore, import.meta.hot))
}
feathers-pinia - ⚠️ Rename `OhMyFetch` export to `OFetch`

Published by marshallswain almost 2 years ago

Now that Nuxt3 has been officially released, the Nuxt/unjs team has renamed the ohmyfetch package to ofetch, which is now here: https://github.com/unjs/ofetch.

The Feathers-Pinia class export has been renamed from OhMyFetch to OFetch:

// old way
import { OhMyFetch } from 'feathers-pinia'

// new way
import { OFetch } from 'feathers-pinia'
feathers-pinia - Fix queries with `findInStore` with `$or`

Published by marshallswain almost 2 years ago

The filterQuery function in Feathers Dove was updated to separate $or from the query. It has been manually added back into the query in the findInStore getter, now. Fixing this bug uncovered another bug in the associate-find tests. That test has also been fixed, proving that $or works correctly for that test, as well.

feathers-pinia - 🐜 $select in findInStore getter no longer creates temps

Published by marshallswain almost 2 years ago

The $select operator has been broken for a long time, but never gets used on the client, so it was only noticed recently to be broken. Instead of returning a partial object, it was returning a new temp with the picked properties.

This release makes sure that $select returns the entire instance from the findInStore getter. It's still possible to use $select in a server query to only retrieve partial objects.

feathers-pinia - 🐜 make `@feathersjs/rest-client` an optional dependency

Published by marshallswain almost 2 years ago

The @feathersjs/rest-client package was brought in to make Nuxt 3 usage easier. It should have been put into optionalDependencies to prevent npm errors when you don't install @feathersjs/rest-client into your app. Now it's fixed.

feathers-pinia - 🐜 Fix types for storageSync Pinia plugin

Published by marshallswain almost 2 years ago

Types for syncWithStorage were incorrectly indicating that the list of state keys in the second argument was optional. They have been updated to clearly show that they are required.

feathers-pinia - 🎁 Adds `store.useGetOnce`

Published by marshallswain about 2 years ago

Auto-Query Once Per Record

The previous pattern of only querying once is so common for real-time apps that we've built a shortcut for it at store.useGetOnce. It uses the same code as above, but built into the store method.

import { useUsers } from '../store/users'

interface Props {
  id: string | number
}
const props = defineProps<Props>()
const userStore = useUsers()

const { data: user } = userStore.useGetOnce(props.id)

Now the same record will only be retrieved once.

feathers-pinia - 🐜 Prevent Infinite Rendering Loops with `useGet` and `useFind`

Published by marshallswain about 2 years ago

Exposing the store causes an infinite loop, so store is now an internal variable in useGet and useFind.

feathers-pinia - 🎁 OhMyFetch Rest Adapter for Nuxt

Published by marshallswain about 2 years ago

Feathers-PInia now includes a Feathers-Rest adapter for ohmyfetch, which means it will work with Nuxt 3!

Here's an example of how to use the Feathers Client in your Nuxt 3 app. It's best to define the Feathers Client inside of a Nuxt plugin in order to avoid any cross-session data leakage. This is also the ideal place to do global configuration for Feathers-Pinia!

// plugins/feathers.ts
import auth from '@feathersjs/authentication-client'
import { $fetch } from 'ohmyfetch'
import rest from '@feathersjs/rest-client'
import socketio from '@feathersjs/socketio-client'
import io from 'socket.io-client'
import { feathers } from '@feathersjs/feathers'
import { OhMyFetch, setupFeathersPinia } from 'feathers-pinia'

export default defineNuxtPlugin(async (_nuxtApp) => {
  // Configuring the client in a plugin avoids stateful data and
  // prevents information from leaking between user sessions.
  const api = feathers()
  const { defineStore } = setupFeathersPinia({
    ssr: !!process.server,
    clients: { api },
    idField: '_id',
    // customize every store
    state: () => ({}),
    getters: {},
    actions: {},
  })

  const host = import.meta.env.VITE_MYAPP_API_URL as string || 'http://localhost:3030'

  console.log('')
  console.log('in feathers plugin')

  // Use Rest on the server
  // Check process.server so the code can be tree shaken out of the client build.
  if (process.server)
    api.configure(rest(host).fetch($fetch, OhMyFetch))

  // Switch to Socket.io on the client
  else
    api.configure(socketio(io(host, { transports: ['websocket'] })))

  api.configure(auth())

  return {
    provide: { api, defineStore },
  }
})

By returning api and defineStore in the provide key, they are now available by calling useNuxtApp() in composables and components. Now we can create a useFeathers composable:

// composables/feathers.ts
/**
 * Provides access to the Feathers client by calling useFeathers(). For example:
 *
 * ```ts
 * const { $api } = useNuxtApp()
 * ```
 */
export const useFeathers = () => {
  const { $api } = useNuxtApp()
  return { $api }
}
feathers-pinia - 🪲 Replace replace `lodash.isEqual` with `fast-deep-equal`

Published by marshallswain about 2 years ago

It's 4x smaller, 7.6x faster, and passes all tests.

feathers-pinia - 🎁 Better `queryWhen` support for `useGet`

Published by marshallswain about 2 years ago

Only Query Once Per Record

For real-time apps, it's not necessary to retrieve a single record more than once, since feathers-pinia will automatically keep the record up to date with real-time events. You can use queryWhen to make sure you only retrieve a record once. Perform the following steps to accomplish this:

  1. Pass immediate: false in the params to prevent the initial request.
  2. Pass a function that returns a boolean to queryWhen. In this example, we return !user.value because we should query when we don't already have a user record.
  3. Manually call get, which will only trigger an API request if we don't have the record. Woot!
import { useUsers } from '../store/users'

interface Props {
  id: string | number
}
const props = defineProps<Props>()
const userStore = useUsers()

const { data: user, queryWhen, get } = userStore.useGet(props.id, { 
  onServer: true, 
  immediate: false            // (1)
})
queryWhen(() => !user.value)  // (2)
await get()                   // (3)

The above example also shows why queryWhen is no longer passed as an argument. It's most common that queryWhen needs values returned by useGet, but those values aren't available until after useGet runs, making them unavailable to queryWhen as an argument. In short, moving queryWhen to the returned object gives us access to everything we need to productively prevent queries.

feathers-pinia - 🎁 Adds `store.useGet`

Published by marshallswain about 2 years ago

The useGet API can now also be found directly on the store. This means that there are now two ways to use useGet: from the store (recommended) or standalone.

Through the Store (Recommended)

You can call useGet directly from the store. the advantage being that you don't have to provide the store in the params, as shown here:

import { useUsers } from '../store/users'

interface Props {
  id: string | number
}
const props = defineProps<Props>()
const userStore = useUsers()

// client-only example
const { data: user } = userStore.useGet(props.id)

// onServer example
const { data: user, isPending, error } = userStore.useGet(props.id, { onServer: true })

Standalone

In standalone mode, you have to import useGet and provide the store option in the params object, as shown here:

import { useUsers } from '../store/users'
import { useGet } from 'feathers-pinia'

interface Props {
  id: string | number
}
const props = defineProps<Props>()
const userStore = useUsers()

// client-only example
const { data: user } = useGet(props.id, { store: userStore })

// onServer example
const { data: user, isPending, error } = useGet(props.id, { store: userStore, onServer: true })
feathers-pinia - 🎁 New `useGet` Utility, params change on `useFind`

Published by marshallswain about 2 years ago

🎁 The New useGet Utility

This release includes the new useGet utility, which has an API similar to the new useFind utility. Here are examples of the two primary use cases. Setup is the same for both use cases:

import { useGet } from 'feathers-pinia'
import { useUsers } from './users.store'

const users = useUsers()

Get From Store, Manual Fetch

With the above lines of code, we can use useGet as intended. This example pulls a record from the store, if available. It also allows manually fetching data.

const { data, get } = useGet(1, { store: users })
await get() // If no arguments are passed, it fetches the record that matches the provided id of `1`.

The data property will reactively update when the response from the API server arrives..

Get from Server

You can provide the onServer option in the params object to pull records from the store and watch the id, like this:

const id = ref(1)
const { data } = useGet(id, { store: userStore, onServer: true })

id.value = 2 // sends a request, when it returns the value of `data` will be the record with the id of `2`.

Renamed paginateOnServer to onServer

In order to keep the APis consistent, the paginateOnServer attribute has been renamed to onServer so that useFind and useGet can use the same API.

feathers-pinia - 🎁 Mega Upgrade to `associateFind`. `useGet` renamed to `useGetWatched`

Published by marshallswain about 2 years ago

🎁 Mega Upgrade to associateFind

The new associateFind utility introduced in the pre-release has been rebuilt on top of the new useFind utility. The newly-rebuilt utility offers a consistent way to keep data in the appropriate store. It uses a combination of previous relationship-handling solutions with some magic of its own to provide the following automatic benefits:

  • Non-Enumerability with some Object.defineProperty magic under the hood.
  • Clean API Requests where associated data never goes to the wrong endpoint.
  • Clone and Commit Support where defining a setter is no longer required.
  • Writable Related Attribute where a built-in, customizable setter allows you to write data to the related store.
  • Customizable Setter through handleSetInstance, so you can keep written data associated to the current record.
  • Pagination Support for lists, and a bunch of other utilities, since they're built on the same Find and Get classes that power useFind and useGet.
  • Loose Coupling between stores.
  • Separation of Concerns keeping data in the correct store.

And all of the functionality comes in a clean, short syntax:

import { BaseModel, associateFind, type AssociateFindUtils } from 'feathers-pinia'

export class User extends BaseModel {
  _id: string
  email = ''
  userId: null | number = null
  createdAt: Date | null = null

  // These are added by associateFind
  messages?: Array<Partial<User>> // reactive list of messages.
  _messages?: AssociateFindUtils<User> // Tools for querying and paginating messages.

  constructor(data?: Partial<Message>, options: Record<string, any> = {}) {
    super(data, options)
    this.init(data)
  }

  static setupInstance(user: Partial<Message>) {
    // access to `store` and `models` is from `this`.
    const { store, models } = this

    // adds a `messages` computed property and `_messages` utility object.
    associateFind(user, 'messages', {
      Model: models.api.Message,
      makeParams: (user) => {
        return { query: { id: user._id } }
      },
      handleSetInstance(user) {
        const id = user.getAnyId()
        if (id && !this.stargazerIds.includes(id)) this.stargazerIds.push(id)
      },
    })
  }
}

🎁 useGet renamed to useGetWatched

The useGet API has been renamed and will stick around for a while to assist in the migration process from Feathers-Vuex. Stay tuned for the next release, which will feature a brand new useGet.

feathers-pinia - 🎁 Declarative Params With `useFind`

Published by marshallswain about 2 years ago

It's now possible to provide a computed params object to useFind. The useFind internals will detect the read-only property and immediately start watching it. When changes occur, it will automatically query for more data. Here's an example:

import { useTasks } from '../stores/tasks'

const taskStore = useTasks()

// 5 most-upvoted tasks for the day
const paramsMostUpvoted = reactive({ 
  query: { 
    dueDate: new Date(), 
    $sort: { upvotes: -1 },
    $limit: 5,
  },
  paginateOnServer: true
})
const { data: mostUpvoted, find: findMostUpvoted } = taskStore.useFind(paramsMostUpvoted)

// 5 least-upvoted tasks for the day
const paramsLeastUpvotedTasks = reactive({ 
  query: { 
    dueDate: new Date(), 
    $sort: { upvotes: 1 },
    $limit: 5,
  },
  paginateOnServer: true
})
const { data: leastUpvotedTasks, find: findLeastUpvoted } = taskStore.useFind(paramsLeastUpvoted)

// Twenty completed tasks for the day
const paramsComplete = reactive({ 
  query: { 
    dueDate: new Date(), 
    isCompleted: true,
    $sort: { upvotes: -1 },
    $limit: 10,
  },
  paginateOnServer: true
})
const { data: completedTasks, find: findComplete } = taskStore.useFind(pastParams)

// Ten most-voted-for, incomplete tasks for the day
const paramsIncomplete = reactive({ 
  query: { 
    dueDate: new Date(), 
    isCompleted: false,
    $limit: 20,
  },
  paginateOnServer: true
})
const { data: incompleteTasks, find: findIncomplete } = taskStore.useFind(paramsIncomplete)
feathers-pinia - 🪲 Match Pinia's `peerDependencies`

Published by marshallswain about 2 years ago

Match pinia's peerDependencies for vue-demi. This fixes errors during npm install on a Vue 3 project.