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

API

H1

The /src/apps directory is a “magic” directory where all the code inside it is automatically loaded as routes if it has an export named api. You can create any directory structure you need and write code freely inside it.

Let’s try a simple API that says hello to the world…?

/src/apps/hello-world/say.ts
/**
* This is an API that says hello to the world!
* These ~~comments~~ will be rendered in the **Cookbook**.
*/
export const api = defineApi({
meta: {},
async action(params: string & typia.tags.MinLength<2> & typia.tags.MaxLength<16>, context) {
return `Hello, world! (from ${params})`;
},
});

As you can see, defining an API is similar to writing a regular function! The action is the specific action performed by the API, which is your logic. You can return any content, whether it’s a string, object, or array, and they will be automatically serialized as JSON (to be precise, TSON). The exported name api is fixed, and only one API can be exported per file.

You might wonder, what is meta? And what are the purposes of the first parameter params and the second parameter context? These concepts will be explained below.

Meta

Allow me to keep you in suspense for now. After you become familiar with Milkio, you can read the Metadata section to learn more about it.

Params

The params determine the values that your API can accept. Interestingly, Milkio automatically uses Typia to ensure that the received values conform to the params type. Any input that doesn’t match the expected types of your method will be rejected, and any excess properties that match the type requirements will be removed. This is the “type-safe magic 🪄“.

For you, all you need to do is write pure TypeScript types, and you can freely use generics and any advanced TypeScript types.

/src/apps/hello-world/say.ts
action(
params: { id: string; gender: string; email: string; age: number; },
context
) {

Typia’s notable feature is its Type Tags, which allow you to further enhance your params types.

id: string & typia.tags.Format<"uuid">
gender: "male" | "female" | "other"
email: string & typia.tags.Format<"email">
age: number & typia.tags.ExclusiveMinimum<19> & typia.tags.Maximum<100>

Like this, limiting string length, checking email format, and more are all possible.

You might wonder how Typa achieves this “magic” when TypeScript types are completely removed when compiled to JavaScript. Typia analyzes your code and performs AOT (Ahead-of-Time) compilation, transforming the validation part of the code into proprietary code that suits your types. This makes writing params easier and improves runtime performance. You can read the AOT Compilation section of Typia to learn more details.

Typia’s AOT compilation relies on TSC, which does affect our choice of runtime and bundling tools. However, since Milkio has built-in support for Typia in the generation phase, you are free to choose any runtime and bundling tools you like without necessarily using TSC as your bundler.

Common Typia Type Tags

Matching a regular expression:

string & typia.tags.Pattern<"^[a-z]+$">

Limiting string length:

string & typia.tags.MaxLength<3> & typia.tags.MinLength<191>

Limiting the number of items in an array:

Array<string> & typia.tags.MinItems<3> & typia.tags.MaxItems<10>

Context

The context is an object that contains information and useful methods related to the current request. The context is different for each request.

/src/apps/hello-world/say.ts
action(
params: { ... },
context
) {

That sounds vague, but the most common thing we encounter regarding the context is probably context.logger, which is similar to console and allows us to log key information during code execution for debugging purposes. To write a log for the current request, you can simply do:

context.logger.log("Hello, Milkio!");

The logger is used in a similar way to console, and you can read about it in the Logger section.

Headers

In the context, you can also access headers. Headers are a way for clients, browsers, and cloud service providers to pass additional data to us.

For example, a browser can tell us the user’s operating system, and Vercel can tell us the city where the user is located. Additionally, we often pass user tokens and other information through headers to the server.

export const api = defineApi({
meta: {},
async action(params: undefined, context) {
const city = context.headers.get("X-Vercel-IP-City");
return `You might be in this city: ${city}`;
},
});

Note that the headers demonstrated above are not regular HTTP headers; they are sent by Vercel. Therefore, unless your code is deployed on Vercel, you may not be able to run this code correctly.

URL

In the context’s detail, we can also access the URL object of the current request.

Note that the data in detail only exists for HTTP calls. This means that when running tests, these values will not be available.

export const api = defineApi({
meta: {},
async action(params: undefined, context) {
const fullUrl = context.detail.fullUrl!;
return `Your full URL: ${fullUrl.toString()} Your path: ${fullUrl.pathname}`;
},
});

URL Params

We can also access URL parameters, which are the strings after the ? in the URL, like in the example below:

Terminal window
http://example.com/foo?name=bar&topic=api
export const api = defineApi({
meta: {},
async action(params: undefined, context) {
const fullUrl = context.detail.fullUrl!;
return `Name obtained from URL Params: ${fullUrl.searchParams.get("name")}`; // bar
},
});

More

In addition to the above, the context has many other uses. After you become familiar with Milkio, you can read the Context section to learn more about it.