Comment créer une application de calendrier - Guide complet

Publié le

Cet article est fait pour vous si vous êtes un développeur qui doit intégrer un calendrier dans une application, ou un fondateur qui envisage de créer une application de calendrier ou un produit avec des intégrations de calendrier.

Nous allons voir comment créer une application de calendrier, prendre chaque décision étape par étape, choisir les bonnes technologies et passer en revue les principaux défis et bonnes pratiques.

Quel est le périmètre de cet article ?

L’objectif de cet article est de vous aider à créer une application de calendrier complète, ou à intégrer des fournisseurs de calendrier dans une application existante, afin que vos utilisateurs puissent connecter leurs calendriers et les gérer depuis votre produit.

L’article est également utile si vous souhaitez vous connecter aux fournisseurs de calendrier sans afficher d’interface de calendrier du tout. Quelques exemples : une application de tâches, une application de rencontres ou une fonctionnalité qui insère simplement un événement dans le calendrier d’un utilisateur.

N’hésitez pas à reprendre certaines parties de cet article et à les réutiliser dans votre propre base de code, y compris les choix technologiques, l’approche par API unifiée ou des extraits de code.

Vue d’ensemble de l’architecture et de la pile technologique

Nous avons choisi le Web comme plateforme pour cet exemple, car c’est la plus rapide à mettre en route et la communauté offre beaucoup de support pour les bibliothèques de calendrier.

Le langage de programmation est TypeScript, et le framework web est Next.js.

Voici un aperçu de la pile technique que nous allons utiliser :

  • Frontend : Next.js (App Router, TypeScript)
  • Backend : routes API Next.js + tRPC.
  • Base de données : PostgreSQL (en utilisant Prisma comme ORM)
  • APIs de calendrier : API de calendrier unifiée Apiroc
  • Hébergement : Agnostique au fournisseur, vous pouvez l’héberger sur Vercel ou là où vous êtes à l’aise.
  • Authentification : OAuth2 (Google, Microsoft) via better-auth

Ci-dessous, le schéma d’architecture qui montre comment les éléments de la pile s’articulent :

Définitions :

  • Client (1) : L’appareil de l’utilisateur final. L’illustration montre un appareil mobile, mais cela pourrait aussi être un ordinateur de bureau ou un portable (tout appareil doté d’un navigateur web tel que Google Chrome). Le client affiche le frontend Next.js, gère les interactions utilisateur et communique avec le backend via des requêtes HTTPS sécurisées.
  • Serveur Web (2) : Le serveur web héberge et sert l’interface utilisateur, une application web Next.js (App Router). Il délivre un HTML, CSS et JavaScript optimisés au client et fournit le rendu côté serveur (SSR) ainsi que la régénération statique incrémentale (ISR) pour des performances rapides et des avantages SEO.
  • API Next.js (3) : L’API Next.js est la couche backend, implémentée avec les routes API et tRPC au sein de la même application Next.js. C’est le hub central qui relie le frontend, la base de données et les intégrations externes comme l’API de calendrier unifiée Apiroc. Contrairement aux endpoints REST traditionnels, tRPC offre une communication typée de bout en bout entre le frontend et le backend sans schéma d’API séparé. Le client peut appeler directement des procédures backend, avec une inférence complète des types TypeScript. Cela accélère le développement et réduit les erreurs à l’exécution.
  • PostgreSQL (4) : La base de données PostgreSQL stocke toutes les données persistantes de l’application, y compris les utilisateurs, les sessions, les comptes de calendrier connectés, etc. Elle constitue la source de vérité pour toutes les données liées aux utilisateurs et aux états de synchronisation. Avec Prisma comme couche ORM, le schéma est proprement mappé à la base, ce qui rend les migrations et les requêtes faciles à gérer.
  • API de calendrier unifiée Apiroc (5) : Apiroc est l’API que nous utilisons pour intégrer tous les fournisseurs de calendrier via une interface standardisée. Nous n’avons donc pas à écrire une implémentation par fournisseur, à gérer des formats de données différents, à maintenir plusieurs intégrations ni à suivre les changements d’API des fournisseurs. Notre API appelle Apiroc avec la clé d’API et les calendriers sur lesquels nous voulons travailler (CRUD d’événements, de calendriers, et plus), quel que soit le fournisseur. Apiroc communique ensuite avec les fournisseurs de calendrier et renvoie la réponse dans le même format standardisé pour chaque fournisseur. Illustration de l’API de calendrier unifiée Apiroc

