API Maker

The backend engine for the AI era

Your code

Languages & Runtimes

Write your backend in TypeScript, Python, PHP, C#, Go, Rust, Java or Kotlin. Every piece of code keeps its own language.

Custom APIs, hooks, schedulers, event listeners, migration scripts, utility classes and test cases are written in the language you pick at the top of the code editor. Each language runs on its own runtime, in Docker sandboxes of its own, with the packages of its own registry, and gets the same g: the request, the answer and every API of API Maker.

How it works

  1. Pick the language

    Choose it at the top of the code editor of a custom API, a hook, a scheduler or any other piece of code. TypeScript also offers its runtime: Node.js or Bun.

  2. Write main

    main gets g, the same in every language: the request in g.req, the answer in g.res, and your databases, the cache and the system APIs in g.sys.

  3. Save

    TypeScript is compiled when you save. Go, Rust, C#, Java and Kotlin are compiled right after it, in the background, and an error of the compiler shows in the console of your account. Python and PHP are ready at once.

  4. It runs in the sandbox of its language

    A Docker container with the runtime of the language, the version you chose and your packages answers the request, and reads your databases from there.

What you get

The same g everywhere

Every language gets the same request, answer and APIs, named the way the language names things: g.sys.db.get_all in Python and Rust, g.Sys.Db.GetAll in C# and Go, getAll in TypeScript, PHP, Java and Kotlin.

Many languages, one account

One custom API in TypeScript, the next in Python, a scheduler in Go, an event listener in Kotlin. A custom API calls one written in another language over HTTP, with g.sys.system.callExternalApi.

g.sys is a function call

The sandbox holds API Maker itself, as a native library of the runner of its language. A g.sys call reads the database from the sandbox, with no trip back to the server.

An example

One backend, three teams

The web team writes the checkout API in TypeScript, the data team a nightly report in Python, and the team of the price engine keeps its Go. They share the databases, the tokens, the logs and one Git repository, and their APIs call each other over HTTP.

Each language, its runtime and its packageslanguages
Language     Runtime             Default image                        Packages fromTypeScript   Node.js or Bun      node:24-slim, oven/bun:1-slim        npmPython       Python              python:3.13-slim                     PyPIPHP          PHP                 php:8.4-cli                          PackagistC#           .NET                mcr.microsoft.com/dotnet/sdk:10.0    NuGetGo           Go                  golang:1.27                          Go modulesRust         Rust                rust:1-slim                          crates.ioJava         JDK                 eclipse-temurin:25-jdk               Maven CentralKotlin       Kotlin on the JDK   eclipse-temurin:25-jdk               Maven Central

The default version of each runtime: choose another one in the sandbox settings. The oldest ones API Maker runs are Node.js 20, Bun 1.0, Python 3.12, PHP 8.3, .NET 8, Go 1.21, Rust 1.88 and JDK 20.

TypeScripthello.ts
import * as T from 'types';async function main(g: T.IAMGlobal) {    return { hello: g.req.body.name || 'world' };}module.exports = main;

The same custom API in every language: it reads name from the body and answers { hello: name }.

Pythonhello.py
from am import Gasync def main(g: G):    return {'hello': g.req.body.name or 'world'}
PHPhello.php
<?phpfunction main(AmG $g){    return ['hello' => $g->req->body->name ?? 'world'];}
C#Hello.cs
file static class Api{    public static async Task<object?> Main(G g)    {        return new { hello = g.Req.Body.GetString("name", "world") };    }}
Gohello.go
package codeimport "apimaker/am"func Main(g *am.G) (any, error) {    name := g.Req.Body().String("name")    if name == "" {        name = "world"    }    return am.M{"hello": name}, nil}
Rusthello.rs
use am::{Errors, G, Value, json};pub fn main(g: &mut G) -> Result<Value, Errors> {    let name = g.req.body()["name"].as_str().unwrap_or("world");    Ok(json!({ "hello": name }))}
Javahello.java
import am.G;import java.util.Map;class Api {    static Object main(G g) {        String name = g.req.getBody().getString("name", "world");        return Map.of("hello", name);    }}
Kotlinhello.kt
import am.Gfun main(g: G): Any? {    return mapOf("hello" to g.req.body.getString("name", "world"))}

Good to know

  • One API is one language: the code of an API and all its hooks are written in the same language, because one sandbox answers the request. A custom API which has hooks keeps its language until they are rewritten.
  • Settings are always TypeScript: the Basic Info of a custom API and of a token generator, API settings, schemas, internationalization texts and secrets. API Maker runs them itself when you save, without a sandbox or packages.
  • Nothing is installed unless you add it: a package your code imports has to be in the packages of its language.

Questions

API Maker is built in Rust. Do I have to write Rust?

No. The server of API Maker is written in Rust, and your code runs next to it, in a sandbox of its own language. Pick the language your team knows.

Can code of one language call code of another?

Yes. Every piece of code has its own language, and a custom API calls another one over HTTP with g.sys.system.callExternalApi, whatever its language: each runs in the sandbox of its language.

Does the language change what my code can do?

No. Every language gets the same g, with the same databases, cache and system APIs. What differs is how the language writes them, its packages and its runtime.