← Back to list

Finishing My API Routing System With Toolips

Continuing my API routing project by adding a Parent Routing system.

Emma Boudreau in chifi · 2026-06-16 05:29 · 55 claps · 4.8 min read paywalled
#programming #julia #web-development #software-development #technology
Open on Medium ↗
Wiki topics: 💻 · Programming 🌐 · Web Development

Finishing My API Routing System With Toolips

Continuing my API routing project by adding a Parent Routing system.

api and parent routing

In my last article, I created a new Toolips extension that streamlines the creation of APIs. The result is pretty convenient for building APIs, turning code like this:

addition = route("/") do c::AbstractConnection
    args = get_args(c)
    x = parse(Int64, args[:x])
    y = parse(Int64, args[:y])
    write!(c, x + y)
end

Into a concise and convenient form:

adder_r = api_route((c, x::Int64, y::Int64) -> write!(c, x + y), GET, "/add")

The route goes from taking several steps to becoming a one-liner. Those are some pretty awesome results. This article acts as a part two to the article where we created this system, so if you would like to read it for more context here is a link:

[embed]Modding My Web Framework With An API Router multiple dispatch routingmedium.com

Today I wanted to take this project a bit further by adding routes that combine POST and GET requests into the same target. I will also be further expanding on this project further by building another project to help implement this system into any website.

combined API routes

In order to make routes that easily take a GET and POST request in the same route, we will make a simple structure that holds both routes and the actual path for our page.

struct CombinedAPIRoute <: AbstractAPIRoute
    path::String
    routes::Pair{APIRoute{:get}, APIRoute{:post}}
end

And I will add another api_router Method to make this route from two routes.

api_route(r1::APIRoute{:get}, r2::APIRoute{:post}) = begin
    path = r1.path
    combined = CombinedAPIRoute(path, r1 => r2)
    r1.path = ""
    r2.path = ""
    combined::CombinedAPIRoute
end

api_route(r1::APIRoute{:post}, r2::APIRoute{:get}) = api_route(r2, r1)

Now we will make a simple route! dispatch that calls different routes depending on the Method . I will use get_method , a handy function from Toolips that takes our Connection .

function route!(c::AbstractConnection, route::CombinedAPIRoute)
    if get_method(c) == "POST"
        route!(c, route.routes[2])
    else
        route!(c, route.routes[1])
    end
end

