Geoip & the Future 🎉

Liam Stanley
Published 4 years ago

GeoIP is an open source (and freely hosted) GeoIP service that I’ve been maintaining for quite a few years. Today marks the release of 1.0.0 for GeoIP -- this new release brings quite a few changes, features, and deprecations that I will outline below. The backend and frontend have been completely rewritten, in addition to the migration from Vue 2 to Vue 3, and moving from SemanticUI to NaiveUI.

Where It Started

I originally wrote GeoIP more than 8 years ago, utilizing Flask & Jinja2, in Python, and the frontend was of course the tried-and-true Bootstrap UI toolkit. Take a look at the below screenshot from 2016, grabbed from archive.org. I almost forgot what it looked like, it’s been so long!:

Starting in 2017, I rewrote the project in Go, with a Vue 2 frontend using SemanticUI. That effort evolved the project to be more performant, as well as more intuitive to run/use, and include a more feature rich API.

Looking into the Future

New Features

Dark mode!

I have finally decided to bite the bullet and implement dark mode across the frontend, which will be based on your system preferences. At this time, I do not plan to support a dark mode toggle. This should improve the experience, even for those who use browser extensions to force dark mode -- previously this didn’t work correctly, because the embedded map would still be rendered in a light-oriented format.

Improved Bulk Lookup

I’ve drastically improved the experience of bulk lookup support. This includes the addition of a new API endpoint that lets you search up to 25 addresses at a time, which should cut the time it takes to complete bulk lookups to 1/25th what it previously was. In addition to this, you can now also view, as well as download the returned results as a JSON object, for use with any kind of external processing that you may want to do.

ASN Lookups

GeoIP now also does ASN lookups for addresses, which returns the Autonomous System Name organization information, and the full associated network block for that given ASN.

Accuracy Radius Calculations

The new embedded maps should now also render accuracy radiuses, in addition to dynamically adjusting zoom depending on the level of accuracy, to represent the assumption of how accurate the results could be. Previously, I used markers for points on the map with a static zoom, however this can be very misleading as far as accuracy is concerned.

Improved API Documentation

I have switched the API documentation to utilize OpenAPI in the latest release. This will make it easier for others to generate API clients for GeoIP, as well as to better understand what requests, and thus responses should look like. The new API documentation page is now a fully-embedded OpenAPI viewer, with support for view things like:

  • Path parameters (e.g. /api/v2/lookup/{address}).
  • Query parameters (e.g. ?pretty=true).
  • Expected response codes, headers, and body schema.
  • The ability to invoke the API via the documentation to see actual responses.
  • And more!

Breaking Changes

With these new features comes some breaking changes for users utilizing the API. I’ve outlined some of these below.

API Changes

I’ve moved to a new API version, and this new version should be used moving forward. To summarize the changes between the old version, and the new v2 version:

  • Deprecation of /api/<address>[/{filters}] and /api/ping
  • Removing support for URL filters. This will actually be no longer functional when I push this version to production, and I’ve done this due to these endpoints almost never being used.
  • A new dedicated endpoint specifically for singular lookups, to support an endpoint for bulk lookups. The old endpoint will still function for a period of time, before it is completely removed.
  • New endpoints don’t return errors inline, they return a new custom error object response, with appropriate HTTP status codes (e.g. 400, 404, 500).
  • X-Cache and X-Maxmind-* headers have been removed. These also weren’t commonly used, and weren’t structured in a way to support multiple database types. If you’d like to obtain information about which databases and database versions are being used, check out the new metadata endpoint in the documentation.
  • New GeoIP result objects include some field changes:
    • query has been added to more easily determine what the exact input was for the request.
    • asn has been added, which is the ASN of the requested address, in the format AS<number>.
    • asn_org is the organization key that owns the associated network block/ASN.
    • network has been added, and should be set to the ASN CIDR range.
    • accuracy_radius_km has been added to help understand what the accuracy of the latitude and longitude in the response is.
    • ip_type has been added, which represents an integer of 4 or 6 for IPv4 and IPv6.
    • error is no longer available, as this is handled via a new error object (see above).
  • New disable_host_lookup query parameter for both single-lookup and bulk-lookup endpoints, which allows much faster lookup times (this is enabled by default for bulk lookups).
  • You can now fetch the OpenAPI spec directly from the frontend for use with code generation.

