diff --git a/lib/mobilizon/addresses/addresses.ex b/lib/mobilizon/addresses/addresses.ex
index 891167a31abebd94893b9b2a142a0ab1452b6eea..78607311f3d9ef99bd9fa75c764b9fe467bb12d6 100644
--- a/lib/mobilizon/addresses/addresses.ex
+++ b/lib/mobilizon/addresses/addresses.ex
@@ -108,6 +108,7 @@ defmodule Mobilizon.Addresses do
   @doc """
   Processes raw geo data informations and return a `Geo` geometry which can be one of `Geo.Point`.
   """
+  # TODO: Unused, remove me
   def process_geom(%{"type" => type_input, "data" => data}) do
     type =
       if !is_atom(type_input) && type_input != nil do
@@ -145,4 +146,62 @@ defmodule Mobilizon.Addresses do
   defp process_point(_, _) do
     {:error, "Latitude and longitude must be numbers"}
   end
+
+  @doc """
+  Search addresses in our database
+
+  We only look at the description for now, and eventually order by object distance
+  """
+  @spec search_addresses(String.t(), list()) :: list(Address.t())
+  def search_addresses(search, options) do
+    limit = Keyword.get(options, :limit, 5)
+
+    query = from(a in Address, where: ilike(a.description, ^"%#{search}%"), limit: ^limit)
+
+    query =
+      if coords = Keyword.get(options, :coords, false),
+        do:
+          from(a in query,
+            order_by: [fragment("? <-> ?", a.geom, ^"POINT(#{coords.lon} #{coords.lat})'")]
+          ),
+        else: query
+
+    query =
+      if country = Keyword.get(options, :country, nil),
+        do: from(a in query, where: ilike(a.addressCountry, ^"%#{country}%")),
+        else: query
+
+    Repo.all(query)
+  end
+
+  @doc """
+  Reverse geocode from coordinates in our database
+
+  We only take addresses 50km around and sort them by distance
+  """
+  @spec reverse_geocode(number(), number(), list()) :: list(Address.t())
+  def reverse_geocode(lon, lat, options) do
+    limit = Keyword.get(options, :limit, 5)
+    radius = Keyword.get(options, :radius, 50_000)
+    country = Keyword.get(options, :country, nil)
+    srid = Keyword.get(options, :srid, 4326)
+
+    import Geo.PostGIS
+
+    with {:ok, point} <- Geo.WKT.decode("SRID=#{srid};POINT(#{lon} #{lat})") do
+      query =
+        from(a in Address,
+          order_by: [fragment("? <-> ?", a.geom, ^point)],
+          limit: ^limit,
+          where: st_dwithin_in_meters(^point, a.geom, ^radius)
+        )
+
+      query =
+        if country,
+          do: from(a in query, where: ilike(a.addressCountry, ^"%#{country}%")),
+          else: query
+
+      Repo.all(query)
+    end
+  end
 end
diff --git a/lib/mobilizon_web/context.ex b/lib/mobilizon_web/context.ex
index c9abf01d0f8e03bdf04d5647cddb89c03c96b141..be5834d04dc00f59208f83abaf75507c65dda7a7 100644
--- a/lib/mobilizon_web/context.ex
+++ b/lib/mobilizon_web/context.ex
@@ -12,11 +12,17 @@ defmodule MobilizonWeb.Context do
   end
 
   def call(conn, _) do
-    with %User{} = user <- Guardian.Plug.current_resource(conn) do
-      put_private(conn, :absinthe, %{context: %{current_user: user}})
-    else
-      nil ->
-        conn
-    end
+    context = %{ip: to_string(:inet_parse.ntoa(conn.remote_ip))}
+
+    context =
+      case Guardian.Plug.current_resource(conn) do
+        %User{} = user ->
+          Map.put(context, :current_user, user)
+
+        nil ->
+          context
+      end
+
+    put_private(conn, :absinthe, %{context: context})
   end
 end
