Lightbug πŸ”₯🐝- The First Mojo HTTP Framework

Published on
11-01-2024
Author
Aisys
Category
News
https://cdn.aisys.pro/stories/lightbug-the-first-mojo-http-framework.jpg

Mojo is a language that combines the readability of Python with the speed of C++.


It's useful for different things, from low-level code that's close to hardware, via the Back-End API design, to the world of Front-End and the Web. Mojo is powerful enough to scale across the entire modern stack. The language has also been designed with AI and Machine Learning in mind β€” so it will be particularly useful for AI developers and Data Scientists.


Mojo is still young though. The ecosystem has been missing the tools for everyday software development, like networking or basic HTTP operations.


This is where Lightbug πŸ”₯🐝 flies in.


Lightbug πŸ”₯🐝 is a simple HTTP framework written in pure Mojo, with no external dependencies by default. It's meant to serve as a foundation for more complex projects and allows you to develop Web services like APIs, set up basic routing, or even serve HTML pages with Mojo, while taking advantage of the features of this language, like static typing and great performance.


To get started, simply install Mojo and Git, then clone the Lightbug Github repo:


git clone https://github.com/saviorand/lightbug_http.git


Once it's cloned, switch to the directory in your command line:


cd lightbug_http


Then run the server (yes, that's the Mojo file extension! πŸ”₯):


mojo lightbug.πŸ”₯


You should see the following line printed to the console:


πŸ”₯🐝 Lightbug is listening on 0.0.0.0:8080
Ready to accept connections...


Now, you can start making requests to your server or try opening localhost:8080 or 0.0.0.0:8080 in the browser β€” you should see the intro screen. Welcome to the Web, Mojo-style! Now, let's get to some real coding.


While Lightbug is still young, the core functionality people expect to be able to develop for the modern Web is already there.


Note that since there's no package manager yet, you should simply include lightbug_http as a subfolder inside your own project. This will work as a Mojo package and will allow you to import tools like web primitives, servers, and more from Lightbug.


For example, add this to the top of your file to import the server:


from lightbug_http.sys.server import SysServer


This will import a server implementation in pure Mojo. If you want to use the Python implementation, import the PythonServer instead. It will work in the same manner.


To make an HTTP service, simply make a struct that satisfies the HTTPService trait, meaning it has a func method with the following signature:


trait HTTPService:
 fn func(self, req: HTTPRequest) raises -> HTTPResponse:
     ...


This uses the built-in primitives to take in an HTTPRequest, execute any custom logic you write in Mojo or Python, and return an HTTPResponse object with some data back to the API consumer.


Let's make a service that prints all requests sent to 0.0.0.0:8080 to the console. To do this, create a file called my_awesome_service.πŸ”₯, and paste the following:


from lightbug_http import *

@value
struct Printer(HTTPService):
   fn func(self, req: HTTPRequest) raises -> HTTPResponse:
      let body = req.body_raw
      print(String(body))
      return OK(body)

fn main() raises:
    var server = SysServer()
    let handler = Printer()
    server.listen_and_serve("0.0.0.0:8080", handler)


Run mojo my_awesome_service.πŸ”₯, and send the request to 0.0.0.0:8080 from your favorite API clients, like Insomnia or Bruno. You should see some details about the request printed to the console.


Congrats! You're officially a Mojo web developer now πŸ”₯.


In the example, we initialize a variable called handler with let (meaning it cannot be re-assigned) and pass it to listen_and_serve as a second parameter for clarity.


Adding a @value decorator is optional: if you're an advanced Mojician, you can add an __init__ constructor method instead. It will work the same; @value just generates this and other useful methods automatically.


struct Printer(HTTPService):
	fn __init__(inout self):
		print("Printer initialized!")

	fn func(self, req: HTTPRequest) raises -> HTTPResponse:
		let body = req.body_raw	
		print(String(body))
		return OK(body)


You might say, but that's just one route! Modern APIs require much more than that.


Lightbug can also do some basic routing as well:


@value
struct ExampleRouter(HTTPService):
   fn func(self, req: HTTPRequest) raises -> HTTPResponse:
      let body = req.body_raw
      let uri = req.uri()

      if uri.path() == "/":
            print("I'm on the index path!")
      if uri.path() == "/first":
            print("I'm on /first!")
      elif uri.path() == "/second":
            print("I'm on /second!")

      return OK(body)


Add this to your my_awesome_service.πŸ”₯, and pass it as a handler to the server:


fn main() raises:
    var server = SysServer()
	let handler = ExampleRouter()
	server.listen_and_serve("0.0.0.0:8080", handler)


You can now open your browser and go to localhost:8080/first, localhost:8080/second to see the changes.


This functionality should give you a basis to develop your own apps, libraries, and services that make use of HTTP, while taking advantage of the flexibility and customization options that a lightweight framework/toolkit that is the lightbug_http can provide.


We plan on making routing, as well as other tasks like authoring and generating APIs from an OpenAPI specification, designing your data model, and building web applications even more enjoyable in the future by building lightbug_api and lightbug_web packages. Check out our Roadmap for details.


That's it for an introduction to Lightbug πŸ”₯🐝! Hope it was useful.


This is an open-source, non-commercial community project.


Please star ⭐ our Github repo, join the Discord, and check out how to contribute with your code, so we can make it even better for the fellow Mojicians.


Til’ next time!

Discussion (20)

Not yet any reply