CLI Flag and Environment Variable changes

I have pulled most of the standard CLI functionality into a common library, and as such, some changes were necessary as far as CLI flags and environment variables go. In addition, the support of multiple database types means the database related flags have been changed. This will help ensure new databases being added in the future should require less changes.

For a full list of new flags and environment variables, take a look at the readme.

Ok, what else?

Internally, various improvements have been made. To highlight just a few:

Frontend

  • Moved all logic from Javascript to Typescript. This will ensure there are less potential bugs due to my shitty code. :slightly_smiling_face:
  • Vue 2 to Vue 3. This has opened Pandora's box for simplification, further separation of concerns, and various other maintainability improvements.
  • New and well-maintained UI library, NaiveUI.
  • Generated Typescript OpenAPI client, utilizing fetch, rather than Axios, which means less dependencies.
  • Built and bundled with Vite, improving maintainability, speed of development, and making more compact bundles that are properly tree-shaken.
  • State has moved from Vuex, to Pinia. Although this application doesn’t have much state, the support of Typescript, and the simplifications over Vuex have been a welcome addition.
  • Support for an embedded OpenAPI doc using RapiDoc. Surprisingly easy to use, and much better than the rather ugly SwaggerUI that I’ve seen being used a million times.
  • Now E2E tested with Playwright. It was insanely easy to use this framework for E2E testing, compared to tools like Cypress, especially with some of the core features including auto-waiting, await/async support, interactive test code generator, and more.

Special thanks to Anthony Fu (@) for his awesome work in the Vue ecosystem, including Vite plugins, UnoCSS, his work on VueUse, and more. His work alone was a huge driver for the simplicity in the migration from Vue 2 to Vue 3, and improved DX.

Backend

Some of this work has been done over the past 6-12mo, but nonetheless:

  • Much of the logic has been split up, making it more maintainable (and potentially testable, if I ever write any of those).
  • Now utilizing structured logs to make parsing and forwarding to external systems easier.
  • Now utilizing lrstanley/chix, which has quite a few bundled HTTP middleware that are meant to be paired with go-chi, including a more secure solution to obtain RealIP’s, improved panic recovery middleware, more intuitive solution for embedding static assets into binaries, and more.
  • Now utilizing lrstanley/clix, which is a wrapper around go-flags that adds support for generating markdown docs, pre-bundled base flags (like --version, --debug, --log.<opt>, auto-loading dot-env files, and more.
  • Multiple layers of caching now occur for GeoIP lookups, ARN lookups, hostname resolution, as well as reverse DNS resolution.
  • Support for easily extending the types of Maxmind databases that can be used.

Github Actions

I also wanted to shout out my experience with Github Actions. I’ve used many CI/CD solutions in the past, and Github Actions is definitely one of the best I’ve used. The ability to integrate with SCM repositories in such a cohesive manor, it has definitely scratched an itch that has been bothering me for some time. Just to summarize some of the cool things I’ve done across multiple projects over the past few months, and is shown with GeoIP:

  • Auto-labeling PR’s and issues with bug/improvement, size of PR’s, and supporting auto-running various tasks against PR’s to validate quality contributes.
  • Linting and testing both frontend as well as backend.
  • Code quality and security scanning, with auto-uploads to Githubs native security tools.
  • Auto-building all binaries, docker images, etc.
  • Initiating new releases, with auto-generated release notes, and updating readmes and similar with new version numbers.
  • And much more!

Conclusion & Final Thoughts

I have had lots of fun doing this rewrite, and I think the final result is a much more stable tool, with a fast/simple/clean user interface.

Test out the latest version of GeoIP at https://geoip.pw, and let me know your thoughts. Have an idea for a feature, or notice a bug? Feel free to submit an issue!

 

Made with by lrstanley