Skip to content
当前有符合你浏览器所设置语言的版本,是否前往zh-CN版本的网站?
There is a version suitable for your browser's language settings. Would you like to go to the zh-CN language of the site?
HomeDocument

Getting Started

H1

Every Milkio project comes equipped with a dedicated client. Other projects developed using TypeScript can easily call your Milkio project through this client, enjoying robust type checking, autocompletion, and the convenience of types serving as documentation.

const result = await client.execute("hello-world/say", { params: "🥛 Milkio" });

Let’s Get Started!

Assuming you already have a project that wants to call Milkio, whether it’s Vue, React, Electron, React Native, Mini Program, or any other project, as long as they support the TypeScript language. If you don’t have one yet, we recommend trying out Vite to kickstart your project.

Once you’re ready, imagine your project and Milkio project are located in the same directory, with a directory structure roughly like this:

Terminal window
workspace
├─ a-milkio
├─ ...
└─ package.json
└─ a-web-app
├─ ...
└─ package.json

Now, open VS Code where your web project is located, and add your Milkio project by clicking File -> Add Folder to Workspace.... This way, you can work with both projects simultaneously in VS Code.

In VS Code, select Terminal -> New Terminal, navigate to your web project directory in the terminal, and install the client package from your Milkio project:

Terminal window
npm install ../a-milkio/packages/client

In your web project, choose a location you like, create a client.ts file, and write the following code:

import { createClient } from "client";
export const client = createClient({
baseUrl: "http://localhost:9000/"
});

Now, let’s try executing your Milkio project through the client! UwU

const result = await client.execute("hello-world/say", { params: "🥛 Milkio" });
console.log(result);

Handling Failures

Since network connections are not always stable and requests don’t always go smoothly, we must consider how to handle failed requests.

For failed requests, Milkio notifies you of the failure reason through a return value rather than throwing an exception (similar to Go language). Throwing exceptions can inadvertently lead us to overlook exception handling, causing the code to stop unexpectedly in unforeseen places. We should show the user the reason for failure (e.g., display an alert box) and restore some variable settings when a request fails.

const result = await client.execute("hello-world/say", { params: "🥛 Milkio" });
if (!result.success) return alert(result.fail.message);
console.log("You successfully said: ", result.data);

By using union types, Milkio ensures that you must correctly extract result.data only after confirming the success of the request, as shown in the code above. This helps maintain the accuracy of our application, even though our requests may always be successful, the user’s network connection may not always be good. This is similar to common practices in Go:

// golang
if err != nil { return err }
fmt.Println("Hello, World!")

User-Friendly Failure Messages

Failure messages are meant to explain the reason for failure, but not all messages are user-friendly enough. For example, if a user’s password is too short, causing a TYPE_SAFE_ERROR failure, we may not want to display:

Parameter Error: The current value is '$input.user.password', which does not meet 'string & tags.MinLength<8>' requirements

Instead, we might want the user to see a friendlier prompt:

Password is too short, try adding more than 8 characters

When a failure occurs in Milkio, in addition to returning a message, it also returns data related to the failure, which is the first parameter of each method in /src/fail-code.ts in the Milkio project.

We can utilize this data for more detailed failure handling. For example:

const result = await client.execute(...);
if (!result.success) {
if (result.fail.code === "TYPE_SAFE_ERROR" && result.fail.data.path === "$input.password") return alert("Password is too long or too short");
if (result.fail.code === "TYPE_SAFE_ERROR" && result.fail.data.path === "$input.username") return alert("Username does not meet the criteria, should not contain Chinese characters or symbols");
return alert(result.fail.message); // For messages other than custom type-safe errors
}

For TYPE_SAFE_ERROR, you can access the following information for judgment:

NamePurposeExample
pathThe path where the type matching failed, always starting with $input$input.password
expectedThe expected typestring & tags.MinLength<8>
valueThe actual value received123456

Streams

If you need to access APIs that provide streams as results, you should use the executeStream method. More details can be found in the Stream section.

Non-Browser Environment Usage

We assume your code runs in the browser, hence it uses localStorage, fetch, and AbortController. If you wish to run it in a different server environment (e.g., calling one Milkio from another), ensure your environment provides these functionalities.

Typically, localStorage needs customization because in a server-side environment, we might want data stored in a database or elsewhere. You can customize it by passing a storage option:

export const client = createClient({
baseUrl: "http://localhost:9000",
storage: {
async getItem(key: string) {
// ...
},
async setItem(key, value) {
// ...
},
async removeItem(key) {
// ...
}
}
});

The methods in storage can be asynchronous, making it convenient to integrate with database or other data storage solutions.

If your code environment lacks fetch and AbortController (supported in environments like Bun, Cloudflare Worker, etc.), you can customize them by passing fetch and AbortController options:

export const client = createClient({
baseUrl: "http://localhost:9000",
fetch: fetchPolyfill,
abort: abortControllerPolyfill,
});

If you don’t use Stream, you can omit the abort option and provide only fetch.