axios-rest-resource
Schema-based HTTP client powered by axios. Built with Typescript. Heavily inspired by AngularJS' $resource
.
Installation
npm i axios-rest-resource axios
Quick start
-
Create resource module in your utils folder
import { ResourceBuilder } from 'axios-rest-resource'
export const resourceBuilder = new ResourceBuilder({
baseURL: 'http://localhost:3000',
})
-
Using a newly created resource builder create an actual resource
import { resourceBuilder } from 'utils/resource'
export const entity1Resource = resourceBuilder.build('/entity1')
-
Use your resource whenever you want to make an AJAX call
import { entity1Resource } from 'api/entity1'
const resRead = entity1Resource.read()
const resReadOne = entity1Resource.readOne({ params: { id } })
const resCreate = entity1Resource.create({ data })
const resUpdate = entity1Resource.update({ data, params: { id } })
const resRemove = entity1Resource.remove({ params: { id } })
URL token substituion
axios-rest-resource applies interceptorUrlFormatter interceptor by default. It handles {token} substitution in URLs.
Method Interface Customization
You can customize the interface of your resource methods using withParams
and withResult
. These allow you to define type-safe parameter handling and response transformation, making your API calls more intuitive and type-safe.
Note: These are different from axios's built-in transformRequest
and transformResponse
. While axios's transforms modify the request/response data internally, our withParams
and withResult
change the method's interface - how you call it and what it returns.
import { resourceBuilder } from 'utils/resource'
interface User {
id: number
email: string
}
interface SignInResponse {
token: string
user: User
}
export const usersResource = resourceBuilder.build('/users', {
signIn: {
method: 'post',
url: '/sign_in',
withParams: (email: string, password: string) => ({
data: { email, password },
headers: { 'X-Custom': 'test' },
}),
withResult: (response): SignInResponse => ({
token: response.data.auth_token,
user: {
id: response.data.user.id,
email: response.data.user.email,
},
}),
},
getProfile: {
method: 'get',
withResult: (response): User => ({
id: response.data.id,
email: response.data.email,
}),
},
register: {
method: 'post',
withParams: (email: string, password: string) => ({
data: { email, password },
}),
},
})
const signInResult = await usersResource.signIn('email@example.com', 'password')
console.log(signInResult.token)
console.log(signInResult.user.id)
const profile = await usersResource.getProfile()
console.log(profile.email)
const registerResult = await usersResource.register('email@example.com', 'password')
console.log(registerResult.data)
The interface customization provides:
- Type-safe parameter transformation with optional parameters
- Type-safe response data transformation
- Custom headers support
- Independent use of transforms (can use either or both)
- Full TypeScript type inference for parameters and return types
Custom resource schema
Create resource module in your utils folder:
import { ResourceBuilder } from 'axios-rest-resource'
export const resourceBuilder = new ResourceBuilder({
baseURL: 'http://localhost:3000',
})
Extended Schema
Extend the default schema with additional methods:
import { resourceSchemaDefault } from 'axios-rest-resource'
import { resourceBuilder } from 'utils/resource'
export const entity2Resource = resourceBuilder.build('/entity2', {
...resourceSchemaDefault,
doSomething: {
method: 'post',
url: '/do-something',
},
})
Example usage:
import { entity2Resource } from 'api/entity2'
const resRead = entity2Resource.read()
const resReadOne = entity2Resource.readOne({ params: { id } })
const resCreate = entity2Resource.create({ data })
const resUpdate = entity2Resource.update({ data, params: { id } })
const resRemove = entity2Resource.remove({ params: { id } })
const resDoSomething = entity2Resource.doSomething()
Custom Schema
Create a completely custom schema without extending the default:
import { resourceBuilder } from 'utils/resource'
export const entityResource = resourceBuilder.build('/entity', {
doSomething: {
method: 'post',
url: '/do-something',
},
})
Partial Schema
Use only specific methods from the default schema:
import { resourceSchemaDefault } from 'axios-rest-resource'
import { resourceBuilder } from 'utils/resource'
const { read, readOne } = resourceSchemaDefault
export const entityResource = resourceBuilder.build('/entity', {
read,
readOne,
})
Rails Schema
If you're using Ruby on Rails, there is also a default schema that matches Rails' conventions for controller actions:
import { railsResourceSchema } from 'axios-rest-resource'
import { resourceBuilder } from 'utils/resource'
export const entityResource = resourceBuilder.build('/entity', railsResourceSchema)
In depth
What does ResourceBuilder
do exactly upon creation?
When you call new ResourceBuilder(axiosConfig)
- If your
axiosConfig
doesn't have headers.Accept
property it sets it to 'application/json'. - It creates a new instance of axios passing
axiosConfig
to axios.create
. - It adds
interceptorUrlFormatter
to request interceptors of the newly created instance of axios. - It exposes the newly created instance of axios for further modifications at
axiosInstance
.
Each instance of ResourceBuilder has its own axiosInstance
. It's useful if you want to do something more with your axios instance like adding an interceptor.
import { ResourceBuilder } from 'axios-rest-resource'
import axios, { AxiosInstance } from 'axios'
const resourceBuilder = new ResourceBuilder({
baseURL: 'http://localhost:3000',
})
resourceBuilder.axiosInstance.interceptors.response.use(myCustomResponeInterceptor)
export { resourceBuilder }