Cómo integrar la API de Google Calendar en tu aplicación

Publicado el

En esta guía explicamos paso a paso cómo integrar la API de Google Calendar en tu aplicación. Cubrimos la configuración del proyecto de Google Cloud, los alcances necesarios, los inconvenientes más comunes y un ejemplo real del flujo de autorización.

Requisitos previos

Esta guía asume que tienes una dirección de correo electrónico, un dominio que puedas usar al crear el proyecto de Google Cloud, algo de experiencia programando y una idea más o menos clara de lo que quieres construir.

La guía también es útil si nunca has trabajado con la API de Google Calendar y quieres entender cada paso necesario para añadirla a tu aplicación.

Cómo utilizar e integrar la API de Google Calendar en tu aplicación

1. Regístrate en Google Developer Console

Si todavía no tienes una cuenta en Google Developer Console, créala en https://console.cloud.google.com/.

2. Crea o selecciona un proyecto de Google Cloud existente

Google Cloud permite que desarrolladores y organizaciones tengan varios proyectos. Asegúrate de estar en el proyecto correcto antes de seguir los pasos de abajo.

Haz clic en el desplegable de proyectos en la parte superior izquierda de la pantalla. El nombre suele coincidir con el de tu proyecto.

Google Cloud Home

Selecciona un proyecto existente o crea uno nuevo haciendo clic en «New Project» en la parte superior derecha del modal.

Google Cloud - Select Project

3. Habilita los servicios de la API de Google Calendar

Cuando ya tengas la cuenta y estés en el proyecto correcto, sigue estos pasos para habilitar los servicios de la API de Google Calendar:

  1. Ve a la Consola de Google Cloud
  2. Haz clic en «APIs & Services» Google Cloud - Click APIs and Services
  3. Haz clic en «Enable APIs and services» Google Cloud - Enable APIs and services
  4. Busca «Google Calendar API» Search for Google Calendar API service
  5. Haz clic en «Enable» para activar el servicio. Enable Google Calendar API service

4. Configura la pantalla de consentimiento OAuth

Tras habilitar la API, el siguiente paso es configurar la pantalla de consentimiento OAuth. Es la pantalla que ven los usuarios finales cuando conectan su calendario con tu aplicación. Normalmente muestra el logotipo, el nombre de la app, los permisos que solicitas y más.

  1. Haz clic en la pestaña «OAuth Consent Screen». Click OAuth Consent Screen
  2. Haz clic en «Get Started». Click Get Started
  3. Rellena la sección «App Information». Introduce el nombre de la app y el correo de soporte. Fill in the “App Information” section
  4. Elige la audiencia . Puede ser interna o externa. Elige interna si la app no será pública y solo usuarios de tu organización podrán conectar sus calendarios. Elige externa si cualquier cuenta pública podrá iniciar sesión, forme parte o no de tu organización. Choose the audience
  5. Introduce tu información de contacto. Google requiere un correo para notificar cambios en el proyecto. How to Integrate Google Calendar API Into Your App
  6. Marca la casilla «Agree to the Google API Services: User Data Policy». Check the Agree to the Google API Services
  7. Haz clic en «Create» . Click “Create”

5. Crea tu cliente OAuth

Una vez configurada la pantalla de consentimiento, puedes crear el cliente OAuth de tu proyecto. Haz clic en la pestaña «Clients» y después en «Create Client».

También puedes hacer clic en «Create OAuth Client» en la página de resumen.

Create OAuth Client

Puedes crear un cliente para cada plataforma en la que se ejecute tu aplicación. Por ejemplo, si desarrollas una app web y otra para iOS, necesitas un ID de cliente OAuth distinto para cada una.

Google Cloud - Client Options

En este ejemplo crearemos un cliente «Web Application» y lo llamaremos «Web Client».

Google Cloud - Web Client

En el mismo flujo configuramos también los orígenes JavaScript autorizados y las URI de redirección autorizadas.

En Authorized JavaScript origins, indica el dominio/URL que aloja tu app web, por ejemplo: myapp.domain.com

Google Cloud - Authorized Domains