diff --git a/lib/mobilizon_web/resolvers/address.ex b/lib/mobilizon_web/resolvers/address.ex
new file mode 100644
index 0000000000000000000000000000000000000000..df249365906f5abe9e2e0ba6754ab173866c211c
--- /dev/null
+++ b/lib/mobilizon_web/resolvers/address.ex
@@ -0,0 +1,33 @@
+defmodule MobilizonWeb.Resolvers.Address do
+  @moduledoc """
+  Handles the comment-related GraphQL calls
+  """
+  require Logger
+  alias Mobilizon.Addresses
+  alias Mobilizon.Service.Geospatial
+
+  def search(_parent, %{query: query}, %{context: %{ip: ip}}) do
+    country = Geolix.lookup(ip) |> Map.get(:country, nil)
+
+    local_addresses = Task.async(fn -> Addresses.search_addresses(query, country: country) end)
+
+    remote_addresses = Task.async(fn -> Geospatial.service().search(query) end)
+
+    addresses = Task.await(local_addresses) ++ Task.await(remote_addresses)
+
+    {:ok, addresses}
+  end
+
+  def reverse_geocode(_parent, %{longitude: longitude, latitude: latitude}, %{context: %{ip: ip}}) do
+    country = Geolix.lookup(ip) |> Map.get(:country, nil)
+
+    local_addresses =
+      Task.async(fn -> Addresses.reverse_geocode(longitude, latitude, country: country) end)
+
+    remote_addresses = Task.async(fn -> Geospatial.service().geocode(longitude, latitude) end)
+
+    addresses = Task.await(local_addresses) ++ Task.await(remote_addresses)
+
+    {:ok, addresses}
+  end
+end
diff --git a/lib/mobilizon_web/schema.ex b/lib/mobilizon_web/schema.ex
index 9d140130fd56fbdc5ec9ffa7cdcff8d5edf30ffb..0373b80ce5cb69cae78193d5cb4579b1d33cce20 100644
--- a/lib/mobilizon_web/schema.ex
+++ b/lib/mobilizon_web/schema.ex
@@ -132,6 +132,7 @@ defmodule MobilizonWeb.Schema do
     import_fields(:event_queries)
     import_fields(:participant_queries)
     import_fields(:tag_queries)
+    import_fields(:address_queries)
   end
 
   @desc """
diff --git a/lib/mobilizon_web/schema/address.ex b/lib/mobilizon_web/schema/address.ex
index d3b560a166da8f8f58be648c2aae60821750d5d4..577ead16481823ee01247e74328bce71d0178e17 100644
--- a/lib/mobilizon_web/schema/address.ex
+++ b/lib/mobilizon_web/schema/address.ex
@@ -3,6 +3,7 @@ defmodule MobilizonWeb.Schema.AddressType do
   Schema representation for Address
   """
   use Absinthe.Schema.Notation
+  alias MobilizonWeb.Resolvers
 
   object :physical_address do
     field(:type, :address_type)
@@ -36,4 +37,21 @@ defmodule MobilizonWeb.Schema.AddressType do
     value(:phone, description: "The address is a phone number for a conference")
     value(:other, description: "The address is something else")
   end
+
+  object :address_queries do
+    @desc "Search for an address"
+    field :search_address, type: list_of(:physical_address) do
+      arg(:query, non_null(:string))
+
+      resolve(&Resolvers.Address.search/3)
+    end
+
+    @desc "Reverse geocode coordinates"
+    field :reverse_geocode, type: list_of(:physical_address) do
+      arg(:longitude, non_null(:float))
+      arg(:latitude, non_null(:float))
+
+      resolve(&Resolvers.Address.reverse_geocode/3)
+    end
+  end
 end
