Introduction

Welcome to the third lesson of "Exposing Your Code Translator with FastAPI"! You’ve already learned how to design a clean, layered backend and explored the basics of REST APIs. Now, you’re about to take a big step forward: connecting the intelligent code translation pipeline we developed in Course 1 to our web service. By the end of this lesson, you’ll have a real API that can translate code between languages — making your project both practical and powerful.

In this lesson, you’ll see how to bridge the gap between your machine learning pipeline and your FastAPI backend. This is where your project truly comes alive, as you expose your translation logic to the outside world through well-designed API endpoints. Let’s dive in and make your code translator accessible to everyone!

Lesson Roadmap

In this lesson, we'll implement the key API endpoints that make your code translator accessible to users. Specifically, we'll build:

  • Health Check Endpoint: A simple /health endpoint to confirm that your API is running. This is a common best practice for any web service, making it easy to monitor and debug.
  • Supported Languages Endpoint: The /languages endpoint will return a list of languages your translator can handle. This helps clients know what translation targets are available and keeps your API self-documenting.
  • Code Translation Endpoint: The main /translate endpoint will accept code and a target language, then return the translated code. This is the core functionality of your service, connecting the outside world to your Haystack-powered translation pipeline.

You’ll see how to connect your existing translation pipeline to these endpoints, ensuring each layer of your application stays clean and focused. By the end, your FastAPI backend will be ready to serve real translation requests!

Building the Service Functions

Let’s start by implementing the core service functions that will power your API. These functions will handle supported languages and run the translation pipeline.

# List of languages our translator can output
SUPPORTED_LANGUAGES = [
    'python', 'c', 'cpp', 'javascript', 'typescript', 'shell', 'r'
]

def get_supported_languages():
    # Return the list of supported languages
    return SUPPORTED_LANGUAGES

Here, you define a simple list of supported languages and a function to return it. This makes it easy to update your language support in one place.

Now, let’s add the function that actually runs the translation:

from code_translator.pipeline import translate_code

def run_code_translation(code, target_language):
    # Pass the code and target language to the translation pipeline
    result = translate_code(code, target_lang=target_language)
    return result

This function acts as the bridge to your Haystack pipeline. It takes the code and the target language, calls the pipeline, and returns the result. Notice how the service layer doesn’t care about HTTP or API details — it just focuses on the translation logic.

Simple Controller Endpoints: Health and Languages

Let’s start with two straightforward controller functions: one for a health check and another to list supported languages.

from api import service

# Health check endpoint
async def health_check():
    return {"status": "ok"}

# Endpoint to get supported languages
async def get_supported_languages():
    languages = service.get_supported_languages()
    return {"languages": languages}

These functions are simple and direct. The health check lets users know your API is running, while the languages endpoint provides a list of available translation targets.

Robust Code Translation Controller

The main event is the controller for code translation. This function needs to validate input, handle errors, and call the service layer.

from fastapi import Request, HTTPException, status
from fastapi.responses import JSONResponse

async def translate_code_handler(request: Request):
    """
    Accepts code and target language, returns translated code and explanation.
    """
    payload = await request.json()
    code = payload.get("code")
    target_language = payload.get("target_language")

    # 400 if missing required fields
    if code is None:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Missing required field: 'code'."
        )
    if target_language is None:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Missing required field: 'target_language'."
        )

    # 422 if fields are present but invalid
    if not isinstance(code, str) or not code.strip():
        raise HTTPException(
            status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
            detail="The 'code' field must be a non-empty string."
        )

    supported = service.get_supported_languages()
    if not isinstance(target_language, str) or target_language.lower() not in supported:
        raise HTTPException(
            status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
            detail=f"Target language '{target_language}' is not supported."
        )

    result = service.run_code_translation(code, target_language.lower())
    return JSONResponse(content=result)

Let’s break down how this function works:

  • Parse the incoming JSON payload: The function extracts the code and target_language fields from the request body.
  • 400 Bad Request for missing fields: If either code or target_language is missing from the payload, the function raises a 400 BAD REQUEST error with a clear message indicating which field is missing.
  • 422 Unprocessable Entity for invalid fields: If the fields are present but invalid (e.g., code is not a non-empty string), the function raises a 422 UNPROCESSABLE ENTITY error with a descriptive message.
  • Run the translation: If all checks pass, the function calls the service layer to perform the translation.
  • Return the result: The translated code and explanation are returned as a JSON response.