En Authorized redirect URIs, introduce todas las URL a las que redirigirás a los usuarios después de que se autentiquen con Google. Google añade el código de autorización a esta URL, y la URL debe incluir el protocolo.

Google Cloud - Setup Redirect URL

Tras completar los campos, haz clic en «Create». Google abre entonces un modal con el Client ID y el Client Secret. Copia ambos valores y guárdalos en un lugar seguro (normalmente en tu archivo .env, ya que los usamos en el flujo de autenticación de más abajo). También puedes descargar el archivo JSON y guardarlo en un gestor de secretos como 1Password.

OAuth Client Created - Modal

6. Añade usuarios de prueba

Mientras desarrollas la aplicación en local, no podrás conectar ninguna cuenta de Google Calendar si no añades usuarios de prueba. El motivo es que la app es externa y Google todavía no la ha aprobado.

Para añadir usuarios de prueba:

  1. Haz clic en la pestaña «Audience». Google Cloud - Audience Tab
  2. Desplázate hasta la sección «Test Users». Test Users Section
  3. Haz clic en «Add Users» y escribe el correo del usuario. Add Test users by email

7. Añade los alcances de calendario que vayas a usar

Según el caso de uso que quieras resolver con la integración, puede que necesites solicitar distintos alcances (scopes) cuando el usuario autorice su cuenta de Google Calendar en tu app.

Piensa en los alcances como permisos que pides a los usuarios. Permiten que tu app acceda a datos privados de su cuenta de Google, por ejemplo para listar calendarios o leer eventos.

Google divide los alcances en sensibles y no sensibles. Si añades alcances sensibles, tendrás que enviar la app a verificación. Lo mismo ocurre si tu app ya está verificada y añades más alcances sensibles.

Para gestionar los alcances:

  1. Haz clic en la pestaña «Data Access». Data Access Tab
  2. Haz clic en «Add or remove scopes». Add Or Remove Scopes Button
  3. Busca el alcance por nombre o valor y agrégalo. Select Scopes Section

8. Familiarízate con la API de Google Calendar

Ahora que el cliente de Google está configurado y toda la información necesaria para que los usuarios conecten sus calendarios está lista, toca familiarizarse con la propia API de Google Calendar.

Te recomendamos leer la página de descripción general de la API de Google Calendar y revisar los endpoints más importantes, como los de eventos y calendarios.

9. Usa un servicio de API de calendario unificada para integrar varios proveedores con una sola API

Si Google Calendar es el único calendario que quieres integrar, puedes saltarte este paso. De lo contrario, te recomendamos usar una API de calendario unificada, que te ofrece una sola API para todos los proveedores.

Con una API de calendario unificada solo construyes y mantienes una integración para todos los proveedores. Si más adelante decides dar soporte a Outlook o iCloud, puedes añadirlos sin escribir una integración nueva.

Además, evitas mantener varias integraciones, lidiar con cambios que rompen la compatibilidad y dedicar tiempo a aprender los detalles de la API de cada proveedor.

Ejemplo de flujo de autorización en Google Calendar

El diagrama de abajo muestra un flujo OAuth sencillo con el que los usuarios pueden conectar su Google Calendar a tu aplicación.

Google Calendar OAuth flow

Google ofrece bibliotecas cliente para Node.js, Python y otros lenguajes. Para simplificar, aquí solo usamos llamadas HTTP y TypeScript.

Lado cliente (UI)

La primera parte es el lado cliente (UI), donde mostramos un botón «Connect Google Calendar».

const googleOauthUrl = getGoogleOAuthUrl()
<button href="googleOauthUrl"   rel="noopener noreferrer"> Connect Google Calendar </button>

Recomendamos usar una función auxiliar para construir la URL de OAuth. Mantiene el código legible y facilita pasar parámetros como el estado del cliente o el consentimiento forzado.


const SCOPES = [
  "openid",
  "email",
  "<https://www.googleapis.com/auth/calendar.calendarlist>",
  "<https://www.googleapis.com/auth/calendar.events>",
  "<https://www.googleapis.com/auth/calendar.readonly>",
  // add more scopes as needed
];

