[ANN] Oxygen.jl v1.1.6 (Cron Scheduling)

Hello, everyone!

Today, I’m here to introduce the latest update to the Oxygen.jl. It’s been almost a year since my last post, and I’ve been steadily working on incorporating your feedback and implementing new features to enhance the development experience.

In this post, I’ll specifically discuss the most recent addition to the framework: Cron Scheduling. This feature allows you to schedule recurring tasks effortlessly for both endpoints and standalone functions, using the same Cron expressions we’re all familiar with.

Additionally, I’ll briefly cover the most significant updates that have been introduced since last time. The latest version offers a range of improvements designed to help you build more robust and versatile applications.


  • JSON serialization & deserialization (customizable):
    • This package has always supported automatic JSON serialization and deserialization for request and response data. But now you can also define your own serializers and deserializers by using middleware functions.


  • Cron Scheduling:

    • Added support for Cron scheduling, which makes it easy to schedule recurring tasks to run at specific intervals for both endpoints and functions.
  • Repeat Tasks:

    • Added the ability to create tasks that repeat at specified intervals, making it easy to schedule and manage recurring tasks within your application.
  • Middleware:

    • Middleware functions enable the creation of custom workflows to intercept incoming requests and outgoing responses in a specific order. They can be set at the application, router, and route layers using the middleware keyword argument, combining and executing the middleware defined in each layer. The execution order is always: application, router, and then route.
  • Auto-generated Swagger Documentation:

    • Automatically generates Swagger documentation for each registered route, making it easier to maintain and share API documentation with others. You can view, modify, and extend the autogenerated schema.
  • Route Tagging:

    • Added route tagging, allowing you to categorize routes and easily manage or filter them based on assigned tags. This is used when autogenerating the Swagger documentation to group together similar endpoints.

Cron Scheduling

Oxygen comes with a built-in cron scheduling system that allows you to call endpoints and functions automatically when the cron expression matches the current time.

When a job is scheduled, a new task is created and runs in the background. Each task uses its given cron expression and the current time to determine how long it needs to sleep before it can execute.

The cron parser in Oxygen is based on the same specifications as the one used in Spring. You can find more information about this on the Spring Cron Expressions page.

Cron Expression Syntax

The following is a breakdown of what each parameter in our cron expression represents. While our specification closely resembles the one defined by Spring, it’s not an exact 1-to-1 match.

The string has six single space-separated time and date fields:

 ┌───────────── second (0-59)
 │ ┌───────────── minute (0 - 59)
 │ │ ┌───────────── hour (0 - 23)
 │ │ │ ┌───────────── day of the month (1 - 31)
 │ │ │ │ ┌───────────── month (1 - 12) (or JAN-DEC)
 │ │ │ │ │ ┌───────────── day of the week (1 - 7)
 │ │ │ │ │ │          (Monday is 1, Tue is 2... and Sunday is 7)
 │ │ │ │ │ │
 * * * * * *

Partial expressions are also supported, which means that subsequent expressions can be left out (they are defaulted to ‘*’).

# In this example we see only the `seconds` part of the expression is defined. 
# This means that all following expressions are automatically defaulted to '*' expressions
@cron "*/2" function()
    println("runs every 2 seconds")

Scheduling Endpoints

The router() function has a keyword argument called cron, which accepts a cron expression that determines when an endpoint is called. Just like the other keyword arguments, it can be reused by endpoints that share routers or be overridden by inherited endpoints.

# execute at 8, 9 and 10 o'clock of every day.
@get router("/cron-example", cron="0 0 8-10 * * *") function(req)

# execute this endpoint every 5 seconds (whenever current_seconds % 5 == 0)
every5 = router("/cron", cron="*/5")

# this endpoint inherits the cron expression
@get every5("/first") function(req)

# Now this endpoint executes every 2 seconds ( whenever current_seconds % 2 == 0 ) instead of every 5
@get every5("/second", cron="*/2") function(req)

Scheduling Functions

In addition to scheduling endpoints, you can also use the new @cron macro to schedule standalone functions. This is useful if you want to run code at specific times without making it visible or callable in the API.

@cron "*/2" function()
    println("runs every 2 seconds")

@cron "0 0/30 8-10 * * *" function()
  println("runs at 8:00, 8:30, 9:00, 9:30, 10:00 and 10:30 every day")

Starting & Stopping Cron Jobs

When you run serve() or serveparallel(), all registered cron jobs are automatically started. If the server is stopped or killed, all running jobs will also be terminated. You can stop the server and all repeat tasks and cron jobs by calling the terminate() function or manually killing the server with ctrl+C.

In addition, Oxygen provides utility functions to manually start and stop cron jobs: startcronjobs() and stopcronjobs(). These functions can be used outside of a web server as well.


As we wrap up, I want to extend a sincere thank you to each and every one of you for your incredible support, insightful feedback, and active involvement with the project. Your input has played a crucial role in the growth and development of the package, and I look forward to our ongoing collaboration as we team up to make Oxygen.jl even better!


Interesting. I came here from a search for jenkins alternatives with cron. It makes me want to write a simple jenkins alternative in julia with Oxygen.jl and Dagger.jl.

Hi there,

Thanks for the fantastic updates and work!

I’ve been using Oxygen for quite a while now, and one thing I run into is that at the very bottom of the serverside handling stack there’s a handler that whenever an error happens throws a 500 and “internal server error”, rather than the default behavior of HTTP.jl which returns the actual error message as well.

I haven’t found an easy way to disable this one or change its behavior, do you have any suggestions? Or could this behavior be changed?


1 Like

Hi @louisponet,

Thanks for being THE pioneer of Oxygen by using it in your own package!

This was done because I didn’t want any errors from a request handler to crash the server and/or leak crash details to the client. Now that being said, we can easily customize that behavior by adding some code to toggle the default error handling.


I’ve added a new keyword argument to do just that which is available under the lastest version: 1.1.8. Here’s an example of it in use: custom error handling demo

Let me know if you have any feedback. I’ve also opened up a discussion on my GitHub to discuss this specific feature.

Feel free to join our small discord server to talk more about features, support, and issues: Oxygen.jl discord


Oh this is fantastic, that’s indeed exactly what I was looking for! Awesome stuff, and fast too :smiley:

1 Like