Come creare un'app per calendario - Guida completa
Pubblicato il
Questo articolo fa per te se sei uno sviluppatore che deve integrare un calendario in un’applicazione, oppure un founder che sta pensando di creare un’app di calendario o un prodotto con integrazioni di calendario.
Vedremo insieme come costruire un’app di calendario, prendere ogni decisione passo dopo passo, scegliere le tecnologie giuste e analizzare le principali sfide e migliori pratiche.
Qual è l’obiettivo di questo articolo?
Lo scopo di questo articolo è aiutarti a costruire un’app di calendario completa, oppure a integrare i provider di calendario in un’applicazione esistente, in modo che i tuoi utenti possano collegare i propri calendari e gestirli tramite il tuo prodotto.
L’articolo è utile anche se vuoi collegarti ai provider di calendario senza mostrare alcuna interfaccia di calendario. Alcuni esempi: un’app di attività, un’app di incontri o una funzione che inserisce semplicemente un evento nel calendario di un utente.
Sentiti libero di prendere singole parti di questo articolo e riutilizzarle nel tuo codice, incluse le scelte tecnologiche, l’approccio con API unificata o parti del codice stesso.
Panoramica dell’architettura e dello stack tecnologico
Abbiamo scelto il web come piattaforma per questo esempio, perché è la più rapida da mettere in funzione e la community offre un ampio supporto per le librerie di calendario.
Il linguaggio di programmazione è TypeScript, e il framework web è Next.js.
Ecco una panoramica dello stack tecnologico che utilizzeremo:
- Frontend : Next.js (app router, TypeScript)
- Backend : Next.js API routes + tRPC
- Database : PostgreSQL (utilizzando
Prismacome ORM) - API di calendario : Apiroc Unified Calendar API
- Hosting : indipendente dal provider, puoi ospitarlo su Vercel o ovunque preferisci
- Autenticazione : OAuth2 (Google, Microsoft) tramite
better-auth
Di seguito il diagramma dell’architettura, che mostra come i pezzi dello stack si collegano tra loro:
Definizioni:
- Client (1) : Il dispositivo dell’utente finale. L’illustrazione mostra un dispositivo mobile, ma potrebbe essere anche un desktop o un laptop (qualsiasi dispositivo con un browser web come Google Chrome). Il client esegue il rendering del frontend Next.js, gestisce le interazioni dell’utente e comunica con il backend tramite richieste HTTPS sicure.
- Web Server (2) : Il Web Server ospita e serve l’interfaccia utente, un’applicazione web Next.js (App Router). Fornisce HTML, CSS e JavaScript ottimizzati al client e offre rendering lato server (SSR) e rigenerazione statica incrementale (ISR) per prestazioni elevate e vantaggi SEO.
- Next.js API (3) : L’API di Next.js è il livello backend, implementato con le API routes e tRPC all’interno della stessa applicazione Next.js. È l’hub centrale che collega il frontend, il database e le integrazioni esterne come la Apiroc Unified Calendar API. A differenza degli endpoint REST tradizionali, tRPC offre una comunicazione tipizzata end-to-end tra frontend e backend senza uno schema API separato. Il client può chiamare direttamente le procedure del backend, con inferenza completa dei tipi TypeScript. Questo accelera lo sviluppo e riduce gli errori a runtime.
- PostgreSQL (4) : Il database PostgreSQL memorizza tutti i dati persistenti dell’applicazione, inclusi utenti, sessioni, account di calendario collegati, ecc. È il sistema di riferimento per tutti i dati relativi agli utenti e agli stati di sincronizzazione. Con Prisma come livello ORM, lo schema è mappato in modo chiaro sul database, rendendo migrazioni e query facili da gestire.
- Apiroc Unified Calendar API (5) : Apiroc è l’API che usiamo per integrare tutti i provider di calendario tramite un’unica interfaccia standardizzata. In questo modo non dobbiamo scrivere un’implementazione separata per ogni provider, gestire formati dati diversi, mantenere più integrazioni o stare dietro alle modifiche delle API dei provider. La nostra API chiama Apiroc con la chiave API e i calendari su cui vogliamo lavorare (CRUD di eventi, calendari e altro), indipendentemente dal provider. Apiroc comunica poi con i provider di calendario e restituisce la risposta nello stesso formato standardizzato per ogni provider.

