Implement a REST server in Julia?

I would like to build a REST API for a Julia code. I am currently evaluating whether I use also Julia to implement the REST server or if I use another language (e.g. Python) for the server part.

These are the package that I have seen so far:

  • HTTP.Servers (from HTTP.jl)
  • HttpServer.jl
  • Genie.jl
  • Mux.jl

Do you have any recommendations?

Personally, I like the style of the python library Flask. The julia code can take a long-time to run (hours). So the client will need to check periodically if the calculation is done. But it is important that the calculation should not block any new request.

Thanks for your input!

5 Likes

I’m in the same boat. I’m currently setting up demos using the new HTTP.jl but it’s slow going (there’s not a lot of high-level documentation for it at the moment). In the short term it’s probably easier to build the API in Flask or similar and spawn Julia worker processes.

If you just need a couple of endpoints just to expose computations to the outer world, I’d go with HttpServer.jl - it’s very basic and low-level, but worked well for me a couple of times. If you need more complex routing, Mux.jl should a better fit. Genie.jl is more like Django in Python world: it’s server + ORM + templates, etc., for me it turned to be an overkill. No idea what HTTP.Server is about, so you’d need to figure it out yourself.

I recently updated Pages.jl to use the new / improved HTTP.jl. It might be something to consider for APIs (it’s what I made it for) and I’m happy to help out if you have questions.

1 Like

We use JuliaWebAPI quite extensively to build REST servers. It is built on top of HTTPServer and Mux, and makes it easy to expose any julia function via a JSON based interface. For example, all of Juliabox’s internal APIs are built using this package. It is quite flexible, and extensible to other transports and message formats, if you need something other than HTTP and JSON respectively.

5 Likes

https://github.com/amellnik/Joseki.jl is an example of how you can make simple APIs with just HTTP.jl. In your case it’s probably easier to make the API in whatever to write the jobs to redis or similar and report on progress, and separately have a julia worker that processes them.

1 Like

Although Mux.jl works fine for my latest project it seems like overkill since all I have to do is serve a static web page and respond to simple POST requests. So just yesterday I was wishing for more examples of doing simple stuff with HTTP.jl. Apparently I just willed this into existence. :slight_smile:

Also, I just have to ask: kyu or dan?

1 Like

I peaked around 6 kyu on kgs, and while I haven’t played seriously in a decade I got excited about the new AlphaGo joseki. I’ll add in an example of serving static content soon (although in my use cases I would always do that separately).

amellnik: thank you very much for sharing the code example which is very helpful to me!

I will also have a look to Pages.jl and JuliaWebAPI. Thank you all for your insights.

What were your conclusions regarding implementating restful API s in Julia? I’m currently doing all of this in flask but Would love to switch to Julia.

take one more example for RESTful API in Bukdu.jl
https://github.com/wookay/Bukdu.jl/blob/master/examples/api/endpoint.jl

1 Like

Hi Mike, I ended up using HTTP.jl:
https://github.com/gher-ulg/DIVAnd-REST/blob/master/src/DIVAndREST.jl

HTTP.jl is quite flexible and already quite complete for my needs. The syntax/API is not as short as in Flask but the code overhead is acceptable. Maybe registering function could be simplified by using do functions (like sinatra Sinatra: README ) or macros.

Bukdu looks indeed quite nice and concise. Thank you for sharing it!

1 Like

Some personal experience of JSON processing in a request and generating JSON as a response:

Implementation with Mux:

using Mux
using JSON

@app test = (
  Mux.defaults,
  page(respond("<h1>Hello World!</h1>")),

  page("/user/:user", req -> "<h1>Hello, $(req[:params][:user])!</h1>"),

  route("/resource/process", req -> begin
    obj = JSON.parse(String(req[:data]))

    @show obj

    Dict(:body => String(JSON.json(obj)),
         :headers => Dict("Content-Type" => "application/json")
         )
  end),

  Mux.notfound())

serve(test, 8080)

Base.JLOptions().isinteractive == 0 && wait()

Implementation with Bukdu:

using Bukdu
using JSON

struct WelcomeController <: ApplicationController
    conn::Conn
end

function index(c::WelcomeController)
    render(JSON, "Hello World")
end

function process_resource(c::WelcomeController)
    json = JSON.parse(String(c.conn.request.body))

    @show json

    render(JSON, json)
end

routes() do
    get("/", WelcomeController, index)
    post("/resource/process", WelcomeController, process_resource)
end

Bukdu.start(8080)

Base.JLOptions().isinteractive == 0 && wait()
4 Likes

Hi @Alexander-Barth,

How did your implementation handle multiple requests? I’m implementing something similar at the moment with Genie but the calculation is only on the order of 1-5 minutes and I’ve noticed it will block further requests.

I don’t use the REST server anymore, but I remember to have the same problem. You might want to try with @async or Threads.@spawn. There is more discussion here:

In the end, I had one HTTP request to start the long calculation, and another HTTP request to check if it is completed.

I’ve been trying out Oxygen.jl GitHub - ndortega/Oxygen.jl: A breath of fresh air for programming web apps in Julia

There is multithreaded support out of the box: @async serveparallel(port=8080, host="127.0.0.1")

5 Likes

This is the correct implementation regardless of any asynchronous behavior. There may be elements between the client and server outside of your control that have relatively short timeouts that would disconnect a client waiting on a long computation.

1 Like

Indeed, Oxygen.jl handels this case very well ! And the interface looks very lightweight and pleasant to use.

(for reference here is my test code: a call to e.g. /long/1 does not block a call to /short/1 )

# julia is started with
# julia -t4

using Oxygen, Dates, HTTP

function long(a)
    t = Dates.now() + Dates.Minute(1)

    while (t > Dates.now())
        a = a+1
    end
    return a
end

@get "/long/{a}" function(req::HTTP.Request,a::Int)
    @info "long"
    return Dict("value" => long(a))
end

@get "/short/{a}" function(req::HTTP.Request,a::Int)
    @info "short"
    return Dict("value" => a)
end

serveparallel()
6 Likes

Hi Alexander,

Thanks very much for getting back to me and including an example, really appreciate it. Oxygen indeed looks like quite a nice, simple solution - thanks.

1 Like