@sidev/api-handler v1.0.2
@sidev/
api-handler
A type-safe library for handling API responses and function returns in JS and TS applications.
Features
- Provides consistent return values and type safety.
- Standardizes success and error handling for better readability.
- Integrates easily with TypeScript and JavaScript projects.
- Supports custom data structures and application-specific types.
TOC
Installation
You can install @sidev/api-handler
using npm or yarn:
npm install @sidev/api-handler
or
yarn add @sidev/api-handler
Usage
1. Basic Setup
Import the library and create your API handler:
import { createApiHandler } from '@sidev/api-handler'
const APP_ERRORS = {
AUTH_MEMBER_EXISTS: {
code: 'AUTH_MEMBER_EXISTS',
message: 'Member already exists.',
uiMessages: ['This member already exists.', 'Please log in instead.'],
},
AUTH_NO_CREDENTIALS: {
code: 'AUTH_NO_CREDENTIALS',
message: 'No credentials provided.',
uiMessages: ['No credentials were provided.', 'Please try again.'],
},
}
const { apiError, apiSuccess, ERROR, CODE } = createApiHandler(APP_ERRORS)
2. Handling Errors
You can generate error responses using apiError()
:
const errorInstance = new Error(CODE.AUTH_MEMBER_EXISTS)
const errorResponse1 = apiError(errorInstance)
console.log('apiError(errorInstance) returns:', errorResponse1)
// Using error code as a string
const errorCode = CODE.AUTH_MEMBER_EXISTS
const errorResponse2 = apiError(errorCode)
console.log('apiError(errorCode) returns:', errorResponse2)
Note:
/*
When using `apiError` to throw error codes, you can either pass the error code string directly or wrap it in a new `Error` object.
While both approaches work, wrapping the error code in a new `Error` object will provide a more accurate stack trace, showing the exact location where the error was thrown. This can be a minor but useful difference for debugging purposes.
*/
try {
// Example of throwing an error with a more accurate stack trace
throw new Error(CODE.AUTH_MEMBER_EXISTS)
// Example of throwing an string with error code
throw CODE.AUTH_MEMBER_EXISTS
//
} catch (error) {
// return from catch block
return apiError(error)
}
3. Handling Success Responses
To generate success responses, use apiSuccess()
:
const dataObj = {
data: { key: 'value' },
message: 'Operation successful',
}
const successResponse = apiSuccess(dataObj)
console.log(successResponse)
4. Using in Functions
Here’s an example of using apiSuccess()
and apiError()
in a function:
import { createApiHandler } from '@sidev/api-handler'
import { myErrors } from './my-errors'
const { apiError, apiSuccess, CODE } = createApiHandler(myErrors)
const handleRequest = (condition: string) => {
try {
if (condition === 'error') {
throw new Error(CODE.NOT_AUTHORIZED)
// or
// throw CODE.AUTH_NO_CREDENTIALS;
}
// go on
return apiSuccess({
data: { abc: 'xyz' },
message: 'Request handled successfully',
})
} catch (error) {
// return what was thrown with apiError()
// if any other error happen it will be handled also uniformly
// and return expected ApiErrorResponse object
return apiError(error)
}
}
console.log(handleRequest('error'))
/* logs:
{
success: false,
error: Error: Not authorized
at handleRequest ... ...
message: 'Not authorized',
code: 'NOT_AUTHORIZED',
uiMessages: [
'You are Not authorized',
'This content is only available to authorized users.',
'Look for the login button in the top right corner.'
]
}
*/
console.log(handleRequest('success'))
/* logs:
{
success: true,
data: { abc: 'xyz' },
message: 'Request handled successfully'
}
*/
5. Example with Custom Types (TS)
You can separate your error definitions in a centralized file, even for the whole app. Use type ApiErrorsMap
// my-errors.ts
import { type ApiErrorsMap } from '@sidev/api-handler'
// Centralized error definitions
export const myErrors: ApiErrorsMap = {
NO_ACCOUNT: {
code: 'NO_ACCOUNT',
message: 'Account not found',
uiMessages: [
'Try creating an account.',
'If you are an admin, please contact developer team.',
],
},
NOT_AUTHORIZED: {
code: 'NOT_AUTHORIZED',
message: 'Not authorized',
uiMessages: [
'You are Not authorized',
'This content is only available to authorized users.',
'Look for the login button in the top right corner.',
],
},
// ... more
}
// only for this example mock function
export async function simulatedDb(id: number) {
await new Promise((resolve) => setTimeout(resolve, 30))
/* --- 555 id will simulate unpredicted error from db --- */
switch (id) {
case 555:
throw new Error('Some error outside your definitions')
case 10:
return { id, name: 'John Doe', role: 'Admin' }
case 20:
return { id, name: 'Smith Bob', role: 'Customer' }
default:
return {}
}
}
Now, in module, you can define custom types for your .data with type ApiResponseWith
// example.ts
import { createApiHandler, type ApiResponseWith } from '@sidev/api-handler'
import { myErrors, simulatedDb } from './my-errors'
const { apiError, apiSuccess, CODE } = createApiHandler(myErrors)
type UserAccount = {
id: number
name: string
role: string
}
const getAdminAccount = async (
id: number
): Promise<ApiResponseWith<UserAccount>> => {
try {
const userAccount = await simulatedDb(id) // a mock function
if (userAccount.id) {
if (userAccount.role !== 'Admin') {
throw new Error(CODE.NOT_AUTHORIZED)
}
return apiSuccess({
/* --- data property is typechecking for UserAccount --- */
data: userAccount,
message: 'Account fetched successfully', // optional default "Success"
})
}
throw new Error(CODE.NO_ACCOUNT)
} catch (error) {
return apiError(error)
}
}
/* --- call it with different id param to test outputs --- */
let response
response = await getAdminAccount(20) // success:false NOT_AUTHORIZED
response = await getAdminAccount(10) // success:true
response = await getAdminAccount(555) // success:false UNEXPECTED ERROR
response = await getAdminAccount(1000) // success:false NO_ACCOUNT
if (response.success) {
console.log('success response', response)
} else {
console.log('error response', response)
/* --- do something with uiMessages on frontend --- */
// showToast(response.uiMessages[0])
response.uiMessages.map((message: string) => console.log(message))
}
6. apiSuccess() Important notes and .warning key
Function always returns ApiSuccessResponse object. On bad data attaches warning
import { createApiHandler } from '@sidev/api-handler'
const { apiError, apiSuccess } = createApiHandler({})
/**
* JS runtime check in: apiSucess({ data : ValidData })
*
* - Call to apiSuccess() always! returns success response.
* - In case of misuse of apiSuccess params response.warning is attached.
* - Bad params or value, is converted to String and returned in response.data.
*
* _________________________________________________________________
*/
console.log(apiSuccess()) // with {warning}
console.log(apiSuccess(null)) // with {warning}
/* logs:
{
success: true,
data: 'null',
message: 'Success',
warning: 'Bad params in apiSuccess(params). Params should be { data: value } object. Got null.'
}
*/
console.log(apiSuccess({ abc: 1 })) // with {warning}
console.log(apiSuccess(100)) // with {warning}
console.log(apiSuccess({ data: null, message: 'lorem ipsum' })) // with {warning}
console.log(apiSuccess({ data: undefined })) // with {warning}
console.log(apiSuccess({ data: 888 })) // with {warning}
console.log(apiSuccess({ data: 'lorem ipsum' })) // OK
console.log(apiSuccess({ data: [4, 5, 6] })) // OK
console.log(apiSuccess({ data: { abc: 2 } })) // OK
// ---- In apiError call with bad params will return with code: UNKNOWN_ERROR
console.log(apiError())
/* logs:
{
success: false,
error: Error: Unknown error occurred
message: 'Unknown error occurred',
code: 'UNKNOWN_ERROR',
uiMessages: [ 'An unknown error occurred.' ]
}
*/
Types Overview
ApiHandler
Represents a unified API response interface that can be either success or error response.
declare function createApiHandler(apiErrors: ApiErrorsMap): ApiHandler
export type ApiHandler = {
apiError: (error: unknown) => ApiErrorResponse
apiSuccess: <T extends ValidData>(params: {
data: T
message?: string
}) => ApiSuccessResponse<T>
readonly ERROR: ApiErrorsMap
readonly CODE: Record<string, string>
}
ValidData
Represents valid types of data that can be used in success responses. It can be an object, array, or string.
type ValidData = object | any[] | string
ApiResponseBase
The base structure for all API responses, containing a success
flag and an optional message
.
export type ApiResponseBase = {
success: boolean
message?: string
}
ApiSuccessResponse
Represents a successful API response. It extends ApiResponseBase
and includes the data
field containing the response data. Other error-specific fields are set to never
.
export type ApiSuccessResponse<T extends ValidData = object> =
ApiResponseBase & {
success: true
data: T
warning?: string // internal for misuse of apiSuccess
error?: never
code?: never
uiMessages?: never
}
ApiErrorResponse
Represents an error response. It extends ApiResponseBase
and includes additional fields for error
, code
, and uiMessages
.
export type ApiErrorResponse = ApiResponseBase & {
success: false
error: Error
code: string
uiMessages: string[]
data?: never
warning?: never
}
ApiResponse
A union type that can be either a success or an error response.
export type ApiResponse = ApiSuccessResponse | ApiErrorResponse
ApiResponseWith
A generic type that extends ApiResponse
to include specific data types in success responses.
export type ApiResponseWith<T extends ValidData> =
| ApiSuccessResponse<T>
| ApiErrorResponse
AppError
Defines the structure for application-specific errors, including code
, message
, and uiMessages
.
export type AppError = {
code: string
message: string
uiMessages: string[]
}
ApiErrorsMap
A record type that maps error codes to AppError
objects.
export type ApiErrorsMap = Record<string, AppError>
API Reference
createApiHandler(apiErrors: ApiErrorsMap)
- Description: Initializes an API handler with custom error configurations.
- Parameters:
apiErrors
: A map of application-specific error codes to error details, including messages and UI messages.
- Returns: An API handler object containing methods for generating standardized success and error responses.
apiError(error: unknown): ApiErrorResponse
- Description: Processes an error and returns a structured API error response.
- Parameters:
error
: The error instance or code to be transformed into an API error response.
- Returns: An
ApiErrorResponse
object containing error details and UI messages.
apiSuccess<T extends ValidData>(params: { data: T; message?: string }): ApiSuccessResponse<T>
- Description: Generates a standardized API success response.
- Parameters:
params
: An object containing:data
: The data to include in the success response.message
: An optional message to accompany the success response.
- Returns: An
ApiSuccessResponse<T>
object with the provided data and message.
About
Author
si Slavko Ivanovic
License
Released under the MIT License.