Notez que le Serveur Web (2) et l’API Next.js (3) peuvent tourner sur le même serveur (avec Vercel, Docker, etc.). Ils sont séparés dans le schéma uniquement pour montrer qu’il existe un serveur d’interface et un serveur d’API, même s’ils se trouvent tous deux dans la même base de code Next.js et sont généralement hébergés ensemble.

Concevoir le modèle de données

Maintenant que l’architecture et la pile technique sont claires, il est temps de définir le modèle de données. Nous utilisons Prisma comme ORM.

Ce schéma Prisma définit la structure de données pour une application de calendrier basique qui prend en charge l’authentification des utilisateurs, les connexions de comptes de calendrier (Google, Microsoft) et la synchronisation des événements via une API unifiée.

Les modèles les plus importants sont :

1. User

Représente un utilisateur final de l’application. Chaque utilisateur peut avoir plusieurs sessions, des comptes connectés (OAuth) et des comptes de calendrier. Des champs comme email, name et onboardingCompletedAt aident à suivre le profil et l’état d’onboarding.

2. CalendarAccount

Représente un compte de calendrier externe lié (par ex., un compte Google ou Microsoft). Il stocke le provider, l’email et le status (actif ou expiré). Chaque CalendarAccount appartient à un User et peut contenir plusieurs entrées Calendar.

3. Calendar

Représente un calendrier individuel (comme « Travail », « Personnel » ou « Famille ») au sein d’un compte lié. Il inclut des champs d’affichage tels que name, color, timezone, et des drapeaux comme isPrimary ou isReadOnly. Chaque calendrier est lié à la fois à un User et au CalendarAccount dont il provient.

4. Account

Gère les données du fournisseur OAuth (Google ou Microsoft). Il stocke les jetons d’accès et d’actualisation, les dates d’expiration des jetons et les informations de portée, utilisées pour l’authentification et la synchronisation des calendriers.

5. Session

Suit les sessions de connexion actives des utilisateurs. Contient des champs tels que token, expiresAt, ipAddress et userAgent pour gérer et sécuriser les sessions actives.

6. Verification

Utilisé pour des vérifications ponctuelles, telles que les liens magiques de connexion par e-mail ou les codes d’authentification sans mot de passe. Il stocke des identifiants temporaires et des temps d’expiration.

7. Enums

  • CalendarAccountProvider : Définit les fournisseurs pris en charge ( GOOGLE , MICROSOFT ).
  • CalendarAccountStatus : Indique si un compte connecté est ACTIVE ou EXPIRED .

Le diagramme ER de la base de données :

Database ER Diagram

Ouvrez le fichier schema.prisma dans le dépôt d’exemple pour voir le schéma complet de la base de données, y compris les types et les relations.

Construire le Backend

Comme mentionné, nous utilisons les routes API de Next.js pour construire l’API. Il est très pratique d’avoir l’API et l’UI dans la même base de code et sur le même serveur, car vous pouvez exécuter les deux en même temps.

Mettre en place l’authentification

Nous utilisons better-auth comme framework d’authentification. Better Auth rend l’authentification simple et sans douleur. Suivez le guide Better Auth sur comment intégrer Better Auth avec Next.js, car les étapes sont presque identiques. Vous pouvez également consulter le fichier d’auth dans le dépôt d’exemple pour en savoir plus.

Configurer l’API de calendrier unifiée Apiroc pour communiquer avec tous les fournisseurs de calendrier

Le principal point douloureux lors de la création d’une application de calendrier, ou de l’ajout de calendriers à un produit existant, est la gestion des API spécifiques à chaque fournisseur. Cela coûte beaucoup de temps : il faut apprendre chaque API séparément et gérer des structures de données, des requêtes et des réponses différentes. De plus, il faut construire une intégration séparée pour chaque fournisseur et toutes les maintenir une fois le développement terminé.

Une bonne solution à ce problème est une API de calendrier unifiée qui nous permet d’intégrer tous les fournisseurs via une seule API standardisée. Dans cet exemple, nous utilisons l’API de calendrier unifiée Apiroc.

