Getting Started
In this guide, you'll be building a simple greeting service that accepts a name via a REST API call and returns a welcome message. This will teach you the basics of working with Oryonix:
- How to write Python functions that run on Oryonix
- How to expose your functions as HTTP endpoints
- How to deploy your service
- How to test your deployed service
By the end, you'll have a working service that responds to requests like:
curl -X POST https://your-service-url/hello \
-H "Content-Type: application/json" \
-d '{"name": "Alice"}'
with responses like:
"Hello, Alice! Welcome to Oryonix."
Prerequisites
Before you start, you'll first want to be familiar with the following:
- How to program in Python, including a basic understanding of Python functions
- How to use Git and GitHub
- Basic knowledge of what a REST API is and what JSON is
You should also have the following available to you:
- A GitHub account
- Access to a GitHub organization that allows you to create repositories within it. You can create your own organization for free.
- A Python installation; version 3.12 or newer is required.
- A development environment that you're comfortable with. Any environment will do; Visual Studio Code is a popular one. You may also use a cloud-based development environment like GitHub Codespaces.
Let's get started!
Create Your Workspace
Oryonix applications live inside workspaces, which group related applications together, so we will first have to set up a workspace as well if one doesn't already exist.
If you already know what workspace you want to use, you can skip to the next section.
To create a workspace:
- Login to the Oryonix UI.
- On the homepage, click "Create Workspace" in the upper right corner.
- Give your workspace a name. This name should be limited to characters you might find in a domain name, typically letters, numbers, and the hyphen (
-) character. (.is not allowed.) - Click "Create". Oryonix will create the workspace for you.
Create Your Application
- Open your workspace in the Oryonix UI.
- Click "Create Application" in the upper right corner. This will open the application-creation screen, which will walk you through the steps needed.
- On Basic Information, enter the application name.
- Just like for workspaces, the application name should use only characters you'd find in a domain name—letters, numbers, and hyphens.
- Choose whichever region you think is most appropriate. For the tutorial, this doesn't matter, so choose a region close to you.
- On Network, choose the Private option.
- On Service Creation, we will create a service for your application.
-
Choose the GitHub Organization you want to use to store your service's code. Note that Oryonix will create a GitHub repository in the organization for you named
<app>-<service>, so make sure that name is available.infoIf you don't see your organization in the list, you may need to log out and log back in again.
-
Enter the name of the service. Similar to the application and workspace names, this name should be usable as a single part in a domain name.
-
Don't worry about linking services for now.
-
- Review the information on the Environment Setup page. Appications may have multiple environments for development, testing (or "staging"), and production.
- On the Rate Limiting page, leave everything set to its defaults.
- When you're ready, click Create.
Oryonix will then create the GitHub repository for your service, and create Production and Staging branches in that repository for the production and staging environments.
If your application cannot be created, there are a few things you'll want to check:
- Are you logged in to Oryonix using the right GitHub account?
- Did you grant Oryonix access to your GitHub organization?
- Do you have permission in your GitHub organization to create repositories?
Once you have resolved any permissions issues, you can retry with the same application and service name. If the repository for that service already exists in GitHub, Oryonix will try to reuse it--there is no need to delete and re-create the repository.
Clone the Service Repository
Next, you'll want to clone the GitHub repository that Oryonix created for you onto your local machine, and open it in your development environment. Alternatively, you may create a codespace in GitHub for your repository and use that.
git clone git@github.com:your-org-name/appname-servicename.git
The repository that Oryonix created for you is pre-populated with a very basic service. Let's take a look at the file structure:
hello_service/
├── .github/
│ └── workflows/
│ └── ci.yml
├── .gitignore
├── src/
│ └── main.py
├── spec/
│ └── service.yaml
└── README.md
- .github/workflows/ci.yml - Instructs GitHub on how to build your service and push changes to Oryonix for later deployment.
- .gitignore (may not exist) - Tells Git to ignore certain files in the repository, such as build artifacts which are automatically generated and uploaded to Oryonix.
- src/ - This folder contains all the Python code for your service. Right now, there is just a
main.pyfile which implements a basic service—we'll explore this next. - spec/service.yaml - The service spec file defines the capabilities of your service, and describes how your service expects to be communicated with.
- README.md - Describes what this repository is. Feel free to edit as you like.
Understanding the Python Code
First, let's explore the src/main.py file that Oryonix created for you. This file contains (or imports) all the Python code that is used to run your service.
All flow and action functions must be defined in main.py. They cannot be imported from other modules. We hope to remove this limitation soon. As a workaround, you can define thin wrappers in main.py that call functions defined in other modules.
This file might look slightly different in your repo, but the concepts are the same:
import onix
@onix.action
def greet(arg: str):
return f"Greetings, {arg}"
@onix.flow
def hello_world(arg: str):
ret1 = greet(arg="Hi, Guest1")
return (
f"hello_world invoked with arg: {arg}. "
f"Greet action returned: {ret1} "
)
We have defined two functions, greet() and hello_world(). Both of these are special kinds of Oryonix functions that differ slightly from normal Python functions.
greet() is an action function--we can tell because it's tagged with the @onix.action decorator. Action functions are the workhorses of Oryonix code--anything that has or observes effects in the real world must be done in an action function.
hello_world() is a flow function--we can tell because of the @onix.flow decorator. Flow functions are used to orchestrate other code, such as other flow functions or action functions. Flow functions may not, themselves, have side effects or do any actual work--they just call action functions that do. Only flow functions may be exposed as APIs.
You can read more about flow and action functions, and why they are special, in Flow and Action Functions. For now, it's enough to know that:
- Only flow functions can be exposed as APIs.
- Flow functions may call any other kind of function (flow functions, action functions, and regular Python functions).
- Action functions may ONLY call regular Python functions--they may not call other flow or action functions.
- All side effects must be done from within action functions.
Linking APIs to Python Code
Now let's look at the spec/service.yaml file. This file is an OpenAPI specification that describes all the APIs that your service implements. Yours may look slightly different, but the concepts are the same:
openapi: 3.0.0
info:
title: Hello World API
description: A simple API that accepts any request and responds.
version: 1.0.0
paths:
/hello:
post:
summary: Accepts any request and responds with the same structure
operationId: hello_world
requestBody:
description: Any valid JSON object
[...]
responses:
"200":
description: Successfully echoes the input
[...]
For now, the important thing to observe is that there is one path, /hello, that responds to post requests--this is an endpoint.
This endpoint has an operationId, in this case hello_world. The operationId is what links the spec file to your Python code--it is the name of the Python function to call. In this case, since the operationId is hello_world, when Oryonix receives a POST request for /hello, it will look for a flow function named hello_world and run that function to respond to the request.
All flow functions which are exposed as APIs must be defined in main.py. They cannot be imported from other modules. We hope to remove this limitation soon.
To work around this, you can define thin wrappers in main.py that call functions defined in other modules. This way, you can still organize your code into multiple files and modules, while keeping all flow and action function definitions in main.py. For example:
# src/main.py
import onix
from . import payment
@onix.flow
def process_payment(invoice_id: str, payment_id: str, amount: float):
return payment.process(invoice_id, payment_id, amount)
# src/payment.py
def process(invoice_id: str, payment_id: str, amount: float):
# Payment processing logic here
return "Payment processed"
Deploy the Sample Application
Now that we understand the service that Oryonix has created for us, let's launch our new application and try to call its API.
- In the Oryonix UI, go to the application you created.
- Choose which environment to deploy from the drop-down in the upper left. For now, we'll work with the Production environment.
- Oryonix will take a moment to check for deployment availability.
- Click "Deploy Now".
If Oryonix claims there are no deployments available, there could be a couple reasons:
- You've chosen the wrong environment.
- Your service's artifacts may not have been uploaded to Oryonix yet.
To check if your artifacts were uploaded, go to your service's repository on GitHub. Under the "Actions" tab, you will see a GitHub workflow has been created for you--this workflow automatically uploads artifacts to Oryonix whenever a new commit appears on the relevant branch. If this workflow failed, you'll have to debug it and fix the problem.
You can also upload artifacts manually from your development environment using the onix artifact build and onix artifact upload commands.
Get the URL for the Deployed Application
Oryonix creates a separate service URL for each environment of the application.
To get the URL of the application for a particular environment:
- Go to the required application page and select the environment.
- Click on the
Copy URLbutton on the right side.
Call an API of the Sample Application
Let's try calling the service you just deployed:
$ curl -X POST https://your-service-url/hello \
-H "Content-Type: application/json" \
-d '{"arg": "Alice"}'
"hello_world invoked with arg: Alice. Greet action returned: Greetings, Hi, Guest1"
Hm... that's not very friendly. We have to pass an arg instead of a name. Also, why are we greeting "Guest1"? And why are we greeting them twice? Let's see if we can do something about that.
Make a Change to Your Service
First, make sure you're working on the Production branch of GitHub (or
Staging, if you chose to use the staging environment instead):
git checkout Production
Let's change the hello_world and greet functions to take a name instead of an arg, and return a nicer greeting:
@onix.action
def greet(name: str):
return f"Hello, {name}!"
@onix.flow
def hello_world(name: str):
greeting = greet(name=name)
return f"{greeting} Welcome to Oryonix."
Flow and action functions must be invoked using keyword arguments. Positional arguments are not allowed.
This is because execution state is persistent within Oryonix, and using keyword arguments provides a more stable API for actions and flows to interact as they change over time. It also makes for a more reliable mapping to REST APIs, which are generally keyword-based.
We'll also need to update the spec/service.yaml file to provide a little more info about how to call our hello API:
/hello:
post:
summary: Accepts any request and responds with the same structure
operationId: hello_world
requestBody:
description: A JSON object with the user's name
required: true
content:
application/json:
schema:
type: object
properties:
name:
type: string
description: The user's name
[...]
Note how we replaced the schema definition for the request body with a more specific one that accepts only a JSON object with a name property.
Deploy Your Changes
Now that we've made our changes, we'll need to deploy them. This is really simple:
- Commit your changes to your git repository.
- Push them to the correct branch on GitHub (in our case,
Production, or whichever branch has the same name as your environment). - Wait for GitHub Actions to build and upload your service to Oryonix.
- Deploy your service from the Oryonix UI.
If Oryonix doesn't see that there is a deployment available, you'll want to follow the same steps as under Deploy the Sample Application above.
Generate an API Key
Oryonix requires all API calls to be authenticated. Before testing your changes, you'll need to create an API key and include it in your requests.
To create an API key:
- Make sure you have selected the correct application and environment in the Oryonix UI.
- Click on the "Authentication" tab.
- Click "Create API Key" and give your key a unique name.
- Click "Create".
- Save the API key securely sinec you will not be able to view it again.
Now, you can include the API key in the headers as an Authorization: Bearer token as shown below.
Test Your Changes
Now, if you try to call your API again, you should see something much nicer:
curl -X POST https://your-service-url/hello \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <your-api-key>" \
-d '{"name": "Alice"}'
"Hello, Alice! Welcome to Oryonix."
What's Next?
Congratulations! You've deployed a service on Oryonix, called an API in your service, and made a simple change!
Next, dive deeper into Oryonix by reading about its concepts.