export interface ClientState {
  session: Session;
  returnUrl?: string;
}

export function stateToB64(session: ClientState): string {
  return encode(JSON.stringify(session));
}

export function getGoogleOAuthUrl(
  state: ClientState,
) {
  const params = new URLSearchParams({
    client_id: process.env.GOOGLE_CLIENT_ID || "",
    redirect_uri: `${getHostName()}/api/connect/google`, // change the redirect URL as needed
    response_type: "code",
    scope: SCOPES.join(" "),
    prompt: "consent",
    access_type: "offline",
    state: stateToB64(state),
  });
  return `https://accounts.google.com/o/oauth2/v2/auth?${params}`;
}

El parámetro prompt admite tres valores: none, consent y select_account.

El valor consent es útil cuando un usuario ya ha autorizado sus calendarios, pero quieres que Google vuelva a mostrar la pantalla de autorización. Esto ocurre, por ejemplo, cuando añades nuevos scopes a tu app y quieres que los usuarios los aprueben.

Otro caso para consent es cuando el usuario no concedió todos los scopes que requiere tu app, así que quieres pedirlos de nuevo.

El valor select_account pide al usuario que elija una cuenta.

El valor none no muestra ninguna pantalla de autenticación ni de consentimiento.

Lado API (backend)

A continuación creamos el manejador de la API. Recibe el código y los scopes de Google e intercambia el código por tokens.

En este ejemplo usamos zod para la validación.

import { z } from "zod";
const successSchema = z.object({
  code: z.string(),
  scope: z.string(),
  state: z.string(),
});

const errorSchema = z.object({
  error: z.string(),
});
type ErrorParams = z.infer<typeof errorSchema>;

const querySchema = z.union([successSchema, errorSchema]);

// Handler
const googleHanlder: NextApiHandler = async (req, res) => {
  try {
    const result = querySchema.parse(req.query);
    if (isError(result)) {
      const q = new URLSearchParams({
        error: "ACCESS_DENIED",
      });
      return res.redirect(`/?${q}`);
    }

    const { session, returnUr } = stateFromB64(
      result.state
    );

    if (!hasRequiredScopes(result.scope)) {
      const q = new URLSearchParams({
        error: "MISSING_REQUIRED_PERMISSIONS",
      });
      return res.redirect(`/?${q}`);
    }

    const { access_token, refresh_token, id_token, expires_in } =
      await exchangeCodeForTokens(result.code);
    const { email } = decodeIdToken(id_token);

		// Update or insert the calendar connection, depending on your use case
    const connection = await upsertConnection(
      {
        email,
        accessToken: access_token,
        refreshToken: refresh_token,
        expiresInSeconds: expires_in,
        status: ConnectionStatus.ACTIVE,
        provider: CalendarProvider.GOOGLE,
        scopes: result.scope,
        reminderCount: 0,
        lastRemindedAt: null,
      },
      session.user
    );

    const q = new URLSearchParams({
      cid: connection.id,
    });
    if (returnUrl) q.append("returnUrl", returnUrl);

		// The API redirects back to the client side, returning the connection, or errors if any 
    res.redirect returnUrl ? returnUrl : `/calendars/google?${q}`
    );
  } catch (e: any) {
    let error = JSON.stringify(e);

      const querystr =
        typeof req.query === "string" ? req.query : JSON.stringify(req.query);
      console.error("Error in googleHandler", querystr);
      console.error("Failed to connect Google account", e);

    const q = new URLSearchParams({
      error,
    });
    return res.redirect(`/?${q}`);
  }
};

Igual que en el lado cliente, recomendamos funciones auxiliares pequeñas para intercambiar el código por tokens, decodificar el ID token, leer el estado en base64, etc.

