If you have been following my blog at all recently, you might notice that I have been developing a real passion for web-development. However, while most web-developers might wield JavaScript, or perhaps even Python, for their web-development ventures, I have been developing websites exclusively in Julia for sometime now. Not only have I found this to be a really awesome application of the Julia language, but I have found the various packages in the ecosystem for web-development to also be awesome!
I recently wrote an article where I put forth some arguments as to why I think Julia could actually become a relatively popular language for web-development. While I am not a web-developer, I do find elements of the domain are important to Data Science, and I think that need has propelled Julia into some real potential for more general-purpose web-development. If you would like to read this overview, it is available here:
Though the Julia web-development community is small, there is actually a rather robust ecosystem for developing the web in Julia! Many of these packages are also incredibly versatile and able to cover a range of different domains that have prominent web-development needs. As most developers might expect, when it comes to any package in any language, there are always design choices that are made which might benefit one application over another for the use of that particular package. Merely knowing Flask does not mean you do not need to know Django, and the same can be said for these Julia packages, as well. In order to get a better idea of what some of the web-development frameworks in Julia hope to accomplish and are capable of, I thought it might be apt to do a comparison between a few of them. As you might expect, this overview is going to include Genie.jl. In addition to Genie, I will also be comparing my project, Toolips.jl, because — of course this is the one I use the most — and lastly a micro-framework called Oxygen.jl.