Nota che il Web Server (2) e la Next.js API (3) possono girare sullo stesso server (con Vercel, Docker, ecc.). Nel diagramma sono separati solo per chiarire che esistono un server UI e un server API, anche se entrambi vivono nella stessa codebase Next.js e di solito sono ospitati insieme.
Progettazione del modello di dati
Ora che architettura e stack tecnologico sono chiari, è il momento di definire il modello di dati. Usiamo Prisma come ORM.
Questo schema Prisma definisce la struttura dei dati per un’applicazione di calendario di base che supporta l’autenticazione degli utenti, la connessione degli account di calendario (Google, Microsoft) e la sincronizzazione degli eventi tramite un’API unificata.
I modelli più importanti sono:
1. User
Rappresenta un utente finale dell’app. Ogni utente può avere più sessioni, account collegati (OAuth) e account di calendario. Campi come email, name e onboardingCompletedAt aiutano a tracciare il profilo e lo stato di completamento dell’onboarding.
2. CalendarAccount
Rappresenta un account di calendario esterno collegato (ad esempio, un account Google o Microsoft). Memorizza il provider, l’email e lo status (attivo o scaduto). Ogni CalendarAccount appartiene a un singolo User e può contenere più voci Calendar.
3. Calendar
Rappresenta un singolo calendario (come “Lavoro”, “Personale” o “Famiglia”) all’interno di un account collegato. Include campi di visualizzazione come name, color, timezone e flag come isPrimary o isReadOnly. Ogni calendario è collegato sia a un User che al CalendarAccount da cui proviene.
4. Account
Gestisce i dati del provider OAuth (Google o Microsoft). Memorizza i token di accesso e aggiornamento, i tempi di scadenza e le informazioni sugli ambiti (scope), utilizzati per l’autenticazione e la sincronizzazione del calendario.
5. Session
Tiene traccia delle sessioni di accesso attive degli utenti. Contiene campi come token, expiresAt, ipAddress e userAgent per gestire e proteggere le sessioni attive.
6. Verification
Utilizzato per verifiche una tantum, come link magici per il login via email o codici di autenticazione senza password. Memorizza identificatori temporanei e tempi di scadenza.
7. Enums
CalendarAccountProvider: definisce i provider supportati (GOOGLE,MICROSOFT)CalendarAccountStatus: traccia se un account collegato èACTIVEoEXPIRED
Diagramma ER del database:
Apri il file schema.prisma nel repository di esempio per visualizzare lo schema completo del database, inclusi tipi e relazioni.
Costruire il Backend
Come accennato, usiamo le API routes di Next.js per costruire l’API. È molto comodo avere API e UI nella stessa codebase e sullo stesso server, perché puoi eseguire entrambe contemporaneamente.
Creazione dell’autenticazione
Usiamo better-auth come framework di autenticazione. Better Auth rende l’autenticazione semplice e indolore. Segui la guida ufficiale su come integrare Better Auth con Next.js, poiché i passaggi sono quasi identici. Puoi anche consultare il file auth nel repository di esempio per approfondire.
Configurare la Apiroc Unified Calendar API per comunicare con tutti i provider di calendario
Il principale punto critico nella creazione di un’app di calendario, o nell’aggiunta dei calendari a un prodotto esistente, è la gestione delle API specifiche dei vari provider. Questo costa molto tempo: occorre imparare ogni API separatamente e gestire strutture dati, richieste e risposte diverse. Inoltre, bisogna creare un’integrazione separata per ogni provider e mantenerle tutte dopo la fine dello sviluppo.
Una buona soluzione a questo problema è una API di calendario unificata che ci permette di integrarci con tutti i provider tramite un’unica API standardizzata. In questo esempio, usiamo la Apiroc Unified Calendar API.
Per iniziare con Apiroc, segui questi passaggi:
- Per prima cosa, registrati su Apiroc e crea un account gratuito.
- Dopo la registrazione, abilita i provider di calendario che desideri integrare. Ti consigliamo di abilitare Google Calendar e Outlook, così da vedere il vantaggio di un prodotto basato su un’API di calendario unificata. Non devi creare un tuo client Google o Microsoft per il sandbox e lo sviluppo. Puoi usare i client Google e Microsoft di Apiroc per collegare account Google Calendar o Outlook alla tua applicazione.
- Crea una chiave API e salvala nella variabile di ambiente
APIROC_API_KEY
Creare il client dell’API Apiroc
Dopo aver configurato Apiroc e ottenuto la chiave API, è il momento di creare il client che comunica con la Apiroc Unified Calendar API:
import { env } from "@/env";
import type {
EndUserAccount,
PaginatedResponse,
UnifiedCalendar,
UnifiedEvent as UniversalEvent,
} from "@/server/lib/apiroc/types";
import ky from "ky";
export const apirocApi = ky.create({
prefixUrl: env.NEXT_PUBLIC_APIROC_URL,
headers: {
"x-api-key": env.APIROC_API_KEY,
},
});
export async function getEndUserAccountById(id: string) {
const response = await apirocApi.get<EndUserAccount>(
`endUserAccounts/${id}`,
);
return response.json();
}
export async function getCalendarsForEndUserAccount(endUserAccountId: string) {
const response = await apirocApi.get<
PaginatedResponse<UnifiedCalendar>
>(`calendars/${endUserAccountId}`);
return response.json();
}
interface GetCalendarEventsParams {
pageToken?: string;
pageSize?: number;
syncToken?: string;
startDateTime?: string;
endDateTime?: string;
timeZone?: string;
expandRecurrences?: boolean;
}
export async function getCalendarEvents(
endUserAccountId: string,
calendarId: string,
params: GetCalendarEventsParams = {},
) {
const queryParams = new URLSearchParams(params as Record<string, string>);
const response = await apirocApi.get<
PaginatedResponse<UniversalEvent>
>(`events/${endUserAccountId}/${calendarId}?${queryParams}`);
return response.json();
}
export async function getCalendarEvent(
endUserAccountId: string,
calendarId: string,
eventId: string,
) {
const response = await apirocApi.get<UniversalEvent>(
`events/${endUserAccountId}/${calendarId}/${eventId}`,
);
return response.json();
}
export async function createCalendarEvent(
endUserAccountId: string,
calendarId: string,
event: Partial<UniversalEvent>,
) {
const response = await apirocApi.post<UniversalEvent>(
`events/${endUserAccountId}/${calendarId}`,
{json: event},
);
return response.json();
}
export async function editCalendarEvent(
endUserAccountId: string,
calendarId: string,
eventId: string,
event: Partial<UniversalEvent>,
) {
const response = await apirocApi.put<UniversalEvent>(
`events/${endUserAccountId}/${calendarId}/${eventId}`,
{json: event},
);
return response.json();
}
export async function deleteCalendarEvent(
endUserAccountId: string,
calendarId: string,
eventId: string,
) {
await apirocApi.delete(
`events/${endUserAccountId}/${calendarId}/${eventId}`,
);
}
Non devi definire i tipi del client da solo. Installa l’SDK ufficiale per Node.js (@apiroc/unified-calendar-api-node-sdk) e importa direttamente da lì i tipi per account utente finale, calendari ed eventi. L’SDK include anche un client pronto all’uso, quindi le funzioni qui sopra possono essere ancora più brevi.
Il diagramma di sequenza qui sotto mostra come l’app di calendario di esempio interagisce con la Apiroc Unified Calendar API per integrarsi con tutti i provider di calendario.
Creare le API routes
Con il client Apiroc pronto, possiamo creare le rotte API per la gestione degli account di calendario e degli eventi. Non è necessario creare da soli le API per le sessioni, perché se ne occupa Better Auth.
L’API contiene definizioni di rotte per:
- Account di calendario : Rotta che espone metodi HTTP per elencare tutti gli account di calendario e per eliminarne uno tramite ID.
- Eventi del calendario : Rotta che espone metodi HTTP per eseguire operazioni CRUD sugli eventi.
- Calendari : Rotta che espone metodi HTTP per aggiornare i calendari.
Una definizione di rotta con tRPC è simile a questa:
export const calendarEventsRouter = createTRPCRouter({
getCalendarEvent: publicProcedure
.input(
z.object({
endUserAccountId: z.string(),
calendarId: z.string(),
eventId: z.string(),
}),
)
.query(async ({ ctx, input }) => {
return await getCalendarEvent(
input.endUserAccountId,
input.calendarId,
input.eventId,
);
}),
});
Il metodo getCalendarEvent proviene dal client Apiroc che abbiamo costruito sopra.
Apri la cartella delle rotte API nel repository di esempio per vedere il contenuto di ogni rotta. Incollarle tutte qui renderebbe l’articolo ripetitivo.
Costruire il Frontend
Il frontend è costruito con Next.js + TypeScript. Quando si sviluppa un’app di calendario, il componente più importante è, esatto, il calendario.
In base alla nostra esperienza, le migliori librerie per interfacce calendario in Next.js e React sono:
Per questo esempio, abbiamo scelto react-big-calendar per la sua semplicità d’uso con Next.js. Tieni presente che consigliamo fullcalendar per le applicazioni in produzione, poiché è più personalizzabile e ha un supporto della community più ampio.
Fullcalendar è disponibile anche per altri framework come Svelte, Vue.js, ecc.
Utilizzo di react-big-calendar:
<Calendar
culture="en-US"
localizer={localizer}
events={events}
defaultView="week"
eventPropGetter={eventPropGetter}
components={components}
onSelectSlot={(slotInfo) => {
setCreateEventStart(slotInfo.start);
setCreateEventEnd(slotInfo.end);
setCreateEventOpen(true);
}}
onSelectEvent={(event) => {
setSelectedEvent(event);
}}
selectable
onRangeChange={(range) => {
// Week view: range is array of dates
if (Array.isArray(range) && range.length >= 2) {
setDateRange([range[0]!, range[range.length - 1]!]);
return;
}
// Month view: range is object with start/end
if (
range &&
typeof range === "object" &&
"start" in range &&
"end" in range
) {
setDateRange([range.start, range.end]);
return;
}
// Day view: range is a single Date
if (range instanceof Date) {
setDateRange([range, range]);
return;
}
}}
/>
Per vedere l’implementazione completa, apri il percorso src/app/(protected)/(calendar) nel repository GitHub. Il componente principale è la pagina events-calendar.tsx. Troverai anche componenti per la modifica, l’eliminazione e la creazione di eventi (inclusi gli eventi ricorrenti).
Ecco come appare il calendario:
L’utente può cliccare su una cella e creare un evento:
Quando l’utente clicca su un evento esistente, può eliminarlo o modificarlo.
Quando l’evento è ricorrente, l’utente può scegliere se modificare solo l’istanza selezionata o l’intera serie.
Ecco come appare l’interfaccia di modifica evento:
L’interfaccia ha bisogno di qualche rifinitura, ma l’obiettivo non era un’app di calendario perfetta. L’obiettivo era un’app di calendario funzionante con un’integrazione di calendario che funziona. Puoi occuparti dello stile grafico e adattarlo al brand della tua applicazione.
Sfide Comuni e Migliori Pratiche
Creare un’app di calendario o aggiungere funzionalità di calendario non è sempre semplice. Anche se la funzionalità principale sembra banale, ci sono molti piccoli dettagli che possono causare problemi in seguito. Di seguito trovi alcune sfide comuni che potresti incontrare e qualche migliore pratica per affrontarle.
1. Fusi orari
Sfida:
Gli eventi possono essere visualizzati all’orario sbagliato quando gli utenti si trovano in fusi orari diversi.
Migliore pratica:
- Salva sempre gli orari nel database in formato UTC. L’eccezione sono gli eventi del calendario, perché non consigliamo di archiviarli nel database. Quando recuperi gli eventi tramite l’API del provider, ricevi anche il fuso orario dell’evento.
- Converti l’orario nel fuso orario locale dell’utente solo al momento della visualizzazione nel frontend.
- Utilizza una libreria come
date-fns-tzoluxonper gestire le conversioni di fuso orario. In questo esempio, usiamodate-fnsedate-fns-tz.
2. Eventi ricorrenti
Sfida:
Gestire eventi che si ripetono (giornalieri, settimanali, mensili) può essere complesso, soprattutto quando gli utenti vogliono modificare o eliminare solo una singola istanza.
Migliore pratica:
- Permetti all’utente di scegliere se aggiornare un solo evento o l’intera serie. Google Calendar, Outlook e molti altri client di calendario seguono questa pratica. L’abbiamo seguita anche nella nostra app di esempio.
3. Scadenza dei token OAuth
Sfida:
Gli utenti possono perdere la connessione ai propri calendari se i token scadono o vengono revocati.
Migliore pratica:
- Memorizza i refresh token in modo sicuro, per poter ottenere automaticamente nuovi access token.
- Gestisci gli errori dei token con attenzione e invita l’utente a ricollegare il proprio account, se necessario.
4. Mantenere i dati sincronizzati
Sfida:
I dati del calendario diventano obsoleti se vengono recuperati una sola volta.
Migliore pratica:
- Utilizza i webhook della Apiroc Unified Calendar API per ricevere una notifica quando gli eventi cambiano nei calendari Google e Microsoft.

