Back

Implementing repository pattern with Vue composables

Warning I need to update this article. I don't advise following its guidance anymore.

In this guide, I'll explain how to implement the repository pattern with Vue composables in a Nuxt 3 application.

Maintaining a clean and flexible data access layer is crucial for scalable applications. The repository pattern provides an abstraction between the data access logic and the rest of the application, making it easier to manage and switch between different data sources.

Setting up the repository configuration

In the App.vue, we set up the repository configuration by providing a centralized repository implementation:

<script lang="ts" setup>
const config = useRuntimeConfig()
const userRepository = {
  list: () => {
    // some implementation based on database driver
  }
}

provide('user-repository', userRepository)
</script>

<template>
  <div>
    <h1>Users</h1>
    <UserList />
  </div>
</template>

In practice, you might want to create a component dedicated to providing these injections, e.g. ConfigurationProvider.vue. TypeScript users will also prefer to add types to provide/inject.

This approach allows for flexible dependency injection, enabling easy swapping of data sources based on the runtime configuration, as well as easy swapping for testing.

Creating a reusable composable

The useUsers composable encapsulates the repository logic, providing a clean interface for data retrieval:

export function useUsers() {
  const userRepository = inject('user-repository')

  if (!userRepository) {
    throw new Error('No user repository provided')
  }

  const list = () => {
    return userRepository.findAll()
  }

  return {
    list
  }
}

By using dependency injection, we decouple the data fetching logic from the specific implementation, making the code more modular and testable.

Consuming the composable in components

In the UserList.vue, we demonstrate how easily the composable can be used to fetch and display data:

<script lang="ts" setup>
const { list } = useUsers()
const { data: users } = await useAsyncData('users', list)
</script>

<template>
  <div v-for="user in users" :key="user.id">
    {{ user.name }}
  </div>
</template>

Conclusion

The repository pattern with Vue composables offers a powerful approach to managing data access in Nuxt 3 applications. By abstracting the data retrieval logic and using dependency injection, we create more maintainable and flexible code that can easily adapt to changing requirements or data sources.

Last updated on December 13, 2024.

👋 About the author

I'm Laurent, a freelance developer experience engineer that helps dev tools build great onboarding experiences.

I specialize in technical writing, UX writing, and full-stack development.

✍️ Related posts

Read my other articles.
Back to the blog

💌 Get in touch

Find me on Bluesky, X (formerly Twitter), LinkedIn, and Github.

You can also contact me via email.