Versatile, modern and lightweight http client based on promises.
npm i -S awi
<script src="https://cdn.jsdelivr.net/npm/awi/dist/awi.js"></script>
Node | Chrome | Edge | Firefox | Opera | Safari |
---|---|---|---|---|---|
6.13.0 | 52 | 17 | 29 | 19 | 10 |
The most basic of requests can be executed seamlessly with Awi. Simply create
a new instance and call the get
sugar method with the desired URL. This call
returns an instance of Awi's Response
interface that has the response body,
status and headers easily accessible.
import { Awi, Response } from 'awi'
const response: Response = await new Awi()
.get('http://server.api/todos')
console.assert(typeof response.body === 'object')
console.assert(typeof response.headers === 'object')
console.assert(response.status === 200)
Awi is at its best when used in TypeScript as you can type-hint all responses and get type checks and nice auto-completion from your IDE.
import { Awi, Response } from 'awi'
interface TodoResponse extends Response {
body: { title: string, completed: boolean }
}
const response: Response = await new Awi()
.get<TodoResponse>('http://server.api/todos/1')
console.assert(typeof response.body.title === 'string')
console.assert(typeof response.body.completed === 'boolean')
Awi provides syntax sugar for all basic request methods. POST
, PUT
and
PATCH
helpers optionally take the body of the request as their second
argument.
import { Awi, Response } from 'awi'
const response: Response = await new Awi()
.post('http://server.api/todos', { title: 'Start using Awi.', completed: true })
console.assert(response.status === 201)
Upon receiving a 400+ response status, Awi automatically rejects the promise so
that you don't have to do arbitrary checks for the response status via if
statements.
import { Awi } from 'awi'
await new Awi()
.post('http://server.api/todos', { completed: false })
.catch(response => console.assert(response.status === 422))
Awi also provides a body
helper to avoid repeating the infamous
.then(res => res.body)
promise callback. This helper accepts a generic type
to type-hint the response body.
Note that this helper sends a
GET
request by default. If you desire to use a different request method, the method needs to be specified using an interceptor.
Also note that if the promise is rejected, the whole response object is returned.
import { Awi } from 'awi'
interface Todo {
title: string
completed: boolean
}
const todo: Todo = await new Awi()
.body<Todo>('http://server.api/todos/1')
console.assert(typeof todo.title === 'string')
console.assert(typeof todo.completed === 'boolean')
Thanks to @bausano and his awesome
data structures package, Awi
has an optional
helper that returns the body of the response as an
Optional<T>
rather than rejecting the promise.
Note that this helper sends a
GET
request by default. If you desire to use a different request method, the method needs to be specified using an interceptor.
Also note that if the request fails due to network issues or misconfiguration, the promise is still rejected.
import { Awi, Optional } from 'awi'
interface Todo {
title: string
completed: boolean
}
const todo: Optional<Todo> = await new Awi()
.optional<Todo>('http://server.api/todos/1')
console.assert(todo instanceof Optional)
Request interceptors are what makes Awi stand out. Inspired by
Koa, Awi provides a use
method that accepts an
asynchronous callback that modifies the request object.
import { Awi, Response } from 'awi'
const response: Response = await new Awi()
.use(async req => req.base = 'http://server.api')
.use(async req => req.path = 'todos')
.get()
console.assert(response.status === 200))
All properties that can be modified on the request object are available in Awi's API reference.
Every request in Awi is uniquely defined by the array of interceptors assigned
to the request. All Awi's helper methods are nothing more but a sugar for
assigning interceptors. All requests can be sent without using the helpers via
the send
method.
import { Awi, Method, Response } from 'awi'
const response: Response = await new Awi()
.use(async req => req.base = 'http://server.api')
.use(async req => req.path = 'todos')
.use(async req => req.method = Method.GET)
.send()
console.assert(response.status === 200))
Although this approach is rather lenghty and using helpers is much cleaner, it provides a straightforward way to extend Awi and/or create request templates.
As you can see, the interceptor concept provides a way to create request templates for your application in a very nice and reusable way. This can be especially useful when making authorized requests.
import { Awi, Response } from 'awi'
// Define the template to be reused.
const auth = () => new Awi()
.use(async req => req.base = 'http://server.api')
.use(async req => req.headers['authorization'] = `Bearer ${localStorage.token}`)
// Use the template and provide further parameters.
const response: Response = await auth()
.get('user')
console.assert(response.status === 200))
All of Awi's functionality is summed up on the wiki page.
It's lightweight
The minified file size is 12kB and we do not intend to make it bigger!
It's designed for developers, by developers
Making http requests is something every developer needs to do on daily basis, especially when it comes to TypeScript or JavaScript. Awi has a neat code base, is strictly typed and we purposefully leave documentation blocks in the built files - this way, you can read up on what each method does by simply ctrl-clicking on it!
It's flexible
You can either choose to go the easy way and using Awi's built-in helpers to execute your HTTP request as a one-liner, or you can define and extract reusable templates for your requests from scratch!
Awi is also open-source, so should you be missing any features that would make your life easier, feel free to contribute.
The interceptor pattern is heavily inspired by Koa, which is also used to create a mock server for our e2e tests.
Implemetation of executors for both web and node are inspired by no other than axios.
MIT