- Ti consigliamo di recuperare gli eventi dai provider di calendario quando l’utente interagisce con l’app. Non memorizzare gli eventi nel tuo database: mantenerli sincronizzati con tutti i provider è un problema difficile. Salvarli in locale porta inoltre pochi vantaggi, perché puoi recuperare gli eventi di tutti i provider tramite Apiroc ogni volta che ti servono.
5. Gestione degli errori API
Sfida:
Le API esterne (Google, Outlook, iCloud) possono restituire errori, limiti di velocità o guasti temporanei.
Migliore pratica:
- Aggiungi una logica di retry per gli errori temporanei (i timeout sono una buona opzione).
- Rispetta i limiti di velocità delle API e riduci il numero di richieste quando necessario. Tieni presente che Apiroc ha dei rate limit, così come i provider di calendario come Google Calendar e Outlook Calendar.
- Registra tutte le richieste fallite per semplificare il debug.
6. Grandi volumi di eventi
Sfida:
Alcuni utenti hanno centinaia o migliaia di eventi, e questo può rallentare l’app.
Migliore pratica:
- Carica gli eventi in modo paginato. La paginazione è supportata da tutti i principali provider di calendario. Se usi Apiroc, tutti i risultati sono già paginati, quindi non avrai questo problema.
- Recupera solo gli eventi dell’intervallo di date visibile (ad esempio, la settimana o il mese corrente). Come regola generale, recupera solo i dati di cui hai bisogno. Un’app di calendario ha una vista giorno, settimana, mese e anno, quindi recupera gli eventi in base all’intervallo visibile.
7. Privacy e sicurezza degli utenti
Sfida:
I dati del calendario contengono spesso informazioni private.
Migliore pratica:
- Non archiviare gli eventi del calendario nel tuo database. È sufficiente conservare l’access token e il refresh token.
- Cifra i token e i campi sensibili nel tuo database. Ti consigliamo anche di criptare il database a riposo; ad esempio, AWS RDS offre la crittografia dei dati integrata.
- Consenti agli utenti di scollegare i propri account calendario in qualsiasi momento. È molto importante: se gli utenti non possono disconnettere o eliminare i calendari nella tua app, revocheranno l’accesso direttamente dalle impostazioni del loro account Google.
FAQ
1. Posso utilizzare un backend diverso invece delle API routes di Next.js?
Sì. Questo esempio usa le API routes di Next.js con tRPC, ma puoi usare qualsiasi framework backend come Nest.js, Express o Django.
La parte fondamentale è che il tuo backend comunichi con la Apiroc Unified Calendar API tramite HTTPS. La struttura del database e la logica dell’API restano sostanzialmente le stesse.
2. Devo creare le mie app sviluppatore Google o Microsoft?
No, puoi utilizzare i client Google e Microsoft di Apiroc durante lo sviluppo.
Quando la tua app passa in produzione, usi le tue credenziali OAuth2, che ti danno pieno controllo e branding personalizzato verso i tuoi utenti.
3. Posso utilizzare un database diverso da PostgreSQL?
Sì. Prisma supporta molti database come MySQL, SQLite e MongoDB.
Abbiamo scelto PostgreSQL perché è affidabile, scalabile e facile da configurare in produzione. Puoi scegliere qualsiasi altro database e ORM, a seconda del tuo stack tecnologico.
4. La Apiroc Unified Calendar API è gratuita?
Puoi iniziare gratuitamente creando un account Apiroc. Non serve alcuna carta di credito.
Il piano gratuito include fino a 10 End User Account ed è ideale per test e piccoli progetti. Per l’uso in produzione, puoi passare al piano Pro (25 $/mese, 50 End User Account inclusi, poi 0,50 $ per ogni End User Account aggiuntivo al mese).
5. Cosa succede se un utente scollega il proprio calendario?
Quando un utente scollega un calendario, l’app deve rimuovere il relativo CalendarAccount e i Calendars associati dal database.
Puoi mantenere dati locali a fini analitici se necessario, ma assicurati di non sincronizzare o accedere più ai calendari disconnessi.
7. Posso aggiungere notifiche o promemoria?
Sì. Puoi costruire i promemoria direttamente nella tua app oppure utilizzare il sistema di notifiche nativo del calendario connesso (Google, Outlook, ecc.).
8. Cosa succede se l’API limita le mie richieste?
Apiroc include limiti di velocità integrati per garantire la stabilità (20 richieste al secondo per le app in sandbox e 300 richieste al secondo per le app in produzione). Se raggiungi il limite, attendi e riprova dopo un breve intervallo. Tieni presente che anche i provider di calendario hanno i propri rate limit a livello di applicazione. Sia Google che Microsoft offrono la possibilità di richiedere limiti più elevati se necessario.
9. È possibile sincronizzare gli eventi in entrambe le direzioni?
Sì. Apiroc supporta la lettura e la scrittura degli eventi, quindi puoi sia leggere che scrivere nei calendari collegati.
Ricevi notifiche tramite webhook quando si verificano modifiche lato provider per i calendari Google e Microsoft. I webhook per i calendari Apple iCloud sono previsti ma non ancora disponibili.
10. Come posso distribuire questo progetto?
Puoi distribuire facilmente l’app su Vercel.
Assicurati di impostare le variabili d’ambiente (DATABASE_URL, APIROC_API_KEY, credenziali OAuth, ecc.) nelle impostazioni del progetto.
Puoi anche containerizzarla con Docker se preferisci un maggiore controllo.