A full list of getters, like get_method are available in the [Toolips reference](https://chifidocs.com/toolips/Toolips/reference). With those small functions out of the way our new route is fully implemented.

struct CombinedAPIRoute <: AbstractAPIRoute
    path::String
    routes::Pair{APIRoute{:get}, APIRoute{:post}}
end

function route!(c::AbstractConnection, route::CombinedAPIRoute)
    if get_method(c) == "POST"
        route!(c, route.routes[2])
    else
        route!(c, route.routes[1])
    end
end

api_route(r1::APIRoute{:get}, r2::APIRoute{:post}) = begin
    path = r1.path
    combined = CombinedAPIRoute(path, r1 => r2)
    r1.path = ""
    r2.path = ""
    combined::CombinedAPIRoute
end

api_route(r1::APIRoute{:post}, r2::APIRoute{:get}) = api_route(r2, r1)

Now we will finish the project by creating a new combined route inside of our test API from the last article. As part of building this project originally, we were using this post_test2 function to test the automatic translation of incoming JSON to Julia dictionaries.

post_test2 = (c::AbstractConnection, info::Dict) -> begin
    write!(c, "information has been sent: $(info["x"])")
end

We will be reusing this function, only now our actual route will be /api/age , and we will combine it with our GET API for getting the age of people in our dictionary.

post2 = api_route(post_test2, POST, "/api/age")

age_route = (c, username) -> begin
    if haskey(users, username)
        write!(c, users[username][:age])
    else
        write!(c, "user $username not found!")
    end
end

age = api_route(age_route, GET, "/api/age")

Now to create our combined route:

post_and_age = api_route(age, post2)

We finish by making sure to export the new route.

export post_and_age

Now let’s try it out! First we will post:

julia> start!(ToolipsAPIRouter.APITestServer)
[ Info: Listening on: 127.0.0.1:8000, thread id: 1
   pid              process type                                  name active
  –––– ––––––––––––––––––––––––– ––––––––––––––––––––––––––––––––––––– ––––––
  2303 ParametricProcesses.Async ToolipsAPIRouter.APITestServer router   true

julia> Toolips.post("http://127.0.0.1:8000/api/age", "{\"x\" : 5}")
"information has been sent: 5"

And then get:

Perfect!

Implementing parent routes

Parent routing is a different idea entirely, and isn’t specifically reserved for building APIs. The goal of this project is for multiple routers to work at the same time by allowing routes to be organized according to their router type. This is loosely connected to our new API-building system,

We will be holding our routes under a ParentRoute type, then we will create a new ParentRouter for a website full of ParentRoutes . Here is my simple ParentRouter type:

struct ParentRoute{T <: Toolips.AbstractHTTPRoute} <: Toolips.AbstractHTTPRoute
    path::String
    page::Function
    pages::Vector{T}
end

Now I will add a route! function for a Vector of Parent routes.

function route!(c::AbstractConnection, routes::Vector{<:AbstractParentRoute})

end

If the target contains a single slash we will route it immediately to that page, but if it is larger we will prepare the target before calling the head and making the same call

function route!(c::AbstractConnection, routes::Vector{<:AbstractParentRoute})
    target = get_route(c)
    n_slashes = count('/', target)
    target = split(target, "?")[1]
    if n_slashes < 1
        if ~(target in routes)
            route!(c, routes["404"])
            return
        end
        route!(c, routes[target], target)
    else
        split_target = split(target, "/")

    end
    nothing::Nothing
end
function route!(c::AbstractConnection, routes::Vector{<:AbstractParentRoute})
    target = get_route(c)
    n_slashes = count('/', target)
    target = split(target, "?")[1]
    if n_slashes < 1
        if ~(target in routes)
            route!(c, routes["404"])
            return
        end
        route!(c, routes[target], target)
    else
        split_target = split(target, "/")
        head = "/" * split_target[2]
        if ~(head in routes)
            route!(c, routes["404"])
            return
        end
        route!(c, routes[head], target)
    end
    nothing::Nothing
end

Finally, we route! the individual ParentRoutes with another route! Method . This one is much more simple.

function route!(c::AbstractConnection, route::ParentRoute, target::AbstractString = route.path)
    if target == route.path
        route.page(c)
    else
        route!(c, route.pages)
    end
    nothing::Nothing
end

Finally, i’ll make a function for building our ParentRoute .

function parent_route(f::Function, path::String, provided_routes::Toolips.AbstractHTTPRoute ...)
    routes = [provided_routes ...]
    if length(routes) == 0
        routes = Toolips.AbstractHTTPRoute[]
    end
    ParentRoute{typeof(routes).parameters[1]}(path, f, routes)
end

Now to test our addition in our test Module by aggregating our routes:

API_route = ToolipsAPIRouter.parent_route("/api", friends, adder_r, post_and_age) do c::AbstractConnection
    write!(c, "welcome to the API")
end

main = ToolipsAPIRouter.parent_route("/") do c::AbstractConnection
    write!(c, "my main website")
end

err = ToolipsAPIRouter.parent_route(Toolips.default_404.page, "404")
export err, API_route, main

A perfect execution!

closing thoughts

I am glad I was able to get through this project today and I am super happy with the results. Not only did we build a super convenient system for managing APIs, but we also created a unique parent system that really expands the capabilities of Toolips . With a bit more advanced work, this could effectively provide a future wherein a proxy and a web-server are running within the same server.

Both of these are awesome additions, and I am certainly considering releasing some packages to share these — might as well, right? I already wrote it. To be a package, though, also means to document, to test, to maintain, to eventually provide on ChifiDocs . It is always much more work than it seems. We will see what I or anyone else wants to do with the code, if anything. Maybe this will all pay off whenever I need to build complicated APIs in the future. Thank you all for reading, I will close out this piece with a link to the source:

[embed]Random_Code/ToolipsAPIRouter at main · emmaccode/Random_Code Just a bunch of random blobs. Contribute to emmaccode/Random_Code development by creating an account on GitHub.github.com


메타데이터
post_id
8e733daaba94
slug
finishing-my-api-routing-system-with-toolips-8e733daaba94
url
https://medium.com/chifi-media/finishing-my-api-routing-system-with-toolips-8e733daaba94
canonical_url
https://medium.com/chifi-media/finishing-my-api-routing-system-with-toolips-8e733daaba94
author_url
https://medium.com/@emmaccode
status
ok
fetched_at
2026-06-24 13:29:15