@digiphilo/opencage-vue
v1.1.0
Published
Vue 3 SDK/Plugin for OpenCage Geocoding API with TypeScript support
Maintainers
Readme
OpenCage Vue.js SDK
A comprehensive Vue 3 plugin and composable library for the OpenCage Geocoding API with full TypeScript support, reactive data binding, and Vue 3 Composition API integration.
Features
✅ Vue 3 Plugin: Global installation with automatic injection
✅ Composition API: Full support for Vue 3's Composition API
✅ TypeScript: Complete type definitions and IntelliSense support
✅ Reactivity: Leverage Vue's reactivity system (ref, reactive, computed)
✅ State Management: Built-in state management for geocoding operations
✅ Error Handling: Reactive error states and comprehensive error management
✅ Caching: Smart caching with Vue reactivity and TTL support
✅ Debouncing: Built-in debouncing for real-time search
✅ Batch Processing: Process multiple queries with progress tracking
✅ SSR Support: Server-side rendering compatibility
✅ Tree Shaking: ESM modules with optimal bundle sizes
✅ DevTools: Vue DevTools integration for debugging
Installation
npm install @opencage/vue-sdk
# or
yarn add @opencage/vue-sdk
# or
pnpm add @opencage/vue-sdkQuick Start
1. Plugin Installation (Recommended)
// main.ts
import { createApp } from 'vue'
import OpenCageVue from '@opencage/vue-sdk'
import App from './App.vue'
const app = createApp(App)
app.use(OpenCageVue, {
apiKey: 'YOUR_OPENCAGE_API_KEY',
// Optional configuration
cache: true,
debounce: 300,
retryAttempts: 3
})
app.mount('#app')2. Using Composition API
<template>
<div>
<input
v-model="searchQuery"
placeholder="Search for a location..."
@input="handleSearch"
/>
<div v-if="loading">Searching...</div>
<div v-if="error" class="error">{{ error.message }}</div>
<div v-if="data" class="results">
<div v-for="result in data" :key="result.formatted" class="result">
<h3>{{ result.formatted }}</h3>
<p>Lat: {{ result.geometry.lat }}, Lng: {{ result.geometry.lng }}</p>
<p>Confidence: {{ result.confidence }}</p>
</div>
</div>
<div v-if="rateLimit" class="rate-limit">
API Calls Remaining: {{ rateLimit.remaining }}/{{ rateLimit.limit }}
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { useOpenCage } from '@opencage/vue-sdk'
const searchQuery = ref('')
const {
data,
loading,
error,
rateLimit,
geocode,
clearResults
} = useOpenCage()
const handleSearch = async () => {
if (searchQuery.value.length < 3) {
clearResults()
return
}
await geocode(searchQuery.value)
}
</script>3. Using Options API
<template>
<div>
<input
v-model="searchQuery"
placeholder="Search for a location..."
@input="handleSearch"
/>
<div v-if="loading">Searching...</div>
<div v-if="error">{{ error }}</div>
<div v-if="results">
<div v-for="result in results" :key="result.formatted">
{{ result.formatted }}
</div>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
data() {
return {
searchQuery: '',
results: null,
loading: false,
error: null
}
},
methods: {
async handleSearch() {
if (this.searchQuery.length < 3) return
this.loading = true
this.error = null
try {
const response = await this.$opencage.geocode(this.searchQuery)
this.results = response.data
} catch (err) {
this.error = err.message
} finally {
this.loading = false
}
}
}
})
</script>Advanced Usage
Reverse Geocoding
<script setup lang="ts">
import { useReverseGeocoding } from '@opencage/vue-sdk'
const { data, loading, error, reverseGeocode } = useReverseGeocoding()
// Get location from coordinates
const getLocationInfo = async (lat: number, lng: number) => {
await reverseGeocode(lat, lng, {
language: 'en',
limit: 1
})
}
// Example: Get user's current location
navigator.geolocation.getCurrentPosition(async (position) => {
await getLocationInfo(position.coords.latitude, position.coords.longitude)
})
</script>Batch Geocoding with Progress
<script setup lang="ts">
import { useOpenCage } from '@opencage/vue-sdk'
const {
data,
loading,
batchProgress,
batchGeocode
} = useOpenCage()
const addresses = [
'Berlin, Germany',
'Paris, France',
'London, UK',
'Madrid, Spain'
]
const processBatch = async () => {
const results = await batchGeocode(addresses)
console.log('Batch results:', results)
}
</script>
<template>
<div>
<button @click="processBatch" :disabled="loading">
Process Batch
</button>
<div v-if="batchProgress" class="progress">
<div class="progress-bar">
<div
class="progress-fill"
:style="{ width: batchProgress.percentage + '%' }"
></div>
</div>
<p>
{{ batchProgress.completed }}/{{ batchProgress.total }}
({{ batchProgress.percentage }}%)
</p>
<p v-if="batchProgress.currentQuery">
Current: {{ batchProgress.currentQuery }}
</p>
</div>
</div>
</template>Advanced Query Options
import { useOpenCage, type GeocodingQuery } from '@opencage/vue-sdk'
const { geocode } = useOpenCage()
// Advanced geocoding with options
const advancedSearch = async () => {
const query: GeocodingQuery = {
query: 'restaurant',
language: 'en',
country: 'DE', // Restrict to Germany
bounds: [52.3, 13.0, 52.6, 13.7], // Berlin bounding box
limit: 10,
minConfidence: 7,
noAnnotations: false
}
await geocode(query)
}Caching and Performance
const {
geocode,
clearCache,
getCacheStats
} = useOpenCage({
cache: true,
cacheSize: 200,
cacheTtl: 600000, // 10 minutes
debounce: 500
})
// Check cache statistics
const stats = getCacheStats()
console.log('Cache stats:', stats)
// Clear cache when needed
clearCache()Configuration Options
Plugin Options
app.use(OpenCageVue, {
apiKey: 'YOUR_API_KEY', // Required
baseUrl: 'https://api.opencagedata.com/geocode/v1', // Optional
timeout: 10000, // Request timeout in ms
cache: true, // Enable caching
cacheSize: 100, // Max cache entries
cacheTtl: 300000, // Cache TTL in ms (5 minutes)
debounce: 300, // Debounce delay in ms
retryAttempts: 3, // Number of retry attempts
retryDelay: 1000 // Delay between retries in ms
})Composable Options
const { geocode } = useOpenCage({
// Override global settings per composable
apiKey: 'DIFFERENT_API_KEY',
cache: false,
debounce: 500
})SSR Support (Nuxt.js)
1. Create a plugin file
// plugins/opencage.client.ts
import OpenCageVue from '@opencage/vue-sdk'
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.use(OpenCageVue, {
apiKey: process.env.OPENCAGE_API_KEY
})
})2. Add to nuxt.config.ts
export default defineNuxtConfig({
runtimeConfig: {
public: {
opencageApiKey: process.env.OPENCAGE_API_KEY
}
},
// Ensure client-side only loading
ssr: true
})3. Use in components
<script setup lang="ts">
import { useOpenCage } from '@opencage/vue-sdk'
// This will only run on client-side
const { data, geocode } = process.client ? useOpenCage() : {
data: ref(null),
geocode: () => Promise.resolve(null)
}
</script>TypeScript Support
The SDK includes comprehensive TypeScript definitions:
import type {
OpenCageOptions,
GeocodingQuery,
GeocodingResult,
OpenCageError,
UseOpenCageReturn
} from '@opencage/vue-sdk'
// Fully typed composable
const composable: UseOpenCageReturn = useOpenCage({
apiKey: 'your-key',
cache: true
})
// Type-safe query building
const query: GeocodingQuery = {
query: 'Berlin',
language: 'en',
limit: 5,
minConfidence: 8
}
await composable.geocode(query)Error Handling
<script setup lang="ts">
import { useOpenCage } from '@opencage/vue-sdk'
const { data, error, geocode, retry } = useOpenCage()
const handleSearch = async (query: string) => {
try {
await geocode(query)
} catch (err) {
// Error is also available in reactive `error` ref
console.error('Geocoding failed:', err)
}
}
// Retry failed requests
const retryLastRequest = async () => {
await retry()
}
</script>
<template>
<div>
<!-- Show error state -->
<div v-if="error" class="error">
<p>{{ error.message }}</p>
<button @click="retryLastRequest">Retry</button>
</div>
</div>
</template>Rate Limiting
The SDK automatically handles OpenCage API rate limits and provides rate limit information:
<script setup lang="ts">
import { useOpenCage } from '@opencage/vue-sdk'
const { rateLimit, geocode } = useOpenCage()
const handleSearch = async (query: string) => {
// Check rate limit before making request
if (rateLimit.value && rateLimit.value.remaining === 0) {
console.warn('Rate limit exceeded. Try again later.')
return
}
await geocode(query)
// Check remaining requests after geocoding
console.log('Remaining requests:', rateLimit.value?.remaining)
}
</script>Testing
The SDK includes comprehensive testing utilities:
import { mount } from '@vue/test-utils'
import { useOpenCage } from '@opencage/vue-sdk'
import MyComponent from './MyComponent.vue'
// Mock the composable
vi.mock('@opencage/vue-sdk', () => ({
useOpenCage: vi.fn(() => ({
data: ref([]),
loading: ref(false),
error: ref(null),
geocode: vi.fn()
}))
}))
test('geocoding component', () => {
const wrapper = mount(MyComponent)
// Your test assertions
})Migration from Vue 2
If you're upgrading from Vue 2, here are the key changes:
Vue 2 (Old)
// Vue 2 style
export default {
data() {
return {
results: null
}
},
async mounted() {
this.results = await this.$opencage.geocode('Berlin')
}
}Vue 3 (New)
<script setup lang="ts">
// Vue 3 Composition API
import { onMounted } from 'vue'
import { useOpenCage } from '@opencage/vue-sdk'
const { data, geocode } = useOpenCage()
onMounted(async () => {
await geocode('Berlin')
})
</script>Performance Tips
- Use caching: Enable caching for repeated queries
- Implement debouncing: Use the built-in debounced functions for search-as-you-type
- Batch requests: Use batch geocoding for multiple addresses
- Optimize queries: Use specific query options to get more relevant results
- Monitor rate limits: Check rate limit status to avoid API quota issues
API Reference
useOpenCage(options?)
Main composable for geocoding operations.
Returns: UseOpenCageReturn
useForwardGeocoding(options?)
Specialized composable for forward geocoding only.
Returns: UseForwardGeocodingReturn
useReverseGeocoding(options?)
Specialized composable for reverse geocoding only.
Returns: UseReverseGeocodingReturn
Contributing
We welcome contributions! Please see our Contributing Guidelines for details.
License
MIT License
Copyright (c) 2025 Rome Stone
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Support
- 📧 Email: [email protected]
- 📖 Documentation: opencagedata.com/api
- 🐛 Issues: GitHub Issues
Author: Rome Stone