diff --git a/test/mobilizon_web/resolvers/address_resolver_test.exs b/test/mobilizon_web/resolvers/address_resolver_test.exs
new file mode 100644
index 0000000000000000000000000000000000000000..dfaf79cca6994b9dccd649c94f5253c357b95656
--- /dev/null
+++ b/test/mobilizon_web/resolvers/address_resolver_test.exs
@@ -0,0 +1,65 @@
+defmodule MobilizonWeb.Resolvers.AddressResolverTest do
+  use MobilizonWeb.ConnCase
+  alias MobilizonWeb.AbsintheHelpers
+  import Mobilizon.Factory
+
+  describe "Address Resolver" do
+    test "search/3 search for addresses", %{conn: conn} do
+      address = insert(:address, description: "10 rue Jangot, Lyon")
+
+      query = """
+        {
+          searchAddress(query: "10 Rue Jangot") {
+            description,
+            geom
+          }
+        }
+      """
+
+      res =
+        conn
+        |> get("/api", AbsintheHelpers.query_skeleton(query, "address"))
+
+      json_response(res, 200)["data"]["searchAddress"]
+      |> Enum.each(fn addr -> assert Map.get(addr, "description") == address.description end)
+    end
+
+    test "geocode/3 reverse geocodes coordinates", %{conn: conn} do
+      address =
+        insert(:address,
+          description: "10 rue Jangot, Lyon"
+        )
+
+      query = """
+        {
+          reverseGeocode(longitude: -23.01, latitude: 30.01) {
+            description,
+            geom
+          }
+        }
+      """
+
+      res =
+        conn
+        |> get("/api", AbsintheHelpers.query_skeleton(query, "address"))
+
+      assert json_response(res, 200)["data"]["reverseGeocode"] == []
+
+      query = """
+        {
+          reverseGeocode(longitude: 45.75, latitude: 4.85) {
+            description,
+            geom
+          }
+        }
+      """
+
+      res =
+        conn
+        |> get("/api", AbsintheHelpers.query_skeleton(query, "address"))
+
+      assert json_response(res, 200)["data"]["reverseGeocode"] |> hd |> Map.get("description") ==
+               address.description
+    end
+  end
+end
diff --git a/test/support/factory.ex b/test/support/factory.ex
index 3584b3d5b074f7f3dfd38a960473566974d2696b..ba31afd4b41dd8f92bed61ed7b23b71d491d405f 100644
--- a/test/support/factory.ex
+++ b/test/support/factory.ex
@@ -70,7 +70,7 @@ defmodule Mobilizon.Factory do
   def address_factory do
     %Mobilizon.Addresses.Address{
       description: sequence("MyAddress"),
-      geom: %Geo.Point{coordinates: {30, -90}, srid: 4326},
+      geom: %Geo.Point{coordinates: {45.75, 4.85}, srid: 4326},
       floor: "Myfloor",
       addressCountry: "My Country",
       addressLocality: "My Locality",
diff --git a/test/support/mocks/geospatial_mock.ex b/test/support/mocks/geospatial_mock.ex
index 8fcf74505215a9a6dc125437e4e50f23273abfa6..f5a3de8b4e08bfd23a8e9d7aa00925b5f8c9565d 100644
--- a/test/support/mocks/geospatial_mock.ex
+++ b/test/support/mocks/geospatial_mock.ex
@@ -1,4 +1,4 @@
-defmodule Mobilizon.Mobilizon.Service.Geospatial.Mock do
+defmodule Mobilizon.Service.Geospatial.Mock do
   @moduledoc """
   Mock for Geospatial Provider implementations
   """
@@ -8,8 +8,8 @@ defmodule Mobilizon.Mobilizon.Service.Geospatial.Mock do
   @behaviour Provider
 
   @impl Provider
-  def geocode(_lon, _lat, _options \\ []), do: [%Address{}]
+  def geocode(_lon, _lat, _options \\ []), do: []
 
   @impl Provider
-  def search(_q, _options \\ []), do: [%Address{}]
+  def search(_q, _options \\ []), do: [%Address{description: "10 rue Jangot, Lyon"}]
 end