Saving to Server
This guide covers how to keep calendar events in sync with a backend - what the calendar sends out on every change, how the bundled RestDataProvider turns those changes into HTTP requests, and what the server side has to look like.
How persistence works
The calendar stores events in memory. Every user action (add, edit, drag, resize, delete) goes through the store as a typed action - add-event, update-event, delete-event. Persistence is the job of a separate "data provider" that you attach to the action chain.
Two pieces are involved:
- The store, which fires actions whenever events change.
- A data provider, which listens for those actions and forwards them to your server.
You wire the provider in once, on init, with api.setNext(provider). From that point on, the provider sees every CRUD action and decides what to do with it. The calendar itself doesn't touch the network - it doesn't know a backend exists.
That split has two consequences:
- The same calendar code works offline, with a custom provider, or with the bundled REST one.
- You can intercept actions before they hit the provider (validation, confirmation) with
api.intercept(...), and observe them after withapi.on(...).
The bundled RestDataProvider handles the common case: a REST endpoint that exposes events as a resource. It serializes dates to ISO strings, debounces updates, and rewrites local ids when the server returns its own.
Serialization
Dates are the only field that needs special handling. The provider's toPayload walks the outgoing object and replaces every Date with an ISO string (date.toISOString()).
On the way back, getData parses incoming events and turns the start, end, and exdates strings back into Date objects. Custom date fields you add yourself need their own parsing - the provider only knows about the three above.
If your server expects a different format, override formatDate:
import { RestDataProvider } from "@svar-ui/vue-calendar";
class MyProvider extends RestDataProvider {
formatDate(date: Date): string {
return date.toISOString().slice(0, 19); // strip milliseconds + Z
}
}
Everything else in the event object goes through JSON.stringify as-is, so custom fields (text, calendarId, priority, etc.) round-trip without extra work.
REST API
Attach the provider in init and load events with getData():
<script setup lang="ts">
import { ref } from "vue";
import { Calendar, Editor, RestDataProvider } from "@svar-ui/vue-calendar";
const provider = new RestDataProvider("https://api.example.com");
const api = ref<any>();
const events = ref<any[]>([]);
const date = ref(new Date());
provider.getData().then(data => {
events.value = data;
if (data.length) date.value = new Date(data[0].start);
});
function init(obj: any) {
api.value.setNext(provider);
}
</script>
<template>
<Calendar ref="api" :init="init" :events="events" :date="date" />
<Editor v-if="api" :api="api" />
</template>
getData() issues a GET /events and parses the dates. setNext(provider) plugs the provider into the action chain - from this point the calendar persists every change.
update-event is debounced by 500 ms inside the provider, so a burst of drag-resize events collapses into one request. add-event strips the temporary client id before sending; the server assigns the real one and the store swaps it in.
If the server URL needs auth headers, set them on the provider:
provider.setHeaders({ Authorization: `Bearer ${token}` });
Custom payloads
The default handlers send the event object verbatim. If your server expects a different shape, subclass and override getHandlers:
import { RestDataProvider } from "@svar-ui/vue-calendar";
class MyProvider extends RestDataProvider {
getHandlers() {
return {
"add-event": {
ignoreID: true,
handler: async data => {
const { id, ...payload } = data.event;
return this.send("calendar/events", "POST", payload);
},
},
"update-event": {
debounce: 500,
handler: async data =>
this.send(`calendar/events/${data.id}`, "PUT", data.event),
},
"delete-event": {
handler: async data =>
this.send(`calendar/events/${data.id}`, "DELETE"),
},
};
}
}
ignoreID on add-event tells the action queue to wait for the server response before letting follow-up actions on the same event run - the local id will be replaced with the one in the response.
Reacting to provider results
The provider returns a promise per action. To run code after the server responds, register a handler with api.on(...):
api.on("add-event", ({ id }) => {
console.log("event saved with id", id);
});
This runs after the provider's handler resolves, with the final id in place.
REST API Reference
The bundled provider expects these endpoints on the URL passed to its constructor:
| Action | Method | Path | Body | Response |
|---|---|---|---|---|
| Load events | GET | /events | - | CalendarEvent[] |
| Add event | POST | /events | event without id | { id } |
| Update event | PUT | /events/{id} | partial event payload | any |
| Delete event | DELETE | /events/{id} | - | any |
Notes:
- All
Datefields in request bodies are ISO 8601 strings. update-eventrequests are debounced by 500 ms per event id.- Recurring edits send the same
update-eventaction withmode: "single" | "following"and anoriginalDate- the server needs to handle splitting recurrences if your app usesrecurring={true}. - The
add-eventresponse should include the new{ id }. Other fields in the response are ignored. - All other actions ignore the response body; rejecting the promise marks the action as failed.
For the action payload shapes, see the Actions reference.