This episode guides you through setting up a 9router workspace, configuring the first route rules and model endpoints, running a local server, then verifying that request and response routing works as expected with a real example.

In episode 2 you learned about 9router's internal architecture: the route engine, model selector, policy engine, and observability layer. Now it's time to put that concept into real practice — install 9router, create a project, and configure the first route.
This episode's roadmap: we'll create a project workspace, prepare the basic configuration, define route rules and model endpoints, run a local server, then verify that requests and responses work as expected. By the end of this episode you'll have a live 9router gateway that can accept real requests.
The first step is making sure the 9router emulator is running. From episode 0 you should already have the emulator image; make sure it's active:
docker ps | grep 9router-localIf it's not running, start it again with the same command as in episode 0. Next, install the 9router CLI as a helper tool for project initialization and validation:
npm install -g @9router/cliOnce installed, create a new project workspace with the init command:
9router init my-gateway
cd my-gatewayThe command above produces a standard folder structure. This structure matters because every file has a clear role:
my-gateway/
config/ <- route rules and policy
endpoints/ <- model endpoint definitions
.env.local <- credentials (not committed)
package.jsonGet into the habit of writing 9router configuration as code that is version-controlled with git. Initializing the repository from the start will help a lot when we discuss configuration management in episode 10.
Now let's define the first route rules. The file config/routes.yaml is where you declare how requests are directed. Start with one simple route that catches all chat requests:
version: "1"
routes:
- name: default-chat
match:
any: true
target:
model: gpt-4o-mini
description: Rute default untuk semua request chatLet's break this down line by line:
| Line | Meaning |
|---|---|
name | The route's identity, which appears in logs and observability |
match | The request criteria this route catches |
any: true | The route catches all requests without exception |
target | The definition of the destination model chosen when the route matches |
With this configuration, all requests will be directed to the gpt-4o-mini model. In episodes 4 and 5 we'll enrich match with intent, keywords, and metadata, and target with multiple models.
Route rules determine where, while the endpoints/models.yaml file defines who gets called — which provider and with what credentials. Add an endpoint for the model we're using:
models:
- id: gpt-4o-mini
provider: openai
credential: OPENAI_API_KEY
max_tokens: 4096
cost_per_1k: 0.00015A few important points:
id must match the target.model value in the route rules.provider points to the provider connector — in later episodes you'll get to know OpenAI, Azure OpenAI, Anthropic, and custom providers.credential refers to the name of the environment variable holding the API key, not the key itself.cost_per_1k is used for cost estimation — important data for model selection decisions in episode 5.Make sure the credentials are available as environment variables, as prepared in episode 0:
export OPENAI_API_KEY="sk-..."Warning
Never write API key values directly into configuration files or code. Always reference the environment variable name like OPENAI_API_KEY, and store the actual value in a secret manager or a dot-env file that is not committed.
Before running the server, validate the configuration you've written. The 9router CLI provides a validation command to catch syntax errors or incorrect references:
9router validate config/routes.yaml endpoints/models.yamlIf the configuration is valid, the command returns a success message. If there's an error such as an unfound endpoint or a credential that hasn't been set, the command will show the problematic line. Make 9router validate a gate before every change — a pattern we'll formalize in episode 19 when discussing CI/CD.
With a valid configuration, it's time to run the gateway:
9router serve --port 8080The server will start listening on port 8080. Notice the startup logs showing the registered routes and provider connection status. This is also the first time you see the observability layer at work — every startup event is recorded as a log.
For development with auto-reload when the configuration changes, use the watch flag:
9router serve --port 8080 --watchIn this mode, changes to route or endpoint files take effect immediately without a manual restart — greatly speeding up iteration when testing rule matching in the next episodes.
Now test the gateway with a real request. Use curl to send a prompt and observe how it's responded to:
curl -X POST http://localhost:8080/v1/chat \
-H "Authorization: Bearer <API_KEY>" \
-H "Content-Type: application/json" \
-d '{"prompt":"Sebutkan tiga manfaat routing AI dalam satu kalimat"}'The request above enters the route engine, is directed by the default-chat route, and calls the gpt-4o-mini model. If everything works, the response contains the model's answer. To see the routing decision details, ask for additional metadata:
curl -X POST http://localhost:8080/v1/chat \
-H "Authorization: Bearer <API_KEY>" \
-H "Content-Type: application/json" \
-d '{"prompt":"Halo","include_route_meta":true}'The response will include additional fields such as the route selected, the model called, the latency per step, and the destination provider. This is your first window into the observability layer — the ability to see routing decisions explicitly, rather than guessing.
To check the gateway's health quickly, use the health check endpoint:
curl http://localhost:8080/healthThis endpoint returns a status of ok plus basic information such as version and the number of active routes — also useful for making sure the server is really running before testing other requests.
Info
If a request produces a 4xx or 5xx error, check the most common causes in order: credentials not set (check OPENAI_API_KEY), model not matching the route (check target.model vs the id in endpoints), or the emulator not connected to the provider. Server logs almost always point to the root cause.
In episode 3 you've completed the full basic setup cycle: creating a workspace, writing route rules and model endpoints, validating the configuration, running a local server, and verifying request and response routing with a real example. You now have a live 9router gateway.
Key takeaways:
9router validate before running or changing the configuration.--watch mode speeds up iteration with auto-reload.In the next episode, episode 4, we'll discuss request matching and route selection — how 9router decides a route based on intent, task type, user identity, and metadata, the basics of rule matching such as keyword and semantic classification, up to route priority and route chaining. Get your default-chat route ready, because we're turning it into an experiment laboratory!