ApinniEndpoint
The @ApinniEndpoint decorator annotates a controller method to define an API endpoint, specifying its path, HTTP method, and optional request/response types. It supports type inference through generics, decorator options, or method return types, following a defined priority for type resolution. The decorator integrates with the Apinni pipeline to generate type-safe API definitions for use with utility types like ApiRequest, ApiResponses etc.
- Type-safe endpoint definitions with TypeScript generics.
- Flexible type resolution for requests and responses.
- Multiple ways to define endpoint types and names
Syntax
Define an endpoint with the @ApinniEndpoint decorator, specifying the path, method, and optional request/response configurations.
@ApinniEndpoint<{
query: { type: QueryType; name: "GetUserQueryDto" },
request: { type: RequestType; name: "GetUserRequestDto" }
responses: {
200: { type: UserModelDto, name: 'GetUserResponseDto' },
403: { type: AuthErrorDto, name: 'AuthErrorDto' }
}
}>({
path: '/create-user',
method: 'POST',
query: { model: 'QueryType'; name: "GetUserQueryDto" },
request: { model: 'RequestType'; name: "GetUserRequestDto" }
responses: {
200: { model: 'UserModelDto', name: 'GetUserResponseDto' },
403: { model: 'AuthErrorDto', name: 'AuthErrorDto' }
}
})
Options
| Property | Type | Description |
|---|---|---|
path | string | The endpoint path (e.g., /:id). Supports type-safe parameterized paths. |
method | GET | POST | PUT | PATCH | DELETE | The HTTP method for the endpoint. |
query | { model: string, name?: string } | Array<string> | Optional query type and custom name, or array of simple query param names. |
request | { model: string, name?: string } | Optional request type and custom name. |
responses | { model: string, name?: string } | { [status: number]: { model: string, name?: string } } | Response types, either single (status 200) or multiple (by status code). |
Use :param notation in path for route parameters. TypeScript ensures unique parameter names for type safety.
Generic Types
The decorator supports a generic type parameter interface:
interface EndpointShape {
query?: { type: unknown; name?: string };
request?: { type: unknown; name?: string };
responses?: { [status: number]: { type: unknown; name?: string } };
}
This allows explicit type definitions for queries, requests, and responses, along with expressions like User[].
In this way you don't have to worry about incorrect types' name spelling, named imports or any other limitations related to a raw string names.
Type Resolution Priority
Types for requests and responses are resolved in this order:
- Generic Types: Defined via
<EndpointShape>(e.g.,{ request: { type: UserRequest } }). - Decorator Options: Specified in
query.model,request.modelorresponses.model. - Method Return Type: Inferred for responses if no
responsesoption is provided.
Ensure model references exist in your TypeScript project to avoid incorrect type resolutions.
Examples
Below are examples demonstrating common use cases for @ApinniEndpoint. Use the tabs to explore different configurations.
- Basic Endpoint
- Request with Models
- Query with model
- Simple Query Params
- Generic Request
Define a simple GET endpoint with an inferred response type.
import { ApinniController, ApinniEndpoint } from '@apinni/client-ts';
@ApinniController({ path: '/users' })
class UserController {
@ApinniEndpoint({ path: '/:id', method: 'GET' })
getUserById() {
return { id: '123', name: 'User' };
}
}
["/users/:id"]: {
GET: {
query: never;
request: never;
responses: {
200: GetUsersByIdResponse; /* { id: string; name: string } */
}
}
}
Specify a request and responses type using the model option.
import { ApinniController, ApinniEndpoint } from '@apinni/client-ts';
@ApinniController({ path: '/users' })
class UserController {
@ApinniEndpoint({
path: '/:id/create-post',
method: 'POST',
request: { model: 'UserRequest' }
responses: {
200: { model: 'UserResponse', name: 'UserResponseDto' },
400: { model: 'ErrorResponse' }
}
})
createPost(body) {
if (/* some condition */) {
throw new Error('error message');
}
return { success: true };
}
}
["/users/:id/create-post"]: {
POST: {
query: never;
request: PostUsersByIdCreatePostRequest; /* UserRequest */
responses: {
200: UserResponseDto; /* UserResponse */
400: PostUsersByIdCreatePost400Response /* ErrorResponse */
}
}
}
Specify a query type using the model option.
import { ApinniController, ApinniEndpoint, EndpointShape } from '@apinni/client-ts';
@ApinniController({ path: '/users' })
class UserController {
@ApinniEndpoint({
path: '/search',
method: 'GET',
query: { model: 'UserQuery' }
})
searchUsers(queryParams) {
return [{ id: '123', name: 'User' }];
}
}
["/users/search"]: {
POST: {
query: GetUsersSearchQuery; /* UserQuery */
request: never;
responses: {
200: GetUsersSearchResponse; /* Array<{ id: string; name: string }> */
}
}
}
Define simple query parameters as an array of strings.
import { ApinniController, ApinniEndpoint, EndpointShape } from '@apinni/client-ts';
@ApinniController({ path: '/users' })
class UserController {
@ApinniEndpoint({
path: '/list',
method: 'GET',
query: ['page', 'limit', 'sort']
})
listUsers(queryParams) {
return [{ id: '123', name: 'User' }];
}
}
["/users/list"]: {
POST: {
query: GetUsersListQuery; /* { page?: string; limit?: string; sort?: string } */
request: never;
responses: {
200: GetUsersSearchResponse; /* Array<{ id: string; name: string }> */
}
}
}
Use generics to define a query, request or responses types explicitly.
import { ApinniController, ApinniEndpoint, EndpointShape } from '@apinni/client-ts';
import type { UserRequest, CreatePostQuery } from './types';
@ApinniController({ path: '/users' })
class UserController {
@ApinniEndpoint<{
query: { type: CreatePostQuery, name: 'CreateUserPostQuery' };
request: { type: UserRequest, name: 'GetUserPayload' };
}>({
path: '/:id/create-post',
method: 'POST'
})
createPost(body) {
return { success: true };
}
}
["/users/:id/create-post"]: {
POST: {
query: CreateUserPostQuery; /* CreatePostQuery */
request: GetUserPayload; /* UserRequest */
responses: {
200: PostUsersByIdCreatePostResponse; /* { success: boolean } */
}
}
}
Explore these examples in a live TypeScript environment: CodeSandbox Link.
Usage Notes
- Type References: Ensure
modelreferences a type defined in your TypeScript project. - Single Response: A single
responsesobject is automatically assigned to status 200. - Validation: Validate generics and
modelreferences to prevent generation issues.
Related Topics
- ApinniController – Learn about defining controllers.
For advanced use cases, check the Apinni API Reference or contact the Apinni community.