Skip to main content

BunnyHopApi is a lightweight and fast web framework designed to handle modern web development needs.

Project description

BunnyHopApi

BunnyHopApi is a lightweight and fast web framework designed to handle modern web development needs. It provides full support for:

  • HTTP Requests: Easily handle all HTTP methods.
  • SSE (Server-Sent Events): Support for server-sent events.
  • WebSockets: Real-time bidirectional communication.
  • Middlewares:
    • At the server level.
    • At the route level.
    • At the endpoint level.
  • CORS: Simple configuration to enable CORS.
  • Web Page Rendering:
    • Static pages.
    • Dynamic pages with Jinja2.
  • Type Validation: Automatic validation for query parameters, path parameters, and request bodies.
  • Swagger Documentation: Automatically generated Swagger documentation for all endpoints.
  • Exceptional Performance: Designed to be fast and efficient.

Key Features

1. HTTP, SSE, and WebSocket Support

BunnyHopApi allows handling standard HTTP requests, SSE for real-time updates, and WebSockets for bidirectional communication.

Example: HTTP Endpoint

class HealthEndpoint(Endpoint):
    path = "/health"

    def get(self, headers):
        return 200, {"message": "GET /health"}

Example: SSE Endpoint

class SseEndpoint(Endpoint):
    path = "/sse/events"

    @Endpoint.with_content_type(Router.CONTENT_TYPE_SSE)
    async def get(self, headers) -> {200: str}:
        events = ["start", "progress", "complete"]

        for event in events:
            yield f"event: {event}\ndata: Processing {event}\n\n"
            await asyncio.sleep(1.5)

        yield "event: end\ndata: Processing complete\n\n"

Example: WebSocket Endpoint

class WSEndpoint(Endpoint):
    path = "/ws/chat"

    async def connection(self, headers):
        logger.info("Client connected")
        logger.info(f"Headers: {headers}")

        return True

    async def disconnect(self, connection_id, headers):
        logger.info(f"Client {connection_id} disconnected")

    async def ws(self, connection_id, message, headers):
        logger.info(f"Received message from {connection_id}: {message}")
        for i in range(10):
            yield f"event: message\ndata: {i}\n\n"
            await asyncio.sleep(0.2)

2. Flexible Middlewares

Define middlewares at different levels:

  • Global: Applied to all routes and endpoints.
  • Route-specific: Applied to a specific set of endpoints.
  • Endpoint-specific: Applied to an individual endpoint.

Example: Middleware

async def global_middleware(endpoint, headers, **kwargs):
    logger.info("global_middleware: Before calling the endpoint")
    response = await endpoint(headers=headers, **kwargs)
    logger.info("global_middleware: After calling the endpoint")
    return response

3. Type Validation

BunnyHopApi provides automatic type validation for query parameters, path parameters, and request bodies using Pydantic models.

Example: Query Parameters

class UserEndpoint(Endpoint):
    path = "/user"

    def get(
        self, headers, age: QueryParam[int], name: QueryParam[str] = "Alice"
    ) -> {200: MessageModel}:
        return 200, {"message": f"GET /user/ pathparams: age {age}, name {name}"}

Example: Path Parameters

    def get_with_params(self, user_id: PathParam[int], headers) -> {200: MessageModel}:
        return 200, {"message": f"GET /user/{user_id}"}

Example: Request Body

class BodyModel(BaseModel):
    name: str
    age: int

    def post(self, headers, body: BodyModel) -> {201: MessageModel}:
        return 201, {"message": f"POST /user/ - {body.name} - {body.age}"}

4. Swagger Documentation

BunnyHopApi automatically generates Swagger documentation for all endpoints, making it easy to explore and test your API.

Example: Access Swagger

Once the server is running, visit /docs in your browser to view the Swagger UI.

5. Web Page Rendering

  • Static Pages: Serve HTML files directly.
  • Dynamic Pages: Use Jinja2 for dynamic template rendering.

Example: Static Page