Pour commencer avec Apiroc, suivez ces étapes :

  1. D’abord, créez un compte Apiroc gratuit.
  2. Après votre inscription, activez les fournisseurs de calendrier que vous souhaitez intégrer. Nous recommandons d’activer Google Calendar et Outlook, afin de voir l’intérêt d’un produit d’API de calendrier unifiée. Vous n’avez pas besoin de créer votre propre client Google ou Microsoft pour le bac à sable et le développement. Vous pouvez utiliser les clients Google et Microsoft d’Apiroc pour connecter des comptes Google Calendar ou Outlook à votre application.
  3. Créez une clé d’API et stockez-la dans la variable d’environnement APIROC_API_KEY

Construire le client API Apiroc

Après avoir configuré Apiroc et récupéré la clé d’API, il est temps de construire le client qui communique avec l’API de calendrier unifiée Apiroc :

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}`,
  );
}

Vous n’avez pas besoin de définir les types du client vous-même. Installez le SDK Node.js officiel (@apiroc/unified-calendar-api-node-sdk) et importez directement les types des comptes utilisateur final, des calendriers et des événements. Le SDK fournit aussi un client prêt à l’emploi, ce qui permet de raccourcir encore les fonctions ci-dessus.

Le diagramme de séquence ci-dessous montre comment l’application de calendrier d’exemple interagit avec l’API de calendrier unifiée Apiroc pour s’intégrer à tous les fournisseurs de calendrier.

Créer les routes de l’API

Une fois le client Apiroc en place, nous pouvons créer les routes API pour gérer les comptes de calendrier et les événements de calendrier. Nous n’avons pas besoin de créer nous-mêmes des API de session, car Better Auth s’en charge.

L’API contient des définitions de routes pour :

  • Comptes de calendrier : route qui expose des méthodes HTTP pour lister tous les comptes de calendrier et supprimer un compte de calendrier par ID.
  • Événements de calendrier : route qui expose des méthodes HTTP pour effectuer des opérations CRUD sur les événements de calendrier.
  • Calendriers : route qui expose des méthodes HTTP pour mettre à jour les calendriers.

Une définition de route en tRPC ressemble à ceci :

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,
      );
    }),
});

La méthode getCalendarEvent provient du client Apiroc que nous avons construit ci-dessus.

Ouvrez le dossier des routes API dans le dépôt d’exemple pour voir le contenu de chaque route. Tout coller ici serait assez répétitif.

Construire le Frontend

Le frontend est construit avec Next.js + TypeScript. Lors de la création d’une application de calendrier, le composant le plus important est, vous l’avez deviné, le calendrier.

D’après notre expérience, les meilleures bibliothèques d’interface de calendrier pour Next.js et React sont :

Pour cet exemple, nous avons choisi react-big-calendar car il est simple à utiliser avec Next.js. Gardez à l’esprit que nous recommandons plutôt fullcalendar pour les applications en production, car il est plus personnalisable et dispose d’un plus large support communautaire.

Fullcalendar est aussi disponible pour d’autres frameworks comme Svelte, Vue.js, etc.

Utilisation de 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;
          }
        }}
      />

Pour voir l’implémentation complète, ouvrez le chemin src/app/(protected)/(calendar) dans le dépôt GitHub. Le composant principal est la page events-calendar.tsx. Vous y trouverez également des composants pour modifier des événements (y compris récurrents), supprimer des événements et créer des événements.

Voici à quoi ressemble le calendrier :

Calendar UI

L’utilisateur peut cliquer sur une cellule et créer un événement :

Calendar App - Create event UI

Lorsque l’utilisateur clique sur un événement existant, il peut le supprimer ou le modifier.

Calendar App - Edit or Delete Event UI

Quand l’événement est récurrent, l’utilisateur peut choisir de modifier uniquement l’instance sélectionnée ou toute la série.

Calendar App - Edit Recurring Event Popup

Voici à quoi ressemble l’interface d’édition d’un événement :

Calendar App- Edit Event UI

L’interface mérite encore un peu de finition, mais le but n’était pas une application de calendrier parfaite. Le but était une application de calendrier fonctionnelle avec une intégration de calendrier qui marche. Vous pouvez vous occuper des styles et les adapter à votre marque.

Défis courants et bonnes pratiques

Créer une application de calendrier ou ajouter des fonctionnalités de calendrier n’est pas toujours simple. Même si la fonctionnalité principale semble simple, de nombreux petits détails peuvent poser des problèmes par la suite. Voici quelques défis courants que vous pourriez rencontrer, ainsi que des bonnes pratiques pour les gérer.

1. Fuseaux horaires

Défi :

Les événements peuvent apparaître à la mauvaise heure lorsque les utilisateurs sont dans des fuseaux horaires différents.

Bonne pratique :

  • Enregistrez toujours les heures en UTC dans votre base de données. Les événements de calendrier sont l’exception, car nous ne recommandons pas de les stocker dans votre base du tout. Lorsque vous récupérez des événements via l’API du fournisseur, vous recevez aussi le fuseau horaire de l’événement.
  • Convertissez en heure locale de l’utilisateur uniquement lors de l’affichage côté frontend.
  • Utilisez une bibliothèque comme date-fns-tz ou luxon pour gérer les conversions horaires. Dans cet exemple, nous utilisons date-fns et date-fns-tz .

2. Événements récurrents

Défi :

Gérer les événements qui se répètent (quotidien, hebdomadaire, mensuel) peut être complexe, notamment lorsque les utilisateurs souhaitent modifier ou supprimer une seule instance.

Bonne pratique :

  • Laissez l’utilisateur choisir s’il veut mettre à jour un seul événement ou toute la série. Google Calendar, Outlook et de nombreux autres clients de calendrier suivent cette pratique. Nous l’avons également adoptée dans notre application d’exemple.

3. Expiration des jetons OAuth

Défi :

Les utilisateurs peuvent perdre la connexion à leurs calendriers si les jetons expirent ou sont révoqués.

Bonne pratique :

  • Stockez en toute sécurité les jetons d’actualisation afin d’obtenir automatiquement de nouveaux jetons d’accès.
  • Gérez proprement les erreurs de jeton et invitez les utilisateurs à reconnecter leurs comptes si nécessaire.

4. Garder les données synchronisées

Défi :

Les données de calendrier deviennent obsolètes si vous ne les récupérez qu’une seule fois.

Bonne pratique :

  • Utilisez les webhooks de l’API de calendrier unifiée Apiroc pour être notifié lorsque des événements changent dans les calendriers Google et Microsoft. API de calendrier unifiée Apiroc - Prise en charge des webhooks
  • Nous recommandons de récupérer les événements auprès des fournisseurs lorsque l’utilisateur interagit avec l’application de calendrier. Ne stockez pas les événements dans votre base de données, car les garder synchronisés avec tous les fournisseurs est un problème difficile. Les stocker localement apporte d’ailleurs peu de bénéfice, puisque vous pouvez récupérer les événements de tous les fournisseurs via Apiroc quand vous en avez besoin.

5. Gestion des erreurs d’API

Défi :

Les API externes (Google, Outlook, iCloud) peuvent renvoyer des erreurs, des limites de taux ou des défaillances temporaires.

Bonne pratique :

  • Ajoutez une logique de nouvelle tentative pour les erreurs temporaires (l’utilisation de temporisateurs est une option viable).
  • Respectez les limites de taux de l’API et patientez si nécessaire. Notez qu’Apiroc a des limites de taux, tout comme les fournisseurs de calendrier tels que Google Calendar et Outlook Calendar.
  • Journalisez toutes les requêtes échouées pour faciliter le débogage.

6. Calendriers volumineux

Défi :

Certains utilisateurs ont des centaines voire des milliers d’événements, ce qui peut ralentir l’application.

Bonne pratique :

  • Chargez les événements par pages (utilisez la pagination). Tous les principaux fournisseurs de calendrier prennent en charge la pagination. Si vous utilisez Apiroc, tous les résultats sont paginés, vous n’aurez donc pas ce problème.
  • Ne récupérez que les événements de l’intervalle de dates visible (par exemple, cette semaine ou ce mois). En règle générale, ne récupérez que les données dont vous avez besoin. Une application de calendrier a une vue jour, semaine, mois et année, récupérez donc les événements en fonction de l’intervalle visible.

7. Confidentialité et sécurité des utilisateurs

Défi :

Les données de calendrier incluent souvent des informations privées.

Bonne pratique :

  • Ne stockez pas les événements de calendrier dans votre base de données. Le stockage du jeton d’accès et du jeton d’actualisation devrait suffire.
  • Chiffrez les jetons et les champs sensibles dans votre base de données. Nous recommandons aussi de chiffrer votre base au repos. Des services comme AWS RDS proposent le chiffrement des données par défaut.
  • Permettez aux utilisateurs de déconnecter leurs comptes de calendrier à tout moment. C’est très important. Si les utilisateurs ne peuvent pas déconnecter ou supprimer leurs calendriers dans votre application, ils révoqueront l’accès directement depuis les paramètres de leur compte Google.

FAQ

1. Puis-je utiliser un autre backend au lieu des routes API de Next.js ?

Oui. Cet exemple utilise des routes API Next.js avec tRPC, mais vous pouvez utiliser n’importe quel framework backend comme Nest.js, Express ou Django. L’essentiel est que votre backend communique avec l’API de calendrier unifiée Apiroc via HTTPS. La structure de la base et la logique d’API restent globalement les mêmes.

2. Dois-je créer mes propres applications développeur Google ou Microsoft ?

Non, vous pouvez utiliser les clients Google et Microsoft d’Apiroc pendant le développement. Quand votre application passe en production, vous utilisez vos propres identifiants OAuth2, ce qui vous donne un contrôle et une image de marque complets auprès de vos utilisateurs.

3. Puis-je utiliser une autre base de données que PostgreSQL ?

Oui. Prisma prend en charge de nombreuses bases de données telles que MySQL, SQLite et MongoDB. Nous avons choisi PostgreSQL parce qu’il est fiable, évolutif et facile à mettre en place en production. Vous pouvez choisir toute autre base et tout autre ORM, selon votre pile technique.

4. L’API de calendrier unifiée Apiroc est-elle gratuite ?

Vous pouvez commencer gratuitement en créant un compte Apiroc. Aucune carte bancaire n’est nécessaire. La formule gratuite inclut jusqu’à 10 End User Accounts et convient parfaitement aux tests et aux petits projets. Pour un usage en production, vous pouvez passer à la formule Pro (25 $/mois, 50 End User Accounts inclus, puis 0,50 $ par End User Account supplémentaire et par mois).

5. Que se passe-t-il si un utilisateur déconnecte son calendrier ?

Lorsqu’un utilisateur se déconnecte, l’application doit supprimer le CalendarAccount et les Calendars correspondants de votre base de données. Vous pouvez conserver des données locales pour l’analytique si nécessaire, mais assurez-vous de ne plus synchroniser ni accéder aux calendriers déconnectés.

7. Puis-je ajouter des notifications ou des rappels ?

Oui. Vous pouvez construire des rappels dans votre application ou utiliser le système de notifications natif du calendrier connecté (Google, Outlook, etc.).

8. Que faire si l’API limite le taux de mes requêtes ?

Apiroc inclut des limites de taux intégrées pour la stabilité (20 requêtes par seconde pour les applications en bac à sable et 300 requêtes par seconde pour les applications en production). Si vous atteignez la limite, attendez et réessayez après un court délai. Les fournisseurs de calendrier ont également leurs propres limites au niveau des applications. Ces limites varient, et Google comme Microsoft permettent de demander une limite plus élevée si nécessaire.

9. Est-il possible de synchroniser les événements dans les deux sens ?

Oui. Apiroc permet de lire et d’écrire des événements, vous pouvez donc à la fois lire et écrire dans les calendriers connectés. Vous recevez des notifications webhook lorsque des changements se produisent côté fournisseur pour les calendriers Google et Microsoft. Les webhooks pour les calendriers Apple iCloud sont prévus mais pas encore disponibles.

10. Comment puis-je déployer ce projet ?

Vous pouvez déployer facilement l’application sur Vercel. Assurez-vous de définir vos variables d’environnement (DATABASE_URL, APIROC_API_KEY, identifiants OAuth, etc.) dans les paramètres de votre projet. Vous pouvez également la containeriser avec Docker si vous préférez davantage de contrôle.