micro-azure-functions
v0.1.1
Published
<img src='https://github.com/Albert-Gao/micro-azure-functions/blob/master/logo.png?raw=true' maxWidth="100%" height='auto' />
Downloads
6
Readme
Micro Azure Functions
Intro
- Decouple business logic into reusable functions
- Written in Typescript
- Zero runtime dependencies
- Tiny: 5KB after minified
- Rapid middlewares
- simple reasoning, just running one by one
- early exit with just
throw
httpError()
or anything orcontext.done()
- pass values among middlewares
- Easy debug:
- log request or any error by just turning one config
Why do you build this lib
Azure functions is making it a flash to creating an API endpoint. But that's just the infrastructure part. It doesn't mean your business logic can be simplified.
- I need a middleware setup to decouple my business logic without installing a lib that has many dependencies and result in a bigger bundle size as well.
- I want to deal with a simple interface, where the order is just one by one. I don't want to deal with a mental model where a middleware will be invoked twice for both stages, and handle both the
before
andafter
stage in one function.
What problems does it solve
Middleware is for decoupling logic. I learned the value of beforeHooks
and afterHooks
after adopting Feathers.JS. Which has a beautiful concept of 3 layers for every endpoint, and I found myself start the declarative programming for the backend. No more code for the repeating work. In micro-azure-functions
's context, it is just an array of Middleware
.
Let's say a simple return-a-user endpoint, what does it look like when you are using micro-azure-functions
import { azureFunctions } from 'micro-azure-functions';
const handler = azureFunctions([
validateRequestBody(GetUserSchema),
isStillEmployed,
verifyPaymentStatus,
justReturnUserObjectDirectlyFromDB,
removeFieldsFromResponse('password', 'address'),
combineUserNames,
transformResponseToClientSideStructure,
]);
Ideally, you can just compose your future Azure functions without writing any code except for an integration test. The logic will be declarative. Every middleware here can be fully tested and ready to reuse.
Usage
1. Install
npm install micro-azure-functions
2. Quick start
import { azureFunctions } from 'micro-azure-functions';
const handler = azureFunctions([
context => {
context.res = { status: 200, body: { message: 'it works' } };
},
]);
// call the API, you will get json response: { message: "it works" }
3. The type of the middleware
export type Middleware<PassDownObjType = any> = (
context: MiddlewareContext<PassDownObjType>,
...args: any[]
) => Promise<void> | void;
This is 99.999% identical with the AzureFunctions type from @azure/functions
, but one thing, it appends a new property to Context named passDownObj
, which is used to shared value among middlewares.
4. Two minutes master
How to control the flow?
context.done
will STOP the executionthrow
will STOP the execution- otherwise, the array of
Middleware
will just be executed one by one
How can I
return
- You just use the
Azure
way, setupcontext.res
- we have some handy method for you like
success(), badRequest(), internalRequest()
, so you can docontext.res = success()
, just setup the statusCode for you - or a
success()
(just ahttpResponse()
with status code set to 200, you can still change it)
- You just use the
What can you
throw
- an
httpError()
- an
badRequest()
- an
internalError()
- or anything else
- any un-caught exception will be caught at the very top level, and return with 500
- an
How to check what will be returned as the Http response
- just check the
context.res
- just check the
How to change the
response
- you just update
context.res
- you just update
How to pass something down the chain,
- use
context.passDownObj
- attach your value to it:
context.passDownObj.myValue = 123
,myValue
could be any name
- use
5. About the built-in responses
There are 2 types of response:
Built in
httpError()
forthrow
httpResponse()
forreturn
success()
badRequest()
internalRequest()
Plain JS type
throw
a plainobject
|string
|number
=== (400) response- custom status code by adding
statusCode
property
The built-in
one has some shortcuts to use.
All parameters are customizable.
import { httpError, httpResponse } from 'micro-azure-functions';
// It gives you an instance of HttpError, which extends from Error
const error = httpError({
// default status code is 400 if not set
status: 401,
body: {
message: 'test',
},
headers: {
'x-http-header': 'fake-header',
},
});
// It gives you a plain JS object.
const response = httpResponse({
// default status code is 200 if not set
status: 200,
body: {
message: 'test',
},
headers: {
'x-http-header': 'fake-header',
},
});
The commons headers are:
- 'Access-Control-Allow-Origin': '*',
- 'Access-Control-Allow-Credentials': true,
- 'Content-Type': 'application/json',
5.1. Shortcuts
Compare to the above methods, the only difference is the shortcuts just sets the status code, you can still modify them if you want.
httpError
:badRequest()
: 400internalRequest()
: 500
httpResponse
:success()
: 200
6. Config
6.1 logError
It will context.log
any error caught at the very top level
azureFunctions([], { logError: true });
6.2 logRequest
It will context.log
context.req
:
azureFunctions([], { logRequest: true });
7. Examples
7.1 Validation
In the following case, if the request name is 'albert', only validateRequest
will be called.
import { badRequest, Middleware } from 'micro-azure-functions';
const validateRequest: Middleware = (context, req) => {
if (req.body.name === 'albert') {
throw badRequest({
message: 'bad user, bye bye',
});
}
};
// it will return a 400 error { message: 'bad user, bye bye' }
Or if you like me, you can write a simple validating middleware with the yup
schema, you can then reuse from the client side.
import { Schema } from 'yup';
import { azureFunctions, Middleware, badRequest } from 'micro-azure-functions';
const validateBodyWithYupSchema = (schema: Schema): Middleware => async ({
event,
}) => {
if (!schema.isValid(event.body)) {
throw badRequest('bad request');
}
};
const handler = azureFunctions([validateBodyWithYupSchema(myYupSchema)]);
7.2 processing Response
import { badRequest } from 'micro-azure-functions';
const removeFieldsFromResponse = (fieldsToRemove: string[]): Middleware = (context) => {
const newResponse = Object.assign({}, response);
fieldsToRemove.forEach(field => {
if (context.res[field] != null) {
delete context.res[field]
}
})
return newResponse;
};
const testHandler = azureFunctions(
[
(context) => {
context.res = {
status: 200,
body: {
name: 'albert',
password: '123qwe',
address: 'somewhere on earth'
}
}
},
removeFieldsFromResponse(['password', 'address'])
],
);
// response will be { name: 'albert' }
Credits
- The initial version is heavily inspired by my favourite REST framework: Feathers.JS
- This project was bootstrapped with TSDX.