Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .idea/typed-api-spec.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

46 changes: 46 additions & 0 deletions docs/docs/01_overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
sidebar_position: 1
---

# Overview

typed-api-spec is an TypeScript based declarative API specification.

## Features

### TypeScript based API Specification
You can define your API specification using TypeScript.
No need to use yaml, json, or any other format.

```typescript
type Spec = DefineApiEndpoints<{
"/users": {
get: {
responses: { 200: { body: { userNames: string[] }; }; };
};
};
}>;
```

See [API Specification](./03_API%20Specification/specification) page for more details.

### Type-safe, zero-runtime API client
typed-api-spec provides strict typed [fetch](https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch).
For example, if you define API response schema by [API Specification](./03_API%20Specification/specification), you can get the typed response data.
It is just native [fetch](https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch), so:
- zero runtime: 0kb bundle size
- zero dependencies
- zero learning cost

```typescript
const fetchT = fetch as FetchT<"", Spec>;
const res = await fetchT("/users");
const data = await res.json(); // data is { userNames: string[] }
```

See [Client](./04_client/concept.md) page for more details.

### Server Framework & Validation library integration
typed-api-spec can be integrated with various server frameworks(like [Express](https://expressjs.com/), [Fastify](https://fastify.dev/)) and validation libraries(like [zod](https://zod.dev/), [Valibot](https://valibot.dev/)).

See [Server](/typed-api-spec/docs/category/server) and [Validation](/typed-api-spec/docs/category/validation) page for more details.
69 changes: 69 additions & 0 deletions docs/docs/02_getting-started.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
---
sidebar_position: 2
---

# Getting Started

Let's assume you have a npm project with code like this that __fetch__ from GitHub:

```typescript
const GITHUB_API_ORIGIN = "https://api.github.com";
const url = `/repos/mpppk/typed-api-spec/topics?page=1`;
const response = await fetch(`${GITHUB_API_ORIGIN}${url}`);
if (!response.ok) {
// response.json() returns any
const { message } = await response.json()
return console.error(message);
}
// response.json() returns any
const { names } = await response.json()
console.log(names); // => ["api-spec", "fetch", "typescript"]
```

## Installation

```bash
npm install @mpppk/typed-api-spec
```

## Define API Spec

```typescript
type Spec = DefineApiEndpoints<{
"/repos/:owner/:repo/topics": {
get: {
query: { page?: string };
responses: {
200: { body: { names: string[] }; };
400: { body: { message: string; }; };
};
};
};
}>;
```

## Add types to fetch function

```typescript
import { fetchT } from "@mpppk/typed-api-spec";
const fetch = fetch as FetchT<typeof GITHUB_API_ORIGIN, Spec>;
```

## Use fetch function

```typescript {3}
const GITHUB_API_ORIGIN = "https://api.github.com";
const url = `/repos/mpppk/typed-api-spec/topics?page=1`;
const fetchT = fetch as FetchT<typeof GITHUB_API_ORIGIN, Spec>;
const response = await fetchT(`${GITHUB_API_ORIGIN}${url}`);
if (!response.ok) {
// reponse.json() is typed as { message: string } because resnose is not ok
const { message } = await response.json()
return console.error(message);
}
// reponse.json() is typed as { names: string[] } because resnose is ok
const { names } = await response.json()
console.log(names); // => ["api-spec", "fetch", "typescript"]
```

Notice that only few (highlighted) lines have been changed from original, but now the __fetch__ is type-safe.
149 changes: 149 additions & 0 deletions docs/docs/03_api_specification.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
---
sidebar_position: 3
---

# API Specification

API specification is a definition of the API endpoint.
In typed-api-spec, API specification is written in TypeScript type and has the following structure:

```
Path --> Method --> Spec
```

- Path: The path of the API endpoint.
- Method: The HTTP method of the API endpoint.
- Spec: The specification of the API endpoint, which includes request parameters, response schema, etc.

For example, if you write the following code:

```typescript
type Spec = DefineApiEndpoints<{
// Path
"/users": {
// Method
get: {
// Spec
responses: { 200: { body: { userNames: string[] }; }; };
};
};
}>;
```

the above code defines:
- Path: `/users`
- Method: `get`
- Spec: `{ responses: { 200: { body: { userNames: string[] }; }; }; }`

## Structure

### Path

Path is the path of the API endpoint.
It can contain path parameters like `:id`.

```typescript

type Spec = DefineApiEndpoints<{
// Path has path parameter `:id`
"/users/:id": {
get: {
params: { id: string };
responses: { 200: { body: { user: { id: string; name: string }; }; }; };
};
};
}>;
```

### Method

Method is the HTTP method of the API endpoint.
It can be one of the following:
- `get`
- `post`
- `put`
- `delete`
- `patch`
- `head`
- `options`

```typescript
type Spec = DefineApiEndpoints<{
"/users": {
get: { responses: { 200: { body: { userNames: string[] }; }; }; };
post: { responses: { 200: { body: { userName: string }; }; }; };
};
}>;
```

### Spec

Spec has the following properties:
- `params`: The path parameters of the request.
- `query`: The query parameters of the request.
- `headers`: The headers of the request.
- `body`: The body of the request.
- `responses`: The response schema of the request.
- `body`: The body of the response.
- `headers`: The headers of the response.

```typescript
type Spec = DefineApiEndpoints<{
"/users/:id": {
get: {
params: { id: string };
query: { page?: string };
headers: { "x-api-key": string };
responses: { 200: {
headers: {"content-type": "application/json"};
body: { userNames: string[] }; };
};
};
};
}>;
```

## Validation library integration

typed-api-spec can be integrated with various validation libraries.
For example, you can use zod to define the schema of the request and response.

```typescript
import { z } from "zod";
import { ZodApiEndpoints } from "./index";

const Spec = {
"/users/:id": {
get: {
params: { id: z.string() },
query: { page: z.string().optional() },
headers: { "x-api-key": z.string() },
responses: {
200: {
headers: { "content-type": z.literal("application/json") },
body: { userNames: z.array(z.string()) },
}
},
},
},
} satisfies ZodApiEndpoints
```

For more information, see the [Validation](/typed-api-spec/docs/category/validation) page.

## API

### DefineApiEndpoints

DefineApiEndpoints is a utility type that defines the API specification.
If you write wrong API specification, DefineApiEndpoints will throw a type error.

```typescript
type Spec = DefineApiEndpoints<{
"/users": {
get: { responses: { 200: { body: { userNames: string[] }; }; }; };
};
}>;
```

TODO: Add StackBlitz demo
Loading