
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
(still a work in progress)
A no-nonsense framework for creating server(less) APIs in TypeScript — api♥︎
TypeScript decorators make building routes, parameters, and validation a breeze (don't worry, you still have access to all of the underlying raw express.js req, res objects if you want them).
export class SampleAPI {
@APIEndpoint({path: "/foo"})
fooX(@APIParameter({default: "bar"}) what: string, req?, res?): Promise<any> {
return new Promise<any>((resolve, reject) => {
resolve(`foo ${what}`);
});
}
}
Your API code will run seamlessly both as an API Gateway/Lambda combo or a standard express.js server— apilove automatically determines the environment it's running in and configures itself accordingly.
// This is the only code that is required to run your API.
// When running on Lambda, this acts as your handler to an API Gateway proxy request.
// When running on a server, this acts as your express.js server.
module.exports.handler = APILove.start({
apis:[
{
require: "./SampleAPI"
}
]
});
APIs can be split into smaller chunks and loaded on demand— this can cut down drastically on memory requirements for Lambda functions by only loading the parts of your API that are needed for a given request.
If you use the supplied TypeScript decorators your API handler functions look just like standard functions (because they are).
// Useful for testing and/or using your API like a library without the overhead of a web server.
let sapi = new SampleAPI();
sapi.fooX("bar")
.then((data) => {
console.log(data);
}).catch((error) => {
console.error(error);
});
We include a sample serverless.yml configuration which allows you to fully deploy your API to AWS with serverless in a few keystrokes.
> serverless deploy
We include some standard service libraries that make building APIs even easier:
An apilove API is simply any TypeScript class with decorated methods that return a Promise.
import {APIEndpoint, APIParameter} from "apilove";
export class SampleAPI {
@APIEndpoint({path: "/foo"})
simple(what: string): Promise<any> {
return new Promise<any>((resolve, reject) => {
resolve(`foo ${what}`);
});
}
}
In the above example, apilove will call this method any time a GET request is made to your API at http://myapi.com/foo. It will also expect a parameter called what to be sent in some form or another (more on parameters later).
@APIEndpoint(options: APIEndpointOptions)
interface APIEndpointOptions {
// The method to be used when requesting this endpoint. Defaults to "get".
method?: string;
// The path to reach this endpoint. Defaults to "/".
path?: string;
// Any express.js middleware functions you want to be executed before invoking this method. Useful for things like authentication.
middleware?: ((req, res, next?) => void)[] | ((req, res, next) => void);
// Turn this on if you want to return data as-is and not in HAPI format
disableFriendlyResponse?:boolean;
// Specify a function here to handle the response yourself
successResponse?: (responseData:any, res) => void;
// If set to true, a valid JWT must be present in the request, otherwise a 401 error will be thrown
requireAuthentication?:boolean;
}
apilove has powerful automatic validation and type conversion capabilities so you never have to worry (or worry less) about parsing and validating data coming to your API.
Here is a little bit about how api parameters are mapped to your method:
/foo/:what with the same name?what=bar wtih the same name@APIParameter decorator on your method property.For example:
@APIEndpoint({
method: "POST",
path: "/foo/:what"
})
fooX(
what: string, // This will be retrieved as a string from the URL
@APIParameter({sources: "body"}) data // The body will be parsed and sent back here
): Promise<any> {
return new Promise<any>((resolve, reject) => {
resolve(`foo ${what} with some ${data}`);
});
}
@APIParameter(options: APIParameterOptions)
interface APIParameterOptions {
/**
* If set to true, an error will not be thrown to the API caller if the value is not sent
*/
optional?: boolean;
/**
* A default value to be used if one can't be found. This would be an equivalent shortcut for setting optional=true and providing a default value for your method property
*/
defaultValue?: any;
/**
* A synchronous function that can be used to transform an incoming parameter into something else. Can also be used as validation by throwing an error.
* You also get access to the raw express.js req object if you want it.
*/
processor?: (value: any, req?) => any;
/**
* One or more sources from which to look for this value. This is basically a path in the req object. So for example, a value of `query` would be equivalent to `req.query[myParamName]`
* Multiple values can be defined, and whichever one results in a non-null value first will be used. Defaults to ["params", "query", "body", "cookie", "headers"].
*/
sources?: string[] | string;
/**
* If set to true, the entire source will be returned instead of looking for a particular value. Defaults to false.
*
* Examples:
*
* The following would look for something named `userData` in the query params and return that.
* @APIParameter({sources:["query"]})
* userData:string
*
* The following would take all the query params and return them as an object
* @APIParameter({sources:["query"], includeFullSource:true})
* userData:{[paramName:string] : any}
*/
includeFullSource?: boolean;
/**
* This is the raw name of the parameter to look for in cases where the name can't be represented as a valid javascript variable name.
* Examples usages might be when looking for a header like "content-type" or a parameter named "function"
*/
rawName?: string;
}
If you'd like to redirect the response, simply pass a URL object back to the resolve callback.
While it's generally discouraged (because it may break the ability to call your method like a regular function), you can also gain access to the raw request and response objects by appending them to your method parameters.
If the last two parameters in your method have the name "req", "res", "request" or "response" apilove will send them to you.
@APIEndpoint({
method: "POST",
path: "/foo/:what"
})
fooX(
what: string, // This will be retrieved as a string from the URL
@APIParameter({sources: "body"}) data:any, // The body will be parsed and sent back here
req?, // Access the raw express.js request
res? // Access the raw express.js response
): Promise<any> {
return new Promise<any>((resolve, reject) => {
resolve(`foo ${what} with some ${data}`);
});
}
apilove has many configuration options you can modify at any time using environment variables. See the ./lib/APIConfig.ts file for more documentation on what you can configure.
Once you've defined one or more API classes it's time to expose them to the Internets! With apilove it's as simple as this:
import {APILove} from "apilove";
// Assign the output to an export named "handler". You can point your API Gateway/Lambda combo to this handler
// and apilove will take care of the rest.
// If apilove detects that it's not running in a Lambda function, it will instead spawn an express.js server.
module.exports.handler = APILove.start({
apis: [
{
require: "./SampleAPI"
}
]
});
Why do we pass a path to our API code instead of passing an instance? Because apilove will lazy-load your API only when it needs it.
This feature becomes important when you run large APIs on Lambda— because Lambda functions are ephemeral you aren't using precious memory and CPU resources for API endpoints that aren't needed for a given request.
interface APILoveOptions {
// One or more APIs to allow apilove to load. Remember these are lazy-loaded.
apis?: APILoaderDefinition[];
// By default cookieParser and bodyParser will be loaded. You can set this to false to prevent those from loading. Defaults to true.
loadStandardMiddleware?: boolean;
// Any other express.js middleware you want loaded before requests make it to apilove.
middleware?: [];
// Override default express.js and APILove error handling
defaultErrorHandler?: (error, req, res, next) => void;
// This can be used to provide a default output for all requests. Useful to return a 404 or other default page.
defaultRouteHandler?: (req, res) => void;
}
interface APILoaderDefinition {
// The root path to the API endpoint. For example you could specify "/v1" and all endpoints in this API will now be under that root path. Defaults to "/"
apiPath?: string;
// The path to the code for your API. It should work similarly to the standard node.js require function.
require: string;
// By default apilove will look for a class or module with the same name as the source file (i.e. MyAPI.js would look for an exported module called "MyAPI").
// You can override this functionality by specifying another name here.
moduleName?: string;
}
The quickest way to get started is:
Note: You must enable the emitDecoratorMetadata option in your typescript config (tsconfig.json)
{
"compilerOptions": {
"emitDecoratorMetadata": true
}
}
Deploying apilove APIs with serverless is a breeze. We've included a sample server.yml file in ./example/serverless.yml.
Assuming you've followed the instructions in the previous "getting started" section, simply:
npm install -g serverless.serverless deploy --stage dev or sls deploy --stage prod. You may also need to specify an AWSCLI credential profile with the --profile myprofile switch.That's all there is to it!
FAQs
A no-nonsense framework for creating server(less) APIs in TypeScript — api♥︎
The npm package apilove receives a total of 54 weekly downloads. As such, apilove popularity was classified as not popular.
We found that apilove demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.