Robyn, a web framework for Python, built it on top of Rust.
Published: 2022-12-16 14:44:55

---
title: Robyn, a web framework for Python, built it on top of Rust.
published: true
date: 2022-11-23 23:41:56 UTC
tags: 
canonical_url: https://carlosmv.hashnode.dev/robyn-a-web-framework-for-python-built-it-on-top-of-rust
---

For this article, we are going to build a server using the Robyn web framework.

It will be a simple server, we are going to learn how to serve an HTML file, and how to perform CRUD operations. We will not be using a database to do it, just a python list as a fake database.

### What is Robyn?

> "Robyn is a fast, high-performance Python web framework with a Rust runtime. It has been designed to provide near-native Rust throughput while benefiting from the code being written in Python. Robyn is comparable to other products such as Flask, FastAPI, Django, and a web server of choice. One of the key advantages of Robyn is that it does not require an external web server for production, making it more efficient and streamlined".
> 
> Sanskar Jethi

More about Robyn, [here](https://www.sanskar.me/posts/hello-robyn) in Sanskar's blog.

### Features

> - Under active development!
> - Written in Rust, btw xD
> - A multithreaded Runtime
> - Extensible
> - A simple API
> - Sync and Async Function Support
> - Dynamic URL Routing
> - Multi Core Scaling
> - WebSockets!
> - Middlewares
> - Hot Reloading
> - Community First and truly FOSS!

### Setup

```
py -m venv venv
cd venv/Scripts
activate

```

### Installation

```
pip install robyn

```

### Hello World

Let's write a simple endpoint that returns a "Hello World".

We create a file called "app.py".

```
from robyn import Robyn

app = Robyn( __file__ )

@app.get("/")
async def hello(request):
    return "Hello, world!"

app.start()

```

We import Robyn and create a Robyn instance, in this case, the `app` variable.

Then, we define a path operation decorator. When a request goes to the path `/` using a `get` operation, the `hello` function will handle the request and will respond with a "Hello, World!".

To run the server we write in our terminal:

```
py app.py

```

[Gist](https://gist.github.com/carlosm27/cfb335822dc86f772d90c1058ddd6d8a)

### Serving Static Files

index.html

```
<h1>Hello, World! <h1>


from robyn import Robyn, static_file

app = Robyn( __file__ )

@app.get("/index")
async def get_page(request):
    return static_file("./index.html")

app.start(port=5000)

```

Here, we import the `static file` object and inside the handler, we pass the path of the HTML file we want to serve.

[Gist](https://gist.github.com/carlosm27/1276b2ac4345dd3bdf0a7ad718841911)

### CRUD API

app.py

```
from robyn import Robyn, jsonify, static_file
from helper import get_item

import json

app = Robyn( __file__ )

fake_fruit_database = [
    {"id":1, "fruit":"Apple"},
    {"id":2, "fruit": "Orange"},
    {"id":3, "fruit": "Pineapple"}
]

...

@app.get("/fruits")
def all_fruits(request):
    return jsonify(fake_fruit_database)

...

```

This handler returns the `fake_fruit_database` list as a JSON.

[Gist](https://gist.github.com/carlosm27/1955e8c491c1769cec77195ab564e739)

helper.py

```
def get_item(id: int, db: list):
    fruit = {}
    for dic in db:
        for val in dic.values():
            if val == id:
                fruit = dic
    return fruit

```

This is just a function to help us look in a list a return the dictionary that has the `id` passed in it.

[Gist](https://gist.github.com/carlosm27/4adfa994c7b39b4726d1ca152d210e48)

#### Get by Id handler

```
@app.get("/fruit/:id")
def get_fruit(request):
    id = request['params']['id']
    fruit_id = int(id)

    fruit = get_item(fruit_id, fake_fruit_database)

    if fruit == {}:
        return {"status_code":404, "body": "Fruit not Found", "type": "text"}
    else:    
        return jsonify(fruit)

```

In this handler, we are adding the path parameter `id`, to retrieve the fruit with the "id" passed.

The request object has this form:

```
{'params': {'id': ""} , 'headers': {'content-type': 'application/json'}, 'host': 'localhost:5000', 'body' : [] }

```

To extract the value of `id` we passed the key `params` first and then the key `id`.

If no `id` is matched, the handler returns the message "Fruit not Found". Otherwise returns a json response.

[Gist](https://gist.github.com/carlosm27/cee266f2b744dc137d4b4fcfd78bb11b)

#### Post handler

```
@app.post("/fruit")
def add_fruit(request):

    body = bytearray(request['body']).decode("utf-8")

    fruit = json.loads(body)

    new_id = fake_fruit_database[-1]['id'] + 1

    fruit_dict = {"id":new_id, "fruit":fruit['fruit']}

    fake_fruit_database.append(fruit_dict)
    return {"status_code":201, "body":jsonify(fruit_dict), "type": "json"}

```

In this handler, we have to extract the body from the request object and decode it.

Then, we use the `json.loads()` method, to deserialize and convert the body to a python dictionary.

We generate an `id` for the fruit we are adding. We create a new dictionary and append it to the fruit list. And return the status code `201` and a JSON as a body response.

[Gist](https://gist.github.com/carlosm27/649ccb13772614f21de26380c9c09c1e)

#### Put handler

```
@app.put("/fruit/:id")
def update_fruit(request):
    id = request["params"]["id"]
    body = bytearray(request['body']).decode("utf-8")
    fruit = json.loads(body)

    fruit_id = int(id)
    fruit_dict = get_item(fruit_id,fake_fruit_database)

    if fruit_dict == {}:
        return {"status_code":404, "body": "Fruit not Found", "type": "text"}
    else:    
        fruit_dict['fruit'] = fruit['fruit']
        return jsonify(fruit)

```

We need to extract the `id` parameter and the body for this handler.

Then, we search for the fruit with the `id` passed and replace the fruit value with the one in the request body.

[Gist](https://gist.github.com/carlosm27/0d08e8a6c22327359eaf9a075fd8d778)

#### Delete handler

```
@app.delete("/fruit/:id")
def delete_fruit(request):
    id = request["params"]["id"]

    fruit_id = int(id)
    fruit_dict = get_item(fruit_id,fake_fruit_database)

    if fruit_dict == {}:
        return {"status_code":404, "body": "Fruit not Found", "type": "text"}
    else:    
        fake_fruit_database.remove(fruit_dict)
        return jsonify({"Message":"Fruit was deleted"})

```

Here, we just remove from the fruit list the dictionary with the `id` passed as a parameter.

[Gist](https://gist.github.com/carlosm27/f6ec4d16d2886cee180ab2dbe5541114)

```
app.start()

```

The default `PORT` and `url` are `5000` and`127.0.0.1` respectively.

If we want to start the server on a different port and URL, we have to pass the values to `app.start()`.

```
app.start(port=8000, url="0.0.0.0")

```

### Conclusion

I have been learning Rust for a few months and I thought the approach of using Rust to build a Python web framework and leverage the power of Rust is interesting. I was really enthusiastic to use Robyn and write about it. And I am excited to follow its development.

Thank you for taking the time to read this article.

If you have any recommendations about other packages, architectures, how to improve my code, my English, or anything; please leave a comment or contact me through [Twitter](https://twitter.com/Carlos_marcv), or [LinkedIn](https://www.linkedin.com/in/carlos-marcano-a2135a134/).

The source code is [here](https://github.com/carlosm27/robyn_demo)

### Reference

- [Robyn's GitHub Page](https://github.com/sansyrox/robyn)

- [Robyn Documentation](https://sansyrox.github.io/robyn/#/)

- [Hello, Robyn!](https://www.sanskar.me/posts/hello-robyn)