# Crow API
Crow API lets you build an API intuitively based on the file structure of a project. Provide API Gateway and Lambda function configurations and crow will build out the appropriate paths and methods to the API Gateway. All created resources are available after initialization. `lambdaFunctions` will expose all Lambda functions created for further operations like adding environment variables and providing permissions.
| `crow-api` version | `aws-cdk` version | Notes |
| ------------------ | ----------------- | ----------------------- |
| 0 | 1 | |
| 1 | 2 | Not recommended for use |
| 2 | 2 | |
Contents:
* [Getting Started](#getting-started)
* [Example File Structure](#example-file-structure)
* [Crow API Props](#crow-api-props)
* [`sourceDirectory`](#sourcedirectory)
* [`sharedDirectory`](#shareddirectory)
* [`useAuthorizerLambda`](#useauthorizerlambda)
* [`authorizerDirectory`](#authorizerdirectory)
* [`authorizerLambdaConfiguration`](#authorizerlambdaconfiguration)
* [`tokenAuthorizerConfiguration`](#tokenauthorizerconfiguration)
* [`createApiKey`](#createapikey)
* [`logRetention`](#logretention)
* [`apiGatewayName`](#apigatewayname)
* [`apiGatewayConfiguration`](#apigatewayconfiguration)
* [`lambdaConfigurations`](#lambdaconfigurations)
* [`lambdaIntegrationOptions`](#lambdaintegrationoptions)
* [`models`](#models)
* [`requestValidators`](#requestValidators)
* [`methodConfigurations`](#methodconfigurations)
* [Properties](#properties)
* [`gateway`](#gateway)
* [`authorizer`](#authorizer)
* [`authorizerLambda`](#authorizerlambda)
* [`lambdaLayer`](#lambdalayer)
* [`lambdaFunctions`](#lambdafunctions)
* [`models`](#models)
* [`requestValidators`](#requestValidators)
## Getting Started
[Start your application as a normal CDK app](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html)
```sh
npm install -g aws-cdk
cdk bootstrap # If this is your first cdk app, you will need to bootstrap your AWS account
cdk init app --language typescript
```
Next, install the Crow API package
```sh
npm install --save crow-api
```
In the `lib/` folder generated by the `cdk`, there should be a single file named `<your-app>-stack.js`. Create your Crow API construct inside of that file like so
```python
# Example automatically generated from non-compiling source. May contain errors.
from aws_cdk import Stack, StackProps
from constructs import Construct
from crow_api import CrowApi, ICrowApiProps
class YourAppStack(Stack):
def __init__(self, scope, id, props):
super().__init__(scope, id, props)
{
crowApiProps,
} = props
api = CrowApi(self, "api", {
(SpreadAssignment ...crowApiProps
crow_api_props)
})
```
Your API will start to take shape as you create folders to define paths and methods (see Example File Structure below). To deploy your API, simply run `cdk synth` and `cdk deploy`. Follow the instructions as they are prompted, and you will end up receiving a URL where your API now lives.
## Example File Structure
```
|-- src/
|-- authorizer/
|-- index.js
|-- v1/
|-- book/
|-- get/
|-- index.js
|-- post/
|-- index.js
|-- chapters/
|-- get/
|-- index.js
|-- authors/
|-- get/
|-- index.js
|-- post/
|-- index.js
```
The preceding file structure will create an API with the following routes:
* GET /v1/book
* POST /v1/book
* GET /v1/book/chapters
* GET /v1/authors
* POST /v1/authors
There needs to be an `index.js` file inside of a folder named after an HTTP method in order for a path to be created. The `index.js` file needs to export a `handler` method that will process the payload and return like the following.
```javascript
exports.handler = async function (event, context, callback) {
try {
const data = {
statusCode: 201,
};
return data;
} catch (uncaughtError) {
console.error(uncaughtError);
throw uncaughtError;
}
}
```
## Crow API Props
Crow API takes in a few props to help you customize away from defaults.
#### `sourceDirectory`
By default, Crow walks through the `src` directory in the root of the repository to determine routes and methods, but you can change the top level directory by passing in the `sourceDirectory` prop. The string passed in should not start with or end with a slash (`/`). For example, `src`, `api/src`, or `source` are all valid options to pass in through that prop.
#### `sharedDirectory`
By default, Crow creates a Lambda layer out of the `shared` directory in the source directory of the repository, but you can change the name of the shared directory by passing in the `sharedDirectory` prop. The string passed in should not start with or end with a slash (`/`) and must be a direct child of the source directory. For example, `common` or `utils` are valid but `shared/utils` is not.
The Lambda layer created will be prepended to any the of the layers passed in through `lambdaConfigurations` and added to all Lambda functions created.
#### `useAuthorizerLambda`
Crow will create and attach an authorizer Lambda to specific methods if requested. The `useAuthorizerLambda` prop tells the `CrowApi` Construct that it should create an authorizer Lambda and accepts a boolean value. This is `false` by default.
#### `authorizerDirectory`
Crow will allow for a Lambda authorizer to be created and used by specific methods if requested. The `authorizerDirectory` prop tells Crow where to find the code for the Lambda authorizer **within the source directory which can be specified in the `sourceDirectory` prop**. It expects to find an `index.js` file that exports a `handler` function within the `authorizerDirectory`.
By default, Crow expects to find a directory called `src/authorizer` containing the authorizer Lambda source if the `useAuthorizerLambda` prop is `true`. If a different directory within the source directory should be looked at for this code, it should be specified by passing in a string to the `authorizerDirectory` prop. The string passed in should not start with nor end with a slash (`/`). For example, `auth` or `authLambdaSrc` are valid.
#### `authorizerLambdaConfiguration`
The `authorizerLambdaConfiguration` prop is passed directly to the Lambda functions which will be in charge of your API's authorization. The configuration allowed is exactly the same as the [Lambda Function props](https://docs.aws.amazon.com/cdk/api/v2//docs/aws-cdk-lib.aws_lambda.Function.html).
#### `tokenAuthorizerConfiguration`
The `tokenAuthorizerConfiguration` prop is passed directly to the `APIGateway.TokenAuthorizer` construct which will be in charge of your API's authorization. Anything available in the [class constructor for the `TokenAuthorizer`](https://docs.aws.amazon.com/cdk/api/v2//docs/aws-cdk-lib.aws_apigateway.TokenAuthorizer.html) can be overridden.
**Note:**
Be careful with this configuration item as all configuration here takes precedence over Crow defaults. I suggest not using this configuration item unless you are experienced with the AWS CDK, API Gateway, and Lambda.
#### `createApiKey`
By default, Crow does not create an API key associated with the API. If an API key is desired, pass in the `createApiKey` prop as `true`.
#### `logRetention`
By default, Crow creates log groups for resources it creates and sets the log retention to one week. If a different retention is desired pass in the `logRetention` prop of [enum type `RetentionDays`](https://docs.aws.amazon.com/cdk/api/v2//docs/aws-cdk-lib.aws_logs.RetentionDays.html).
#### `apiGatewayConfiguration`
This props allows for more complex overrides to the API Gateway that fronts your API. The configuration allowed is exactly the same as the [RestApi props](https://docs.aws.amazon.com/cdk/api/v2//docs/aws-cdk-lib.aws_apigateway.RestApi.html).
**Note:**
Be careful with this configuration item as all configuration here takes precedence over Crow defaults. I suggest not using this configuration item unless you are experienced with the AWS CDK and API Gateway.
An example of this prop might look like the following:
```python
# Example automatically generated from non-compiling source. May contain errors.
from source_map_support.register import
import aws_cdk as cdk
import aws_cdk.aws_apigateway as apigateway
from ...lib.crow_api_stack import CrowApiStack
dev_environment = {
"account": "123456789012",
"region": "us-east-1"
}
app = cdk.App()
CrowApiStack(app, "CrowApiStack",
env=dev_environment,
api_gateway_configuration={
"endpoint_configuration": {
"types": [apigateway.EndpointType.REGIONAL]
}
}
)
```
#### `apiGatewayName`
This is a simple prop that names the API Gateway. This is how the API will be identified in the AWS console. The value should be a string without spaces and defaults to `crow-api`.
#### `lambdaConfigurations`
This props allows for more complex overrides to Lambda functions. The prop is an object with keys corresponding to the API path of a Lambda function and a value corresponding to the configuration that should be applied to the Lambda. The configuration allowed is exactly the same as the [Lambda Function props](https://docs.aws.amazon.com/cdk/api/v2//docs/aws-cdk-lib.aws_lambda.Function.html).
**Note:**
Be careful with this configuration item as all configuration here takes precedence over Crow defaults. I suggest not using this configuration item unless you are experienced with the AWS CDK and Lambda.
An example of this prop might look like the following:
```python
# Example automatically generated from non-compiling source. May contain errors.
from source_map_support.register import
import aws_cdk as cdk
from ...lib.crow_api_stack import CrowApiStack
dev_environment = {
"account": "123456789012",
"region": "us-east-1"
}
app = cdk.App()
CrowApiStack(app, "CrowApiStack",
env=dev_environment,
lambda_configurations={
"/v1/book/get": {
"timeout": cdk.Duration.seconds(5)
}
}
)
```
#### `lambdaIntegrationOptions`
This prop is an object with keys corresponding to the API path of a Lambda function and a value corresponding to the configuration that should be applied to the Lambda Integration. The configuration allowed is exactly the same as the [LambdaIntegrationOptions](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_apigateway.LambdaIntegrationOptions.html).
#### `models`
This prop helps set up the `Model`s used in `methodConfiguration` above. It is an array of `CrowModelOptions` which are the same as [`MethodOptions`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_apigateway.ModelOptions.html) except that the `modelName` is required. The `Model`s will receive an ID equal to its `modelName` which is why that prop is required. The `IModel` can then be referenced in `methodConfigurations` using its `modelName`.
#### `requestValidators`
This prop helps set up the `RequestValidator`s used in `methodConfiguration` above. It is an array of `CrowRequestValidatorOptions` which are the same as [`RequestValidatorOptions`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_apigateway.RequestValidatorOptions.html) except that the `requestValidatorName` is required. The `RequestValidator`s will receive an ID equal to its `requestValidatorName` which is why that prop is required. The `IRequestValidator` can then be referenced in `methodConfigurations` using its `requestValidatorName`.
#### `methodConfigurations`
This prop allows for more complex overrides to individual methods. The prop is an object with keys corresponding to the API path of a method and a value corresponding to the configuration that should be applied to the method as well as the key `useAuthorizerLambda` which will invoke the authorizer Lambda whenever the method is called. The configuration allowed is almost exactly the same as [`MethodOptions`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_apigateway.MethodOptions.html) plus the `useAuthorizerLambda` boolean.
The differences between `MethodOptions` and Crow's `CrowMethodConfiguration` (the type for this prop) is that any value referencing `{ [string]: IModel }` (`MethodOptions.requestModels` and `MethodResponse.responseModels`) has been changed to `{ [string]: string }` and similarly `requestValidator` has been changed from `IRequestValidator` to `string`. The strings that are passed should correspond with the `modelName`s or `requestValidatorName`s used in the [`models`](#models) and [`requestValidators`](#requestvalidators) props (see next sections).
**Note:**
If `createApiKey` is `true`, then the `apiKeyRequired` parameter will need to be set for the methods needing the API key.
An example of this prop might look like the following:
```python
# Example automatically generated from non-compiling source. May contain errors.
from source_map_support.register import
import aws_cdk as cdk
from ...lib.crow_api_stack import CrowApiStack
dev_environment = {
"account": "123456789012",
"region": "us-east-1"
}
app = cdk.App()
CrowApiStack(app, "CrowApiStack",
env=dev_environment,
models=[{
"model_name": "authorsPost",
"schema": {
"schema": apigateway.JsonSchemaVersion.DRAFT4,
"title": "/v1/authors/post",
"type": apigateway.JsonSchemaType.OBJECT,
"required": ["name"],
"properties": {
"name": {
"type": apigateway.JsonSchemaType.STRING
}
}
}
}
],
method_configurations={
"/v1/authors/post": {
"api_key_required": True,
"request_models": {
"application/json": "authorsPost"
}
},
"/v1/book/get": {
"use_authorizer_lambda": True
},
"/v1/book/post": {
"api_key_required": True
}
}
)
```
## Properties
A `CrowApi` construct will give full access to all of the resources it created.
#### `gateway`
This is the `apigateway.RestApi` that all of the created Lambda functions sit behind.
#### `usagePlan`
This is the `apigateway.UsagePlan` associated with the API Gateway and pre-created API key if that is enabled.
#### `authorizer`
This is the `apigateway.IAuthorizer` that is attached to the API Gateway.
#### `authorizerLambda`
This is the `lambda.Function` that authorizes API Gateway requests.
#### `lambdaLayer`
If the `sharedDirectory` is populated, this is the `lambda.LayerVersion` created for that code. If the `sharedDirectory` is not populated, then this is `undefined`.
#### `lambdaFunctions`
This is an object with keys being the API paths and the values being the `lambda.Function`s sitting being them. Continuing off of the example file structure from above, the following would be an example of referencing `GET` `/v1/book/chapters`.
```python
# Example automatically generated from non-compiling source. May contain errors.
from aws_cdk import Stack, StackProps
from constructs import Construct
from crow_api import CrowApi, ICrowApiProps
class YourAppStack(Stack):
def __init__(self, scope, id, props):
super().__init__(scope, id, props)
{
crowApiProps,
} = props
api = CrowApi(self, "api", {
(SpreadAssignment ...crowApiProps
crow_api_props)
})
lambda_ = api.lambda_functions["/v1/book/chapters/get"]
lambda_.add_environment("FOO", "bar")
```
#### `models`
This is an object with keys being the `modelName`s and values being the `IModel`s created.
#### `requestValidators`
This is an object with keys being the `requestValidatorName`s and values being the `IRequestValidator`s created.