├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── code-review.yml │ └── deploy.yml ├── .gitignore ├── README.md ├── docs ├── .gitignore ├── .vitepress │ ├── cache │ │ └── deps │ │ │ ├── _metadata.json │ │ │ ├── package.json │ │ │ ├── vue.js │ │ │ └── vue.js.map │ ├── config.ts │ └── theme │ │ ├── custom.css │ │ └── index.ts ├── api-examples.md ├── assets │ ├── icons │ │ ├── ts-logo-128.png │ │ └── ts-logo-round-128.png │ └── images │ │ ├── api-test-1.png │ │ ├── api-test-2.png │ │ ├── api-test-3.png │ │ ├── authorization-popup.png │ │ ├── authorization-request.png │ │ ├── authorize-button.png │ │ ├── crud-swagger-ui.png │ │ ├── file-download.png │ │ ├── file-upload.png │ │ ├── getting-started-swagger-ui-1.png │ │ └── getting-started-swagger-ui-2.png ├── guide │ ├── api-testing.md │ ├── authentication.md │ ├── cli.md │ ├── crud-api-example.md │ ├── defining-api-spec.md │ ├── describing-schema.md │ ├── express-integration.md │ ├── file-upload-download.md │ ├── generating-document.md │ ├── getting-started.md │ ├── index.md │ ├── troubleshooting.md │ └── tspec-config.md ├── index.md ├── introduction.md ├── markdown-examples.md ├── package.json └── yarn.lock ├── examples ├── book-advanced │ ├── .gitignore │ ├── package.json │ ├── src │ │ ├── index.ts │ │ └── openapi.json │ ├── tsconfig.json │ ├── tspec.config.json │ └── yarn.lock ├── file-upload-download-example │ ├── openapi.json │ ├── package.json │ ├── src │ │ └── index.ts │ ├── tsconfig.json │ └── yarn.lock ├── tspec-basic-example │ ├── index.ts │ ├── openapi.json │ ├── package.json │ ├── tsconfig.json │ └── yarn.lock └── tspec-express-example │ ├── index.ts │ ├── openapi.json │ ├── package.json │ ├── tsconfig.json │ └── yarn.lock └── packages └── tspec ├── .eslintrc.js ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── package.json ├── rollup.config.ts ├── src ├── cli │ └── index.ts ├── generator │ ├── config.ts │ ├── index.ts │ ├── openapiGenerator.ts │ ├── openapiSchemaConverter.ts │ ├── schemaParser.ts │ └── types.ts ├── index.ts ├── server │ └── index.ts ├── types │ ├── index.ts │ ├── json-schema-to-openapi-schema.d.ts │ └── tspec.ts └── utils │ ├── merge.ts │ └── types.ts ├── tsconfig.json └── yarn.lock /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/code-review.yml: -------------------------------------------------------------------------------- 1 | name: Code Review 2 | permissions: 3 | contents: read 4 | pull-requests: write 5 | on: 6 | pull_request: 7 | types: 8 | - opened 9 | - reopened 10 | - synchronize 11 | jobs: 12 | code-review: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: anc95/ChatGPT-CodeReview@main 16 | env: 17 | GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" 18 | OPENAI_API_KEY: "${{ secrets.OPENAI_API_KEY }}" 19 | LANGUAGE: English 20 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy 2 | on: 3 | push: 4 | branches: 5 | - main 6 | jobs: 7 | deploy: 8 | runs-on: ubuntu-latest 9 | defaults: 10 | run: 11 | working-directory: docs 12 | permissions: 13 | pages: write 14 | id-token: write 15 | environment: 16 | name: github-pages 17 | url: ${{ steps.deployment.outputs.page_url }} 18 | steps: 19 | - uses: actions/checkout@v3 20 | with: 21 | fetch-depth: 0 22 | - name: Install dependencies 23 | run: yarn --frozen-lockfile --silent 24 | - name: Build 25 | run: yarn docs:build 26 | - uses: actions/configure-pages@v3 27 | - uses: actions/upload-pages-artifact@v1 28 | with: 29 | path: docs/.vitepress/dist 30 | - name: Deploy 31 | id: deployment 32 | uses: actions/deploy-pages@v2 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .idea 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | packages/tspec/README.md -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | 3 | # Logs 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # ESLint 9 | .eslintcache 10 | 11 | # Node 12 | dist/ 13 | -------------------------------------------------------------------------------- /docs/.vitepress/cache/deps/_metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "hash": "0e3be5e7", 3 | "browserHash": "01d499ed", 4 | "optimized": { 5 | "vue": { 6 | "src": "../../../node_modules/vue/dist/vue.runtime.esm-bundler.js", 7 | "file": "vue.js", 8 | "fileHash": "ef88558e", 9 | "needsInterop": false 10 | } 11 | }, 12 | "chunks": {} 13 | } -------------------------------------------------------------------------------- /docs/.vitepress/cache/deps/package.json: -------------------------------------------------------------------------------- 1 | {"type":"module"} -------------------------------------------------------------------------------- /docs/.vitepress/config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitepress' 2 | 3 | // https://vitepress.dev/reference/site-config 4 | export default defineConfig({ 5 | base: '/tspec/', // Reference: https://vitepress.dev/guide/deploy#github-pages 6 | title: "Tspec", 7 | description: "Auto-generating OpenAPI Document with TypeScript Types", 8 | lastUpdated: true, 9 | cleanUrls: true, 10 | themeConfig: { 11 | // https://vitepress.dev/reference/default-theme-config 12 | nav: [ 13 | { text: 'Home', link: '/' }, 14 | { text: 'Guide', link: '/guide/getting-started' } 15 | ], 16 | 17 | sidebar: [ 18 | { 19 | text: 'Introduction', link: '/introduction' 20 | }, 21 | { 22 | text: 'Guides', 23 | items: [ 24 | { text: 'Getting Started', link: '/guide/getting-started' }, 25 | { text: 'Generating Document', link: '/guide/generating-document' }, 26 | { text: 'Describing Schema', link: '/guide/describing-schema' }, 27 | { text: 'Defining API Spec', link: '/guide/defining-api-spec' }, 28 | { text: 'CRUD API Example', link: '/guide/crud-api-example' }, 29 | { text: 'Express Integration', link: '/guide/express-integration' }, // with Request validation 30 | { text: 'API Testing', link: '/guide/api-testing' }, 31 | { text: 'Authentication', link: '/guide/authentication' }, 32 | { text: 'File Upload/Download', link: '/guide/file-upload-download' }, 33 | ] 34 | }, 35 | { 36 | text: 'Reference', 37 | items: [ 38 | { text: 'Troubleshooting', link: '/guide/troubleshooting' }, 39 | // { text: 'CLI', link: '/guide/cli' }, 40 | // { text: 'tspec.config Options', link: '/guide/tspec-config' }, 41 | ] 42 | }, 43 | ], 44 | 45 | socialLinks: [ 46 | { icon: 'github', link: 'https://github.com/ts-spec/tspec' } 47 | ], 48 | 49 | search: { 50 | provider: 'local', 51 | }, 52 | } 53 | }) 54 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/custom.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --vp-home-hero-name-color: transparent; 3 | --vp-home-hero-name-background: -webkit-linear-gradient(120deg, #bd34fe, #41d1ff); 4 | /* --vp-home-hero-name-background: -webkit-linear-gradient(120deg, #42E595, #3BB2B8); */ 5 | /* --vp-home-hero-name-background: -webkit-linear-gradient(120deg, #85C48D, #2BB6C7); */ 6 | } 7 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/index.ts: -------------------------------------------------------------------------------- 1 | import DefaultTheme from 'vitepress/theme' 2 | import './custom.css' 3 | 4 | export default DefaultTheme 5 | -------------------------------------------------------------------------------- /docs/api-examples.md: -------------------------------------------------------------------------------- 1 | --- 2 | outline: deep 3 | --- 4 | 5 | # Runtime API Examples 6 | 7 | This page demonstrates usage of some of the runtime APIs provided by VitePress. 8 | 9 | The main `useData()` API can be used to access site, theme, and page data for the current page. It works in both `.md` and `.vue` files: 10 | 11 | ```md 12 | 17 | 18 | ## Results 19 | 20 | ### Theme Data 21 |
{{ theme }}22 | 23 | ### Page Data 24 |
{{ page }}25 | 26 | ### Page Frontmatter 27 |
{{ frontmatter }}28 | ``` 29 | 30 | 35 | 36 | ## Results 37 | 38 | ### Theme Data 39 |
{{ theme }}40 | 41 | ### Page Data 42 |
{{ page }}43 | 44 | ### Page Frontmatter 45 |
{{ frontmatter }}46 | 47 | ## More 48 | 49 | Check out the documentation for the [full list of runtime APIs](https://vitepress.dev/reference/runtime-api#usedata). 50 | -------------------------------------------------------------------------------- /docs/assets/icons/ts-logo-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ts-spec/tspec/00bc48de816c90cae1aa65aaaf0a35bd06ff14ae/docs/assets/icons/ts-logo-128.png -------------------------------------------------------------------------------- /docs/assets/icons/ts-logo-round-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ts-spec/tspec/00bc48de816c90cae1aa65aaaf0a35bd06ff14ae/docs/assets/icons/ts-logo-round-128.png -------------------------------------------------------------------------------- /docs/assets/images/api-test-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ts-spec/tspec/00bc48de816c90cae1aa65aaaf0a35bd06ff14ae/docs/assets/images/api-test-1.png -------------------------------------------------------------------------------- /docs/assets/images/api-test-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ts-spec/tspec/00bc48de816c90cae1aa65aaaf0a35bd06ff14ae/docs/assets/images/api-test-2.png -------------------------------------------------------------------------------- /docs/assets/images/api-test-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ts-spec/tspec/00bc48de816c90cae1aa65aaaf0a35bd06ff14ae/docs/assets/images/api-test-3.png -------------------------------------------------------------------------------- /docs/assets/images/authorization-popup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ts-spec/tspec/00bc48de816c90cae1aa65aaaf0a35bd06ff14ae/docs/assets/images/authorization-popup.png -------------------------------------------------------------------------------- /docs/assets/images/authorization-request.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ts-spec/tspec/00bc48de816c90cae1aa65aaaf0a35bd06ff14ae/docs/assets/images/authorization-request.png -------------------------------------------------------------------------------- /docs/assets/images/authorize-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ts-spec/tspec/00bc48de816c90cae1aa65aaaf0a35bd06ff14ae/docs/assets/images/authorize-button.png -------------------------------------------------------------------------------- /docs/assets/images/crud-swagger-ui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ts-spec/tspec/00bc48de816c90cae1aa65aaaf0a35bd06ff14ae/docs/assets/images/crud-swagger-ui.png -------------------------------------------------------------------------------- /docs/assets/images/file-download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ts-spec/tspec/00bc48de816c90cae1aa65aaaf0a35bd06ff14ae/docs/assets/images/file-download.png -------------------------------------------------------------------------------- /docs/assets/images/file-upload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ts-spec/tspec/00bc48de816c90cae1aa65aaaf0a35bd06ff14ae/docs/assets/images/file-upload.png -------------------------------------------------------------------------------- /docs/assets/images/getting-started-swagger-ui-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ts-spec/tspec/00bc48de816c90cae1aa65aaaf0a35bd06ff14ae/docs/assets/images/getting-started-swagger-ui-1.png -------------------------------------------------------------------------------- /docs/assets/images/getting-started-swagger-ui-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ts-spec/tspec/00bc48de816c90cae1aa65aaaf0a35bd06ff14ae/docs/assets/images/getting-started-swagger-ui-2.png -------------------------------------------------------------------------------- /docs/guide/api-testing.md: -------------------------------------------------------------------------------- 1 | # API Testing 2 | 3 | Tspec provides a way to test your API using [Swagger UI](https://swagger.io/tools/swagger-ui/). 4 | 5 | ## Setup 6 | 7 | Let's assume that you have the following Express application: 8 | 9 | ::: code-group 10 | ```ts[index.ts] 11 | import express, { RequestHandler } from "express"; 12 | import { Tspec, TspecDocsMiddleware } from "tspec"; 13 | 14 | interface Book { 15 | id: number; 16 | title: string; 17 | } 18 | 19 | const getBookById: RequestHandler<{ id: string }, Book> = (req, res) => { 20 | res.json({ 21 | id: +req.params.id, 22 | title: 'Under the Oak Tree', 23 | }) 24 | } 25 | 26 | export type BookApiSpec = Tspec.DefineApiSpec<{ 27 | tags: ['Book'], 28 | paths: { 29 | '/books/{id}': { 30 | get: { 31 | summary: 'Get book by id', 32 | handler: typeof getBookById 33 | }, 34 | }, 35 | } 36 | }>; 37 | 38 | const initServer = async () => { 39 | const app = express(); 40 | app.get('/books/:id', getBookById); 41 | app.use('/docs', await TspecDocsMiddleware()); 42 | app.listen(3000, () => { 43 | console.log(`Tspec docs is running on http://localhost:3000/docs`); 44 | }); 45 | } 46 | initServer(); 47 | ``` 48 | ::: 49 | 50 | Run the following command to start the server: 51 | 52 | ```bash 53 | ts-node index.ts 54 | ``` 55 | 56 | Then, you can access Swagger UI at `http://localhost:3000/docs` 57 | 58 | 59 | ## Try it out 60 | 61 | Now, you can try out the API in Swagger UI. 62 | 63 | The `Try it out` button is located at the top right of the API section. 64 | 65 |  66 | 67 | 68 | Click the `Try it out` button and enter the `id` parameter. 69 | 70 |  71 | 72 | Then, click the `Execute` button to send the request. 73 | 74 | You can see the response in the `Response body` section. 75 | 76 |  77 | -------------------------------------------------------------------------------- /docs/guide/authentication.md: -------------------------------------------------------------------------------- 1 | # Authentication 2 | 3 | Tspec supports the authentication in the [Swagger UI](https://swagger.io/tools/swagger-ui/). 4 | 5 | Swagger UI provides the `Authorize` button to authenticate the user. 6 | 7 | On this page, we will learn how to use authentication in Tspec. 8 | 9 | ## Defining security scheme 10 | 11 | To use authentication in Tspec, you need to define `securityDefinitions` in the `Tspec.GenerateParams`. 12 | 13 | The `securityDefinitions` is same as the [securitySchemes](https://swagger.io/docs/specification/authentication/) in the OpenAPI spec. 14 | 15 | ::: code-group 16 | ```ts[index.ts]{3} 17 | const tspecParams: Tspec.GenerateParams = { 18 | openapi: { 19 | securityDefinitions: { 20 | jwt: { 21 | type: 'http', 22 | scheme: 'bearer', 23 | bearerFormat: 'JWT', 24 | }, 25 | }, 26 | }, 27 | }; 28 | ``` 29 | ::: 30 | 31 | Then, you can use the `security` property in the `Tspec.DefineApiSpec` to specify the security scheme for the API: 32 | 33 | ::: code-group 34 | ```ts[index.ts]{2} 35 | export type UserApiSpec = Tspec.DefineApiSpec<{ 36 | security: 'jwt', 37 | paths: { 38 | '/my': { 39 | get: { 40 | summary: 'Get my info', 41 | handler: typeof getMyInfo 42 | }, 43 | }, 44 | } 45 | }>; 46 | ``` 47 | ::: 48 | 49 | And that's it! Now, you can see the `Authorize` button in the Swagger UI: 50 | 51 |  52 | 53 | If you click the button, you can see the `Authorization` popup: 54 | 55 |  56 | 57 | You can enter the JWT token in the popup and click the `Authorize` button to authenticate the user. 58 | 59 | And if you send the request, you can see the `Authorization` header in the request: 60 | 61 |  62 | -------------------------------------------------------------------------------- /docs/guide/cli.md: -------------------------------------------------------------------------------- 1 | --- 2 | outline: deep 3 | --- 4 | # Command Line Interface 5 | 6 | ## Generation 7 | 8 | ### **`generate`** 9 | 10 | Generate OpenAPI schema from TypeScript types. 11 | 12 | #### Usage 13 | 14 | ```sh 15 | generate [options] 16 | ``` 17 | 18 | #### Options 19 | 20 | |Option|Type|Description|Example| 21 | |-|-|-|-| 22 | |`--specPathGlobs [path]`|string[]|Path of TypeScript files which you want to generate OpenAPI schema|`src/**/*.ts`| 23 | |`--tsconfigPath [path]`|string|Path of tsconfig|`./tsconfig.json`| 24 | |`--outputPath [path]`|string|Path of output OpenAPI schema|`./generate/openapi.yaml`| 25 | |`--specVersion [version]`|number|Version to use for OpenAPI schema (Currently supports only 3)|`3`| 26 | |`--openapiTitle [title]`|string|`title` property of OpenAPI schema|`Tspec`| 27 | |`--openapiVersion [version]`|string|`version` property of OpenAPI schema|`1.0.0`| 28 | |`--debug [true / false]`|boolean|Print debug information for Tspec|`false`| 29 | |`--ignoreErrors [true / false]`|boolean|Whether ignore errors in Tspec or not|`false`| 30 | 31 | ## Server 32 | 33 | ### **`server`** 34 | 35 | Start Tspec server for display OpenAPI schema with Swagger. 36 | 37 | #### Usage 38 | 39 | ```sh 40 | server [options] 41 | ``` 42 | 43 | #### Options 44 | 45 | **You can also use the CLI options for [`generate`](#generate) command.** 46 | 47 | |Option|Type|Description|Example| 48 | |-|-|-|-| 49 | |`--port [port]`|number|Specify port number for Tspec server|`7080`| 50 | |`--proxyHost [host]`|string|Specify proxy host for Tspec server|`https://tspec.org`| 51 | -------------------------------------------------------------------------------- /docs/guide/crud-api-example.md: -------------------------------------------------------------------------------- 1 | # CRUD Example 2 | 3 | Let's define a CRUD API spec for `Author`: 4 | 5 | ::: code-group 6 | ```ts[index.ts] 7 | import { Tspec } from 'tspec'; 8 | 9 | interface Author { 10 | id: number; 11 | name: string; 12 | } 13 | 14 | export type AuthorApiSpec = Tspec.DefineApiSpec<{ 15 | basePath: '/authors', 16 | tags: ['Author'], 17 | paths: { 18 | '/': { 19 | get: { 20 | summary: 'List of authors', 21 | responses: { 22 | 200: Author[], 23 | }, 24 | }, 25 | post: { 26 | summary: 'Create an author', 27 | requestBody: Author, 28 | responses: { 29 | 201: Author, 30 | }, 31 | }, 32 | }, 33 | '/{id}': { 34 | get: { 35 | summary: 'Get author by id', 36 | path: { id: number }, 37 | responses: { 38 | 200: Author, 39 | }, 40 | }, 41 | put: { 42 | summary: 'Update an author', 43 | path: { id: number }, 44 | requestBody: Author, 45 | responses: { 46 | 200: Author, 47 | }, 48 | }, 49 | delete: { 50 | summary: 'Delete an author', 51 | path: { id: number }, 52 | responses: { 53 | 204: void, 54 | }, 55 | }, 56 | }, 57 | } 58 | }>; 59 | ``` 60 | ::: 61 | 62 | :::details Generated OpenAPI Spec 63 | ```yaml 64 | paths: 65 | "/authors/": 66 | get: 67 | operationId: AuthorApiSpec_get_/ 68 | tags: 69 | - Author 70 | summary: List of authors 71 | responses: 72 | '200': 73 | content: 74 | application/json: 75 | schema: 76 | type: array 77 | items: 78 | "$ref": "#/components/schemas/Author" 79 | post: 80 | operationId: AuthorApiSpec_post_/ 81 | tags: 82 | - Author 83 | summary: Create an author 84 | responses: 85 | '201': 86 | content: 87 | application/json: 88 | schema: 89 | "$ref": "#/components/schemas/Author" 90 | "/authors/{id}": 91 | get: 92 | operationId: AuthorApiSpec_get_/{id} 93 | tags: 94 | - Author 95 | summary: Get author by id 96 | parameters: 97 | - name: id 98 | in: path 99 | required: true 100 | schema: 101 | type: number 102 | responses: 103 | '200': 104 | content: 105 | application/json: 106 | schema: 107 | "$ref": "#/components/schemas/Author" 108 | put: 109 | operationId: AuthorApiSpec_put_/{id} 110 | tags: 111 | - Author 112 | summary: Update an author 113 | parameters: 114 | - name: id 115 | in: path 116 | required: true 117 | schema: 118 | type: number 119 | responses: 120 | '200': 121 | content: 122 | application/json: 123 | schema: 124 | "$ref": "#/components/schemas/Author" 125 | delete: 126 | operationId: AuthorApiSpec_delete_/{id} 127 | tags: 128 | - Author 129 | summary: Delete an author 130 | parameters: 131 | - name: id 132 | in: path 133 | required: true 134 | schema: 135 | type: number 136 | responses: 137 | '204': 138 | content: 139 | application/json: 140 | schema: 141 | type: undefined 142 | ``` 143 | ::: 144 | 145 | The generated Swagger UI looks like this: 146 | 147 |  148 | -------------------------------------------------------------------------------- /docs/guide/defining-api-spec.md: -------------------------------------------------------------------------------- 1 | --- 2 | outline: deep 3 | --- 4 | # Defining API Spec 5 | You can easily define API specifications by passing types. To build an Open API schema, simply pass the necessary information as a generic type parameter of `Tspec.DefineApiSpec`. 6 | 7 | 8 | ## Basic Usage 9 | 10 | Let's define a simple `AuthorApiSpec`: 11 | 12 | ```ts{8} 13 | import { Tspec } from 'tspec'; 14 | 15 | interface Author { 16 | id: number; 17 | name: string; 18 | } 19 | 20 | export type AuthorApiSpec = Tspec.DefineApiSpec<{ 21 | paths: { 22 | '/authors': { 23 | get: { 24 | summary: 'List of authors', 25 | description: 'Returns a single author', 26 | responses: { 27 | 200: Author[], 28 | }, 29 | }, 30 | }, 31 | } 32 | }>; 33 | ``` 34 | 35 | :::details Generated OpenAPI Spec 36 | 37 | The paths and schemas in the generated OpenAPI Spec are described as follows: 38 | 39 | ```yaml{2} 40 | paths: 41 | "/authors": 42 | get: 43 | operationId: AuthorApiSpec_get_/authors 44 | summary: List of authors 45 | description: Returns a single author 46 | responses: 47 | '200': 48 | content: 49 | application/json: 50 | schema: 51 | type: array 52 | items: 53 | "$ref": "#/components/schemas/Author" 54 | components: 55 | schemas: 56 | Author: 57 | type: object 58 | properties: 59 | id: 60 | type: number 61 | name: 62 | type: string 63 | additionalProperties: false 64 | required: 65 | - id 66 | - name 67 | ``` 68 | ::: 69 | 70 | 71 | ## Parameters 72 | ### Path Parameters 73 | 74 | You can define path parameters by using the `path` property of the operation type. 75 | 76 | ::: code-group 77 | 78 | ```ts[Tspec]{6} 79 | export type AuthorApiSpec = Tspec.DefineApiSpec<{ 80 | paths: { 81 | '/authors/{id}': { 82 | get: { 83 | summary: 'Get author by id', 84 | path: { id: number }, 85 | responses: { 86 | 200: Author, 87 | }, 88 | }, 89 | }, 90 | } 91 | }>; 92 | ``` 93 | ::: 94 | 95 | :::details Generated OpenAPI Spec 96 | ```yaml{6-11} 97 | paths: 98 | "/authors/{id}": 99 | get: 100 | operationId: AuthorApiSpec_get_/authors/{id} 101 | summary: Get author by id 102 | parameters: 103 | - name: id 104 | in: path 105 | required: true 106 | schema: 107 | type: number 108 | responses: 109 | '200': 110 | content: 111 | application/json: 112 | schema: 113 | "$ref": "#/components/schemas/Author" 114 | ``` 115 | ::: 116 | 117 | 118 | Possible path parameter types are as follows: 119 | 120 | - `string` 121 | - `number` 122 | 123 | 124 | ### Query Parameters 125 | 126 | You can define query parameters by using the `query` property of the operation type. 127 | 128 | ::: code-group 129 | ```ts[Tspec]{6-9} 130 | export type AuthorApiSpec = Tspec.DefineApiSpec<{ 131 | paths: { 132 | '/authors': { 133 | get: { 134 | summary: 'List of authors', 135 | query: { 136 | limit: number, 137 | offset: number, 138 | }, 139 | responses: { 140 | 200: Author[], 141 | }, 142 | }, 143 | }, 144 | } 145 | }>; 146 | ``` 147 | ::: 148 | 149 | :::details Generated OpenAPI Spec 150 | ```yaml{6-14} 151 | paths: 152 | "/authors": 153 | get: 154 | operationId: AuthorApiSpec_get_/authors 155 | summary: List of authors 156 | parameters: 157 | - name: limit 158 | in: query 159 | schema: 160 | type: number 161 | - name: offset 162 | in: query 163 | schema: 164 | type: number 165 | responses: 166 | '200': 167 | content: 168 | application/json: 169 | schema: 170 | type: array 171 | items: 172 | "$ref": "#/components/schemas/Author" 173 | ``` 174 | ::: 175 | 176 | Possible query parameter types are as follows: 177 | 178 | - `string` 179 | - `number` 180 | - `boolean` 181 | - `string[]` 182 | - `number[]` 183 | - `boolean[]` 184 | 185 | ### Header and Cookie Parameters 186 | 187 | You can define header and cookie parameters by using the `header` and `cookie` properties of the operation type. 188 | 189 | ::: code-group 190 | ```ts[Tspec]{6-11} 191 | export type AuthorApiSpec = Tspec.DefineApiSpec<{ 192 | paths: { 193 | '/authors': { 194 | get: { 195 | summary: 'List of authors', 196 | header: { 197 | 'x-api-key': string, 198 | }, 199 | cookie: { 200 | 'debug': 0 | 1, 201 | }, 202 | responses: { 203 | 200: Author[], 204 | }, 205 | }, 206 | }, 207 | } 208 | }>; 209 | ``` 210 | ::: 211 | 212 | :::details Generated OpenAPI Spec 213 | ```yaml{7-18} 214 | paths: 215 | "/authors": 216 | get: 217 | operationId: AuthorApiSpec_get_/authors 218 | summary: List of authors 219 | parameters: 220 | - name: x-api-key 221 | in: header 222 | schema: 223 | type: string 224 | required: true 225 | - name: debug 226 | in: cookie 227 | schema: 228 | enum: 229 | - 0 230 | - 1 231 | required: true 232 | responses: 233 | '200': 234 | content: 235 | application/json: 236 | schema: 237 | type: array 238 | items: 239 | "$ref": "#/components/schemas/Author" 240 | ``` 241 | ::: 242 | 243 | Possible header and cookie parameter types are as follows: 244 | - `string` 245 | - `number` 246 | 247 | 248 | ### Request Body 249 | 250 | You can define request body by using the `body` property of the operation type. 251 | 252 | ::: code-group 253 | ```ts[Tspec]{6} 254 | export type AuthorApiSpec = Tspec.DefineApiSpec<{ 255 | paths: { 256 | '/authors': { 257 | post: { 258 | summary: 'Create an author', 259 | body: Author, 260 | responses: { 261 | 201: Author, 262 | }, 263 | }, 264 | }, 265 | } 266 | }>; 267 | ``` 268 | ::: 269 | 270 | :::details Generated OpenAPI Spec 271 | ```yaml{6-11} 272 | paths: 273 | "/authors": 274 | post: 275 | operationId: AuthorApiSpec_post_/authors 276 | summary: Create an author 277 | requestBody: 278 | required: true 279 | content: 280 | application/json: 281 | schema: 282 | "$ref": "#/components/schemas/Author" 283 | responses: 284 | '201': 285 | content: 286 | application/json: 287 | schema: 288 | "$ref": "#/components/schemas/Author" 289 | ``` 290 | ::: 291 | 292 | 293 | Body parameter types can be any object type. 294 | 295 | ## Base Path 296 | 297 | You can define the base path by using the `basePath` property of the API spec type. 298 | 299 | ::: code-group 300 | ```ts[Tspec]{2,4,12} 301 | export type AuthorApiSpec = Tspec.DefineApiSpec<{ 302 | basePath: '/authors', 303 | paths: { 304 | '/': { 305 | get: { 306 | summary: 'List of authors', 307 | responses: { 308 | 200: Author[], 309 | }, 310 | }, 311 | }, 312 | '/{id}': { 313 | get: { 314 | summary: 'Get author by id', 315 | path: { id: number }, 316 | responses: { 317 | 200: Author, 318 | }, 319 | }, 320 | }, 321 | } 322 | }>; 323 | ``` 324 | ::: 325 | 326 | :::details Generated OpenAPI Spec 327 | ```yaml{2,14} 328 | paths: 329 | "/authors": 330 | get: 331 | operationId: AuthorApiSpec_get_/authors 332 | summary: List of authors 333 | responses: 334 | '200': 335 | content: 336 | application/json: 337 | schema: 338 | type: array 339 | items: 340 | "$ref": "#/components/schemas/Author" 341 | "/authors/{id}": 342 | get: 343 | operationId: AuthorApiSpec_get_/authors/{id} 344 | summary: Get author by id 345 | parameters: 346 | - name: id 347 | in: path 348 | required: true 349 | schema: 350 | type: number 351 | responses: 352 | '200': 353 | content: 354 | application/json: 355 | schema: 356 | "$ref": "#/components/schemas/Author" 357 | ``` 358 | ::: 359 | 360 | ## Tags 361 | 362 | You can define tags by using the `tags` property of the API spec type. 363 | 364 | ::: code-group 365 | ```ts[Tspec]{2} 366 | export type AuthorApiSpec = Tspec.DefineApiSpec<{ 367 | tags: ['Author'], 368 | paths: { 369 | '/authors': { 370 | get: { 371 | summary: 'List of authors', 372 | responses: { 373 | 200: Author[], 374 | }, 375 | }, 376 | }, 377 | } 378 | }>; 379 | ``` 380 | ::: 381 | 382 | :::details Generated OpenAPI Spec 383 | ```yaml{5-6} 384 | paths: 385 | "/authors": 386 | get: 387 | operationId: AuthorApiSpec_get_/authors 388 | tags: 389 | - Author 390 | summary: List of authors 391 | responses: 392 | '200': 393 | content: 394 | application/json: 395 | schema: 396 | type: array 397 | items: 398 | "$ref": "#/components/schemas/Author" 399 | ``` 400 | ::: 401 | 402 | If you want to define tags for each operation, you can use the `tags` property of the operation type. 403 | 404 | ::: code-group 405 | ```ts[Tspec]{6} 406 | export type AuthorApiSpec = Tspec.DefineApiSpec<{ 407 | paths: { 408 | '/authors': { 409 | get: { 410 | summary: 'List of authors', 411 | tags: ['Author'], 412 | responses: { 413 | 200: Author[], 414 | }, 415 | }, 416 | }, 417 | } 418 | }>; 419 | ``` 420 | ::: 421 | 422 | :::details Generated OpenAPI Spec 423 | ```yaml{5-6} 424 | paths: 425 | "/authors": 426 | get: 427 | operationId: AuthorApiSpec_get_/authors 428 | tags: 429 | - Author 430 | summary: List of authors 431 | responses: 432 | '200': 433 | content: 434 | application/json: 435 | schema: 436 | type: array 437 | items: 438 | "$ref": "#/components/schemas/Author" 439 | ``` 440 | ::: 441 | 442 | ## OperationId 443 | 444 | You can define operationId by using the `operationId` property of the operation type. 445 | 446 | ::: code-group 447 | ```ts[Tspec]{5} 448 | export type AuthorApiSpec = Tspec.DefineApiSpec<{ 449 | paths: { 450 | '/authors': { 451 | get: { 452 | operationId: 'listAuthors', 453 | summary: 'List of authors', 454 | responses: { 455 | 200: Author[], 456 | }, 457 | }, 458 | }, 459 | } 460 | }>; 461 | ``` 462 | ::: 463 | 464 | :::details Generated OpenAPI Spec 465 | ```yaml{4} 466 | paths: 467 | "/authors": 468 | get: 469 | operationId: listAuthors 470 | summary: List of authors 471 | responses: 472 | '200': 473 | content: 474 | application/json: 475 | schema: 476 | type: array 477 | items: 478 | "$ref": "#/components/schemas/Author" 479 | ``` 480 | ::: 481 | 482 | -------------------------------------------------------------------------------- /docs/guide/describing-schema.md: -------------------------------------------------------------------------------- 1 | # Defining Schema 2 | 3 | On this page, we will learn how to define schemas with [JSDoc](https://jsdoc.app/about-getting-started.html) comments. 4 | 5 | ## Description 6 | 7 | You can describe the schema with JSDoc comments. 8 | 9 | ```ts{1,3,5} 10 | /** Book schema Info */ 11 | interface Book { 12 | /** Book ID */ 13 | id: number; 14 | /** Title of the book */ 15 | title: string; 16 | } 17 | ``` 18 | 19 | :::details Generated OpenAPI Spec 20 | ```yaml{4,9,12} 21 | components: 22 | schemas: 23 | Book: 24 | description: Book schema Info 25 | type: object 26 | properties: 27 | id: 28 | type: number 29 | description: Book ID 30 | title: 31 | type: string 32 | description: Title of the book 33 | required: 34 | - id 35 | - title 36 | ``` 37 | ::: 38 | 39 | ## Example 40 | 41 | If you want to add an example to the schema, you can use the `@example` tag. 42 | 43 | ```ts{4,9} 44 | interface Book { 45 | /** 46 | * Book ID 47 | * @example 123456789 48 | * */ 49 | id: number; 50 | /** 51 | * Title of the book 52 | * @example The Great Gatsby 53 | */ 54 | title: string; 55 | } 56 | ``` 57 | 58 | :::details Generated OpenAPI Spec 59 | 60 | ```yaml{9,13} 61 | components: 62 | schemas: 63 | Book: 64 | type: object 65 | properties: 66 | id: 67 | type: number 68 | description: Book ID 69 | example: 123456789 70 | title: 71 | type: string 72 | description: Title of the book 73 | example: The Great Gatsby 74 | required: 75 | - id 76 | - title 77 | ``` 78 | ::: 79 | 80 | ## Format 81 | 82 | If you want to specify the format of the schema, you can use the utility types provided by Tspec. 83 | 84 | ```ts{4,5,6} 85 | import { Tspec } from 'tspec'; 86 | 87 | interface Book { 88 | id: Tspec.Integer; 89 | coverImage: Tspec.ImageUriString; 90 | publishedAt: Tspec.DateTimeString; 91 | } 92 | ``` 93 | 94 | :::details Generated OpenAPI Spec 95 | ```yaml{7-8,10-12,14-16} 96 | components: 97 | schemas: 98 | Book: 99 | type: object 100 | properties: 101 | id: 102 | type: integer 103 | example: 1 104 | coverImage: 105 | format: uri 106 | type: string 107 | example: https://picsum.photos/200/300 108 | publishedAt: 109 | format: date-time 110 | type: string 111 | example: '2023-03-30T12:00:00Z' 112 | additionalProperties: false 113 | required: 114 | - coverImage 115 | - id 116 | - publishedAt 117 | ``` 118 | ::: 119 | 120 | The following utility types are provided by Tspec. 121 | 122 | | Type | Description | 123 | | --- | --- | 124 | | `Tspec.Integer` | Integer | 125 | | `Tspec.DateString` | Date string (e.g. `2021-01-01`) | 126 | | `Tspec.DateTimeString` | Date and time string (e.g. `2021-01-01T00:00:00Z`) | 127 | | `Tspec.PasswordString` | Password string (e.g. `password`) | 128 | | `Tspec.ByteString` | Byte string (e.g. `dGVzdA==`) | 129 | | `Tspec.BinaryString` | Binary string (e.g. `0x1234`) | 130 | | `Tspec.BinaryStringArray` | Binary string array (e.g. `[0x1234, 0x5678]`) | 131 | | `Tspec.EmailString` | Email string (e.g. `test@test.com`) | 132 | | `Tspec.UuidString` | UUID string (e.g. `123e4567-e89b-12d3-a456-426614174000`) | 133 | | `Tspec.UrlString` | URL string (e.g. `http://localhost`) | 134 | | `Tspec.ImageUriString` | Image URI string (e.g. `https://picsum.photos/200/300`) | 135 | | `Tspec.HostnameString` | Hostname string (e.g. `localhost`) | 136 | | `Tspec.Ipv4String` | IPv4 string (e.g. `127.0.0.1`) | 137 | | `Tspec.Ipv6String` | IPv6 string (e.g. `::1`) | 138 | | `Tspec.JsonPointerString` | JSON Pointer string (e.g. `/`) | 139 | 140 | -------------------------------------------------------------------------------- /docs/guide/express-integration.md: -------------------------------------------------------------------------------- 1 | --- 2 | outline: deep 3 | --- 4 | 5 | # Express Integration 6 | 7 | If you are using the [Express framework](https://expressjs.com/), you can integrate Tspec into your project easily. 8 | Tspec automatically parses your [Express](https://expressjs.com/) handler type to generate parameters and responses schemas and provides middleware to serve Swagger UI. 9 | 10 | ## Setup Express Application 11 | 12 | Before integrating Tspec, assume that you have the following Express controller: 13 | 14 | ::: code-group 15 | ```ts[controller.ts] 16 | import { Request, Response } from 'express'; 17 | 18 | type PathParams = { id: string }; 19 | type AuthorRes = { id: string, name: string }; 20 | 21 | export const getAuthor = async ( 22 | req: Request
extends Pick