class SseTemplateEndpoint(Endpoint):
    path = "/sse"

    @Endpoint.with_content_type(Router.CONTENT_TYPE_HTML)
    async def get(self, headers):
        return await serve_static_file("example/templates/static_html/sse_index.html")

Example: Dynamic Page

class JinjaTemplateEndpoint(Endpoint):
    path = "/"

    def __init__(self):
        super().__init__()
        self.template_env = create_template_env("example/templates/jinja/")

    @Endpoint.with_content_type(Router.CONTENT_TYPE_HTML)
    async def get(self, headers):
        return await render_jinja_template("index.html", self.template_env)

6. Performance

BunnyHopApi is extremely fast. Here's a benchmark that demonstrates its performance:

wrk -t12 -c400 -d30s --timeout 1m http://127.0.0.1:8000/health

Results:

Running 30s test @ http://127.0.0.1:8000/health
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     6.68ms    7.03ms 244.27ms   89.09%
    Req/Sec     3.48k     1.14k    9.78k    62.59%
  1243109 requests in 30.10s, 283.34MB read
Requests/sec:  41302.60
Transfer/sec:      9.41MB

7. Serving Static Content

BunnyHopApi allows marking an entire folder as static to serve all its content directly.

Example: Static Folder Configuration

import os
from bunnyhopapi.server import Server

def main():
    server = Server(cors=True, port=8000)

    # Mark the "static" folder as static
    static_folder = os.path.join(os.path.dirname(__file__), "static")
    server.include_static_folder(static_folder)

    server.run()

In this example, all files inside the static folder will be accessible directly from the browser. For example:

  • A file static/style.css will be accessible at http://localhost:8000/static/style.css.

Installation

You can install BunnyHopApi directly from PyPI:

pip install bunnyhopapi

Usage

  1. Create a new Python file and import BunnyHopApi:

    from bunnyhopapi.server import Server
    from bunnyhopapi.models import Endpoint
    
  2. Define your endpoints and middlewares.

    class HealthEndpoint(Endpoint):
        path = "/health"
    
        def get(self, headers):
            return 200, {"message": "GET /health"}
    
  3. Start the server:

    server = Server(cors=True, middleware=global_middleware, port=8000)
    server.include_endpoint_class(HealthEndpoint)
    server.run(workers=4)  # You can specify the number of workers (default: os.cpu_count())
    

    By default, the number of workers is set to the number of CPU cores available (os.cpu_count()), but you can customize it by passing the workers argument to the run method.

Example Project

Check the example/main.py file for a complete example of how to use BunnyHopApi.

License

This project is licensed under the MIT License. See the LICENSE file for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

bunnyhopapi-1.2.1.tar.gz (17.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

bunnyhopapi-1.2.1-py3-none-any.whl (18.1 kB view details)

Uploaded Python 3

File details

Details for the file bunnyhopapi-1.2.1.tar.gz.

File metadata

  • Download URL: bunnyhopapi-1.2.1.tar.gz
  • Upload date:
  • Size: 17.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.2

File hashes

Hashes for bunnyhopapi-1.2.1.tar.gz
Algorithm Hash digest
SHA256 9826b7f6400a74f735f15c3740eabc0343417cd5325e320ea85b67ddf40fc2cf
MD5 fdadb763fef0de3246aad5892e86ab42
BLAKE2b-256 63874e4130f9b438d74c6b018efd4441105fe118024a1e1a63aa988166b5805a

See more details on using hashes here.

File details

Details for the file bunnyhopapi-1.2.1-py3-none-any.whl.

File metadata

  • Download URL: bunnyhopapi-1.2.1-py3-none-any.whl
  • Upload date:
  • Size: 18.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.2

File hashes

Hashes for bunnyhopapi-1.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 cba20dda55101f88c4db387bce228fed36d2361930683e67b9e884269600f1c3
MD5 90bc2c0f728d14102ac707e04e67c5b5
BLAKE2b-256 864b7eba5ad642a03a93701216ea9ec5e85b283031a5f39f609a01be9bf64b82

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page