# Getting Started > **Status:** 🔴 DA REVISIONARE Welcome to **genro-asgi**. This page takes you from installation to a running server that answers real HTTP requests, then explains the hello-world line by line so you know *why* each piece is there before you reach for anything more advanced. ## What genro-asgi is genro-asgi is a **minimal ASGI server core**. You build one server object, mount your applications on it, and it routes incoming requests to `@route`-decorated methods on those applications. Everything else — authentication, sessions, background tasks, OpenAPI, MCP, streaming — grows on the same core by composition, turned on through constructor keyword arguments. Two ideas shape the whole design: - **The server is an object.** You construct an `AsgiServer`, call `.serve()`, and throw it away. There are no global variables and no module-level state; a test can spin up a fresh isolated server on every run. - **Configuration is data.** Features do not appear and disappear — the objects always exist. What changes is the config you hand them (a backend, a set of credentials, a middleware option). You never flip a feature on by monkey- patching; you describe it. If you come from Starlette or FastAPI, the decorate-a-handler workflow will feel familiar; the differences are covered in [Coming from Starlette / FastAPI](coming-from-fastapi.md). ## Installation ```bash pip install genro-asgi ``` Requires Python 3.11+. ## Hello world One file. A `RoutedApplication` subclass with two `@route` handlers, served by an `AsgiServer`: ```python # hello.py from genro_asgi import AsgiServer, RoutedApplication from genro_routes import route class Hello(RoutedApplication): @route() def index(self) -> dict[str, str]: return {"hello": "world"} @route() def greet(self, name: str = "world") -> dict[str, str]: return {"hello": name} if __name__ == "__main__": server = AsgiServer(primary=Hello()) server.serve(host="127.0.0.1", port=8000) ``` Run it: ```bash python hello.py ``` And call it: ```console $ curl http://127.0.0.1:8000/index {"hello": "world"} $ curl "http://127.0.0.1:8000/greet?name=genro" {"hello": "genro"} $ curl "http://127.0.0.1:8000/greet" {"hello": "world"} $ curl -i http://127.0.0.1:8000/nowhere HTTP/1.1 404 Not Found ``` ## Line by line Every line above earns its place. Here is what each one does. ### The primary application ```python class Hello(RoutedApplication): ``` `RoutedApplication` is the base class for an application whose behaviour is described by `@route`-decorated methods. Your subclass *is* the application: its methods are its endpoints. ### The `@route` decorator ```python from genro_routes import route @route() def index(self) -> dict[str, str]: return {"hello": "world"} ``` `route` is imported from **genro-routes**, the protocol-neutral routing library genro-asgi builds on. It is *not* re-exported from `genro_asgi`, so import it directly from `genro_routes`. Marking a method with `@route()` publishes it as an endpoint. The method name becomes the URL segment: `index` answers `GET /index`, `greet` answers `GET /greet`. Returning a `dict` produces a JSON response automatically. ### Query parameters bind to the signature ```python @route() def greet(self, name: str = "world") -> dict[str, str]: return {"hello": name} ``` Parameters in the method signature bind to the request's query string, typed and with defaults. `GET /greet?name=genro` calls `greet(name="genro")`; `GET /greet` with no query string uses the default `"world"`. The annotation drives coercion — declare `max_price: float` and the incoming string is converted to a float before your method runs. ### Building and serving ```python server = AsgiServer(primary=Hello()) server.serve(host="127.0.0.1", port=8000) ``` `AsgiServer(primary=...)` builds the server. The **primary** application is mandatory — it answers `/` and every path no mounted app claims. Omitting it is an error. `server.serve(host=..., port=...)` boots a programmatic uvicorn loop and **blocks** until the process stops. The server object *is* the ASGI application handed to uvicorn — there is no separate app callable to wire up. > There is no `.run()` method and there is no command-line launcher. You start > the server by calling `.serve()` from your own Python entry point. Passing > `port=0` lets the OS assign a free port, which is handy in tests. ## Responding with JSON or HTML A handler that returns a `dict` answers **JSON**. To answer **HTML**, declare the media type on the route and return a string: ```python class Pages(RoutedApplication): @route() def data(self) -> dict: return {"ok": True} # application/json @route(media_type="text/html") def home(self) -> str: return "