In this post, we'll walk through how to access Google Calendar events using Elixir and Phoenix, leveraging the Req HTTP client and Goth for authentication with Google APIs.

My first approach was to use the raw ICS file, but that turned out to be quite tricky to parse (especially with repeating events).

Add the required dependencies

To get started, open your mix.exs file and add these dependencies:

defmodule MyApp.MixProject do
  use Mix.Project

  defp deps do
    [
      {:req, "~> 0.5.0"},
      {:goth, "~> 1.4"}
    ]
  end
end

Don't forget to run mix deps.get to install them.

Setting up Google Calendar API access

Before you can query a Google Calendar, you need to create service account credentials:

  1. Go to Google Cloud Console.
  2. Create a new project (or select an existing one).
  3. Navigate to APIs & Services > Credentials.
  4. Click Create Credentials > Service Account.
  5. After creating the service account, go to Manage Keys and create a JSON key.

This will download a JSON file containing your credentials.

Save it somewhere accessible in your Phoenix project, for example at config/gcp-creds.json.

Still in the Cloud Console:

  1. Go to APIs & Services > Library.
  2. Search for Google Calendar API and enable it for your project.

The service account email (from the credentials JSON) must have access to the calendar (unless it is a public calendar):

  1. Go to Google Calendar.
  2. Open the calendar settings.
  3. Under Share with specific people, add the service account email and give it at least See all event details permission.

Authenticating with Goth

Use the Goth library to authenticate with Google APIs. Configure it in your lib/my_app/application.ex:

defmodule MyApp.Application do
  use Application

  # Read in the credentials file
  @google_credentials File.read!("config/gcp-creds.json") |> Jason.decode!()

  @impl true
  def start(_type, _args) do
    children = [
      MyAppWeb.Telemetry,
      MyApp.Repo,
      {DNSCluster, query: Application.get_env(:my_app, :dns_cluster_query) || :ignore},
      {Phoenix.PubSub, name: MyApp.PubSub},
      # Start the Finch HTTP client for sending emails
      {Finch, name: MyApp.Finch},
      # Add Goth as a child
      {Goth,
       name: MyApp.Goth, 
       source:
         {:service_account, @google_credentials,
          scopes: ["https://www.googleapis.com/auth/calendar.readonly"]}},
      # Start to serve requests, typically the last entry
      MyAppWeb.Endpoint
    ]

    # See https://hexdocs.pm/elixir/Supervisor.html
    # for other strategies and supported options
    opts = [strategy: :one_for_one, name: MyApp.Supervisor]
    Supervisor.start_link(children, opts)
  end
end

Goth will fetch and cache access tokens for the service account automatically.

Get the calendar ID for your Google Calendar

The calendar ID is a required parameter when accessing Google Calendar events through the API, and here's how you can find it:

  1. Go to Google Calendar.

  2. On the left under My calendars, hover over the calendar you want to use.

  3. Click the three dots (โ‹ฎ) next to the calendar name and choose Settings and sharing.

  4. Scroll down to the Integrate calendar section.

  5. You'll see the Calendar IDโ€”it looks something like:

    yourname@gmail.com
    

    or for shared/team calendars:

    abcd1234@group.calendar.google.com
    

Fetching events with Req

The function below fetches calendar events for a given year:

def get_events(calendar_id, year, opts \\ []) do
  # First get an authentication token using Goth
  {:ok, token} = get_token()

  # Get the event using the Google Calendar API
  get_events_from_calendar(calendar_id, year, token)
end

defp get_token() do
  Goth.fetch(YellowduckBe.Goth)
end

defp get_events_from_calendar(calendar_id, year, token) when is_integer(year) do
  {:ok, resp} =
    Req.get("https://www.googleapis.com/calendar/v3/calendars/#{calendar_id}/events",
      headers: [
        Authorization: "Bearer #{token.token}",
        Accept: "application/json"
      ],
      params: [
        timeMin: "#{year}-01-01T00:00:00Z",
        timeMax: "#{year + 1}-01-01T00:00:00Z",
        singleEvents: true,
        maxResults: 2500,
        orderBy: "startTime",
        timeZone: "Europe/Brussels"
      ]
    )

  resp.body["items"]
end

Parameters Explained

  • timeMin, timeMax: Define the date range. Use RFC3339 format (YYYY-MM-DDTHH:MM:SSZ). Only events that start within this range are returned.
  • singleEvents: true: Expands recurring events into individual instances.
  • maxResults: Maximum number of events to return (max is 2500).
  • orderBy: "startTime": Orders results chronologically.
  • timeZone: Sets the time zone used for interpreting timeMin/timeMax.

Summary

Using Elixir, Goth, and Req, you can easily integrate Google Calendar into your Phoenix app. Just set up service account credentials, grant access to the calendar, and use Req.get/2 to query the Google Calendar API with the appropriate parameters.

For more details, refer to the Google Calendar API reference.