export async function exchangeCodeForTokens(code: string) {
  const data = new FormData();
  data.append("code", code);
  data.append("client_id", process.env.GOOGLE_CLIENT_ID || "");
  data.append("client_secret", process.env.GOOGLE_CLIENT_SECRET || "");
  data.append("redirect_uri", `${getHostName()}/api/connect/google`); // your URL
  data.append("grant_type", "authorization_code");

  try {
    const result = await fetch("<https://oauth2.googleapis.com/token>", {
      method: "POST",
      body: data,
    });

    const json = await result.json();
    if (json.error) throw json;

    const parsed = responseSchema.parse(json);
    return parsed;
  } catch (e) {
    console.error("Exchange failed");
    throw e;
  }
}

export function decodeIdToken(idToken: string) {
  const data = jwt.decode(idToken);
  if (typeof data === "string" || !data?.email) {
    throw new Error(`Could not parse id_token: ${idToken}`);
  }

  return data;
}

function isError(query: Record<string, any>): query is ErrorParams {
  return Boolean(query.error);
}

export function stateFromB64(encoded: string): ClientState {
  const str = decode(encoded);

  return JSON.parse(str) as ClientState;
}

Inconvenientes al integrar la API de Google Calendar

  • La verificación puede tardar varias semanas, así que planifica con antelación. El proceso de aprobación lleva tiempo y es habitual que Google rechace la app en el primer envío. Incluye este retraso en tu calendario y avisa a todas las partes implicadas.
  • Los webhooks caducan tras unas 24 horas, así que renuévalos siempre. Puedes registrar webhooks para detectar cambios en un calendario, pero expiran en unas 24 horas. Un cron job puede encontrar los webhooks que vencen en los próximos 20 minutos y renovarlos.
  • Solicita solo los scopes que necesitas. Puede parecer inteligente pedir tantos scopes como sea posible, ya que nunca sabes qué necesitará la próxima versión de tu app. Recomendamos solicitar solo lo que tu app realmente necesita. Primero, Google es muy exhaustivo durante la verificación, y los scopes de más provocan rechazos e idas y vueltas hasta conseguir la aprobación. Segundo, los usuarios dudan a la hora de conceder permisos que no encajan con lo que hace la app.
  • Gestiona las cuotas y los límites de peticiones. Google tiene una cuota por minuto y proyecto, y otra por minuto, proyecto y usuario. Si superas cualquiera de las dos, responde con un 403 usageLimits o un 429 rateLimitExceeded. Usa backoff exponencial para no alcanzar esos límites.
  • Evita registrar datos personales. Hacer logging está bien, pero elimina antes los datos personales de los eventos, como descripciones y correos de los asistentes.

Integra varios proveedores de calendario con la Unified Calendar API de Apiroc

Nuestro equipo lleva años trabajando con Google Calendar, Outlook e iCloud Calendar y ha construido integraciones de calendario que manejan miles de millones de llamadas a la API. Sabemos lo que implica integrar varios proveedores, conocer los detalles de cada API y mantener cada implementación funcionando de forma fiable.

Por eso hemos creado Apiroc, una API de calendario unificada que admite Google Calendar, Microsoft Outlook e iCloud Calendar de forma nativa. Puedes integrar los principales proveedores mediante una única API robusta y fácil de usar.

Puedes empezar gratis, probar la API con hasta 10 End User Accounts, explorar sus capacidades y actualizar más adelante si necesitas más. No hace falta tarjeta de crédito.

Preguntas frecuentes

¿Por qué tengo que añadir usuarios de prueba?

Una aplicación externa sin verificar solo puede usarse con cuentas de prueba autorizadas mientras la desarrollas y hasta que Google la apruebe.

¿Cuánto tiempo permanecen activos los webhooks de Google Calendar?

Las suscripciones a notificaciones push caducan después de unas 24 horas, así que tu backend debe renovarlas antes de que expiren.

¿Existe una forma más sencilla de añadir calendarios de Outlook o iCloud más adelante?

Sí. Una API de calendario unificada como Apiroc permite integrar Google, Outlook e iCloud mediante una sola API. Así ahorras el tiempo y el coste de construir y mantener integraciones separadas.

¿Necesito “offline_access” para los trabajos en segundo plano?

Sí. Cuando añades access_type=offline a la URL de consentimiento, Google devuelve un refresh token, de modo que tu servidor puede llamar a la API aunque el usuario no esté presente.