Genie
The first web-development framework we will discuss in this article is probably the most obvious one. The Genie framework is the classic web-development framework for Julia. This alone brings a host of advantages for Genie over other projects, simply due to its maturity. However, maturity is not the only reason one might want to go with Genie in many cases. The first thing we need to understand about Genie is that Genie is an ecosystem, not just a package. That being said, this ecosystem includes some other awesome packages such as Searchlight — an ORM adapter for Julia (it turns your data-bases into Julia types), and Stipple.jl — a Genie.jl package for interactivity.
Getting started with your first Genie project is about as simple as other web-development frameworks. One thing that certainly is unique, and I would say is somewhat of a negative for Genie, is that there are a lot of files and things can get very confusing very quick with all of the different folders and intricacies in a Genie project. Not to worry — none of this is all that complicated, however in my experience I did find the general structure of a Genie project to be a negative when compared to the other options we will talk about in this article. This structure is also, like some other options, able to be ran entirely through the REPL. That being said, this structure is also optional.
getting started
Getting started with Genie is relatively easy, the first step is to create a new project. One thing that Genie users enjoy over users of every other package is the in-depth documentation offered! Another difference is how arguments are handled, with a default value being provided for the argument if it does not exist. With other options, you might need to work around these things and check if the parameter is there. Today we will be making a small addition endpoint for each framework. Let's start our new Genie project:
julia> Genie.Generator.newapp_webservice("GenieApp")
[ Info: Done! New app created at /home/emmac/dev/GenieApp
[ Info: Changing active directory to /home/emmac/dev/GenieApp
/tmp/jl_PwdWTh/GenieApp/Project.tomlCreating a project does also take a decent bit longer than some of the other options on this list, but this is likely attributed to Genie utilizing more than one package environment — and really generating a lot more files with a more unique bootstrap. However, taking a look at tree we see exactly what I was referring to earlier:
shell> tree .
.
├── bin
│ ├── repl
│ ├── repl.bat
│ ├── runtask
│ ├── runtask.bat
│ ├── server
│ └── server.bat
├── bootstrap.jl
├── config
│ ├── env
│ │ ├── dev.jl
│ │ ├── global.jl
│ │ ├── prod.jl
│ │ └── test.jl
│ ├── initializers
│ │ ├── autoload.jl
│ │ ├── converters.jl
│ │ ├── inflector.jl
│ │ ├── logging.jl
│ │ └── ssl.jl
│ └── secrets.jl
├── Manifest.toml
├── Project.toml
├── public
│ ├── css
│ │ └── genie
│ │ ├── bootstrap.min.css
│ │ ├── prism.cssJeeze Louise
The file we need is all the way down here:
├── routes.jl
├── src
│ └── GenieApp.jl
└── test
├── Manifest.toml
├── Project.toml
└── runtests.jlThis will of course be routes.jl .
using Genie.Router
route("/") do
serve_static_file("welcome.html")
endNow let's change this to take two numbers as arguments and then produce the summation as a return.
route("/") do
x = string(params(:x, 5))
y = string(params(:y, 2))
x = parse(Int64, x)
y = parse(Int64, y)
return(x + y)
endNow let's try this app out:

If you would like to learn more about the Genie framework, and using Genie, here is a link to the latest Genie documentation:
Another great resource would be this book on Genie, which was written by Ivo Balbaert (a prominent expert in data and application programming interfaces to work with that data) and Essenciary (creator of Genie):
One thing I really like about this book is that it not only includes web-development, but also a whole lot more. Much of the book focuses on web-development from a data-driven perspective, which is made even more awesome by the SearchLight ORM. As a Data Scientist, I found the read interesting and the information presented to be quite valuable!
Toolips
Of course, I had to include my own web-development framework, Toolips. While the Genie package might be more centralized, with less extensions and a lot more capabilities out of the box, the focus with this web-development framework is to keep things lightweight and add features with extensions. This is another great option, and really tends to capitalize on the areas where Genie is worse off. Probably the biggest difference between Genie and Toolips at the core is that everything in Toolips has to be served through Julia. In Genie's case, you can actually use Gzip to serve static files, and this includes resources such as images — and this can be substantially better for both performance and computation usage.
However, there certainly are some things you can do with Toolips that cannot be done with Genie, and unless my goal is to fill out a template or simply create a file server, Toolips is the choice I usually go with. However, compared to the enormous amount of resources for Genie and the venerability of Genie due to it being around for a while, this is certainly not the case for everyone. I also happen to be a lot more familiar with Toolips, because I wrote it, and in a lot of ways the best web-development framework not only depends on the application, but also which one you like and which one you have experience in. One thing I will definitively say that I personally prefer to Genie with Toolips projects, and the same can be said for the next module we will discuss, is the project structure and project directories. This, to me, is akin to the comparison between the project structure of Flask and Django — it does not mean one or the other is better, they are simply wildly different and in some ways created to do different things.
getting started
To start a toolips project, we use the new_app and new_webapp methods respectively. new_app will create a new application with only a Logger , and no dependencies. new_webapp will create a new application with a Logger , the Files extension, and finally the ToolipsSession module — which adds fullstack interactivity to toolips with the on method. Both of these take a single String as a positional argument, and this String is the name of our project.
julia> using Toolips; Toolips.new_app("ToolipsApp")
Generating project ToolipsApp:
ToolipsApp/Project.toml
ToolipsApp/src/ToolipsApp.jlThis will create a new Julia module with the addition of dev.jl and prod.jl . These are " environment files" which can be used to either load environmental variables or define different variables. They both utilize the same Pkg environment, but they are able to contain different port configurations — of course the environment detail could also be changed of course.
shell> cd ToolipsApp
/home/emmac/dev/ToolipsApp
shell> tree .
.
├── dev.jl
├── logs
│ └── log.txt
├── Manifest.toml
├── prod.jl
├── Project.toml
└── src
└── ToolipsApp.jl
2 directories, 6 filesUnlike in the case of Genie, Toolips projects are packaged in a very familiar way. We can start our server by including dev.jl . Before we do that though, let's get a basic idea of the syntax and handling arguments. Routes in Toolips can be created by using the route method, or by defining a function — both examples are demonstrated in our new source file:
"""
home(c::Connection) -> _
--------------------
The home function is served as a route inside of your server by default. To
change this, view the start method below.
"""
function home(c::Connection)
write!(c, p("helloworld", text = "hello world!"))
end
fourofour = route("404") do c
write!(c, p("404message", text = "404, not found!"))
endLet's make our x and y addition app:
"""
home(c::Connection) -> _
--------------------
The home function is served as a route inside of your server by default. To
change this, view the start method below.
"""
function home(c::Connection)
args = getargs(c)
if :x in keys(args) && :y in keys(args)
write!(c, parse(Int64, args[:x]) + parse(Int64, args[:y]))
return
end
write!(c, "Please provide x and y arguments!")
endAs of right now, we will need to parse these into Int64s, as in the other solutions, however I have plans to change this eventually with updates to ParseNotEval. It should also be noted how we got these arguments, using the getargs method. One thing I actually would like to take from Genie is the ability to provide defaults for this method, as it would be somewhat better to be able to do this without a conditional in every case! What I could do is tie this to the getarg method by creating a new dispatch for a Connection, Symbol, and Any. Now we will include dev.jl to start our server:
include("dev.jl")
Activating project at `~/dev/ToolipsApp`
[ Info: Precompiling ToolipsApp [e3262f1b-59e9-4387-8656-f3666099fe6d]
[ Info: Listening on: 127.0.0.1:8000, thread id: 1
37;1m[2023:01:26:21:20]: 🌷 toolips> server started at: http://127.0.0.1:8000
WebServer
hosted at: http://127.0.0.1:8000
status: 4
routes
/
404
extensions
Logger

While Toolips is certainly capable of producing APIs that work really well quite easily, this is not necessarily the focus of the project. While this example is not necessarily the greatest use of Toolips, it does do well to demonstrate the basics of the package. If you would like a more in-depth look at this project, I am currently working on a long-form set of video tutorials for the framework! If you would like to view them, here is the first article in a series of Toolips tutorials I have been working on:
Oxygen
The last web-development framework I would like to compare today is Oxygen.jl. Whereas we can probably say that Toolips and Genie are relatively similar options that can serve different purposes, Oxygen is a much different option that serves a more specific purpose. One substantial difference with Oxygen is that there is very little project structure that is generally used. This might illuminate that there are definitely different use-cases for Oxygen than Toolips or Genie.
Oxygen also has some rather unique syntax. All of these different projects seem to be dramatically different flavors of web-development, which is definitely a good thing, not a bad thing. Whereas Toolips takes a more Element approach, Genie takes a more data-oriented approach for web-apps, and finally Oxygen takes a very straightforward API-oriented approach for endpoints. That being said, it certainly is interesting to see all of these different styles of web-development and further compare and contrast their abilities.
getting started
Oxygen is a bit weird when compared to other traditional web-development frameworks that we have in industry. The syntax involves utilizing the @get and @post macros. To start a project with this framework, we are simply going to create a regular Julia module.
(@v1.7) pkg> generate OxygenApp
Generating project OxygenApp:
OxygenApp/Project.toml
OxygenApp/src/OxygenApp.jl
(@v1.7) pkg> activate OxygenApp
Activating project at `~/dev/OxygenApp`
(OxygenApp) pkg> add Oxygen
Updating registry at `~/.julia/registries/General.toml`
Resolving package versions...It should be noted that this is not unique to Oxygen, however this is the only structure of Oxygen — there is no default project method, as is the case with both Toolips and Genie. This is not such a bad thing, but it is definitely noteworthy. That being said, features that both Genie and Toolips have like active route development are not available in Oxygen, which is one shortcoming to this framework. However, for minimalist — and endpoint — applications I could certainly see how this could be apt. Once again, let us create our addition app:
using Oxygen
@get "/" function()
"hello world!"
endSo rather than using closures with a do statement, Oxygen utilizes macros — taking a String and a quoted function as an argument. This is certainly an interesting way of doing this! Where things get even cooler is when we add {} and arguments to that String . This quickly becomes a very easy way to process arguments, furthermore if we utilize a function call our arguments come through parsed and ready to go! Check out this sick one-liner that creates our endpoint from before:
using Oxygen
@get "/{x}/{y}" add(req, x::Int64, y::Int64) = x + y
serve()That's pretty simple, right?! Even cooler is that Oxygen comes with an awesome dashboard specifically for working with endpoints. We can utilize this dashboard to query our endpoints and test returns in the coolest way possible!

That is definitely really awesome, and could definitely come in handy for Oxygen's exact use-case! If you would like to learn the basics of the Oxygen framework, I recently published an article where I took my own dive into this awesome web-development framework — you may read it here:
closing thoughts
Julian web-development is not the same as Pythonic web-development. Many of these web-development frameworks are a lot more painless to use and a lot more feature-package, at least in my subjective experience. From the maturity and usability of Genie, the versatility and interactivity of Toolips, all the way to the fantastic end-pointing usage of Oxygen — there are a lot of diverse and powerful options when it comes to web-development in Julia! That being said, I certainly stand by what I said when I mentioned that Julia could be a potential language for web-developers to keep an eye on — which is very exciting to me!
I would like to thank Essenciary and Ndortega for these awesome contributions to the Julia web-development ecosystem, please show these projects some love! Oxygen in particular really had me surprised, as there are few other web-frameworks that I could cite as more convenient for simple end-pointing. Furthered, it is really cool to take a comprehensive look at these while comparing them to, of course, my own web-development framework. There are a few different things that I see from the other options I actually want to take back to my own project.
Notably, it would be cool to have the same type of function expressions — I could add some kind of change to the Toolips.route method to make it possible to route arguments into a function automatically, which would certainly make these kinds of things more convenient! Furthermore, it would be really interesting to add the default argument feature to Toolips as it is in Genie. I am sorry to be lifting all of your guys' ideas, but quit coming up with such good ones! Thank you for reading everyone, happy web-developing!