In simpler terms, this function carefully checks the incoming data, provides clear error messages, and only proceeds if everything looks good. This kind of robust validation is essential for real-world APIs.

Defining and Registering API Routes

Now that your controller functions are ready, you need to connect them to specific API routes. This is where you define which URLs trigger which functions.

from fastapi import APIRouter
from api.controller import health_check, get_supported_languages, translate_code_handler

router = APIRouter()

# Register the health check endpoint
router.get("/health", tags=["Health"])(health_check)

# Register the supported languages endpoint
router.get("/languages", tags=["Translation"])(get_supported_languages)

# Register the code translation endpoint
router.post("/translate", tags=["Translation"])(translate_code_handler)

Each route is clearly mapped to a controller function. The tags help organize your API documentation, making it easier for users to find what they need. This structure keeps your routing layer clean and easy to maintain.

Configuring the FastAPI Application

Finally, let’s see how all these pieces come together in your FastAPI application. This is where you set up the app and include your routes.

from fastapi import FastAPI
from api.routes import router as api_router

app = FastAPI(
    title="Code Translator API",
    description="A simple API for code translation using Haystack and LLMs.",
    version="0.2.0"
)

# Register all API routes under the /api prefix
app.include_router(api_router, prefix="/api")

This configuration gives your API a clear structure and helpful metadata. By grouping all endpoints under /api, you make your service easier to use and extend in the future.

Testing the Translation Endpoint with Curl

Once your FastAPI application is up and running, it's crucial to test the translation endpoint to ensure it behaves as expected. Using curl, a command-line tool for transferring data with URLs, you can simulate requests to your API and observe the responses. Here are some examples to guide you through testing different scenarios:

  1. Valid Translation Request. To test a successful translation, you can send a POST request with valid code and a supported target language. This example translates a simple Python function to JavaScript:

    curl -X POST http://127.0.0.1:3000/api/translate -H "Content-Type: application/json" -d '{"code": "def add(a, b):\n    return a + b", "target_language": "javascript"}'

    which outputs something similar to:

    {
      "translated_code": "```javascript\nfunction add(a, b) {\n    return a + b;\n}\n```",
      "explanation": "The translation involved converting the Python function definition syntax into JavaScript syntax. The 'def' keyword in Python is replaced with the 'function' keyword in JavaScript, and the indentation style for the function body is adjusted to use curly braces."
    }
  2. Invalid Request (Missing Code). It's important to handle cases where the request payload is incomplete. The following command omits the code field, which should trigger a validation error:

    curl -X POST http://127.0.0.1:3000/api/translate -H "Content-Type: application/json" -d '{"target_language": "javascript"}'

    This results in a 422 UNPROCESSABLE ENTITY HTTP response with the following payload:

    {"detail":"Missing required field: 'code'."
  3. Invalid Request (Unsupported Language). Testing how your API handles unsupported languages is also essential. This example attempts to translate Python code to an unsupported language:

    curl -X POST http://127.0.0.1:3000/api/translate -H "Content-Type: application/json" -d '{"code": "print(123)", "target_language": "italian"}'

    which results again in a 422 UNPROCESSABLE ENTITY response:

    {"detail":"Target language 'italian' is not supported."}

By running these curl commands, you can quickly verify that your translation endpoint is robust, handling both valid and invalid requests gracefully. This testing process is a vital step in ensuring your API is ready for real-world use.

Conclusion and Next Steps

You’ve now connected your code translation pipeline to a robust FastAPI backend, exposing its power through clean and well-structured API endpoints. By following best practices for separation of concerns and error handling, you’ve built a foundation that’s both flexible and reliable.

In the next part of your journey, you’ll get hands-on experience using and extending these endpoints, deepening your understanding of how modern APIs work. Keep up the momentum — your smart code translator is well on its way to becoming a real-world tool!

Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal