# WAPPI - Serverless API Framework

# What is wappi?
`wappi` is a framework that allows a functional API to be run serverless or as a monolith. It is designed to make creating an API backend, simple and organized. So even junior developers can pick it up in no time.   
It incorporates testing, documentation, and simple local development in it's core. This way your code will be clean, documented and tested as a standard. `wappi` will run locally with ease. It has templates for each endpoint out of the box. It incorporates [prisma](https://www.npmjs.com/package/prisma) into it's dataLayer by default.   
`wappi` is written in TypeScript, with types also incorporated in it's core.  

`wappi` pairs with `wappi/frontend` which will link your frontend requests to your backend API in a simple structure. It's all JSON, so you don't have to learn a new syntax or language. 

### Install globally
Wappi is a npx package. To use without typing npx each time, install it globally:
```Bash
npm install -g wappi
```
#### Or Use
```Bash
npx wappi [command]
```

## How to use:

When installed globally:  
**Commands**
```txt

---------------------------------------------------------------------------------------------------------------
Command | Shorthand | Short Description                                                     | Example
--------|-----------|-----------------------------------------------------------------------|-------------------
create  |     c     |  Creates a new functional endpoint                                    | wappi c
--------|-----------|-----------------------------------------------------------------------|-------------------
build   |     b     |  Creates the build for running locally                                | wappi b
--------|-----------|-----------------------------------------------------------------------|-------------------
delete  |     d     |  Deletes a current functional endpoint version and namespace          | wappi d
--------|-----------|-----------------------------------------------------------------------|-------------------
help    |     h     |  Show this help menu                                                  | wappi h
--------|-----------|-----------------------------------------------------------------------|-------------------
init    |     i     |  Initializes WAPPI  (required for all the other commands to work)      | wappi i
--------|-----------|-----------------------------------------------------------------------|-------------------
lint    |     l     |  Runs eslint over code. Based off the .eslintrc.json                  | wappi l
--------|-----------|-----------------------------------------------------------------------|-------------------
run     |     r     |  Run wappi locally. Runs on the port set in config (wappi.json)       | wappi r
--------|-----------|-----------------------------------------------------------------------|-------------------
test    |     t     |  Test all your code, using Jest                                       | wappi t
--------|-----------|-----------------------------------------------------------------------|-------------------
update  |     u     |  update all your node modules, to be using the latest versions        | wappi u
--------|-----------|-----------------------------------------------------------------------|-------------------
watch   |     w     |  Watches the API folder for changes, works with `wappi run`           | wappi w
---------------------------------------------------------------------------------------------------------------
```

_Note: If not installed globally, use it with npx._ `npx wappi [command]`

## Folder Structure

### NameSpacing and best practices

* Always use camelCase, for all variable names, database tables names, etc.   
* `wappi` returns all data with keys of objects as camelCase by default.    

##### Endpoint (Function) Names
Each endpoint is PascalCase with a underscore _.    
The underscore `_` is to separate the namespace with the function purpose as well as the version number.
Example: 
`User_GetAllUsers_1_0_0` || `User_Update_2_0_2` || `CompanyProducts_AddAllBrandedProducts_2_0_2` || `Core_Login_1_1_0`
The endpoints are grouped by the folder names, which are separated by the underscores.  
So you can have many `User` endpoints, and many `User_Update` Versions. All easily organized. 

### DataLayer
The DataLayer folder is where you organize all your requests to databases and stores. This way you can reuse the same database calls over multiple endpoints.

### Core
These are a few Core Files which are included by default. They help `wappi` do auto authentication. Allows you to decode the JWT and retrieve infor from it relating to the user, etc. 

### CoreHelpers
These are simple helpers that allow you as a user to do some basic things.   
If you want add your own helpers, add them here.  

### Logging
Logs in development is important.
wappi has logs by default in every endpoint.
You can customize how to treat your logs and where they should go in the `CoreHelpers/logging.ts` file.


_Note: When you run `wappi update`, the Core & CoreHelpers folders will be updated with the latest versions of the default functions (Except Core_Login). So if you make changes to these default functions, they will be overridden. However, your custom functions will not be changed or removed._

## Versioning
`wappi` comes with versioning out of the box.  
When running `wappi create` you will have the option of copying the last versions code or creating a new version from scratch.   

**Why version?**   
When deploying a backend fix with a frontend, there is a fundamental issue if they have a dependency on each other. That is: you will need to deploy them at the same time to avoid any production issues.   
With `wappi` you will can create a new version of your endpoint. This will mean you can deploy this version to production first. It can be there because nothing will be using it. Then you deploy your front end code with the version update and other updates. Resulting in ZERO downtime and a happy CTO! ;)


### Pre-requisite knowledge to make awesome APIs
* Basic Typescript
* Basic Jest (not hard to learn)
* JSON
* Or use Chat GPT...

# How to Create a new endpoint?
To Create a new endpoints: run `wappi c`
You will be prompted to add in a namespace and function name. 
Everything is always camelCase.    
Except for the function name itself. This is PascalCase. Just as a Class would be named.   
The function name also has underscores _ for namespaces. (Ex: User_GetAllUserData)   
This is so all the functions can be grouped together.

When `wappi c` is created it will create the following files and automatically name everything correctly.

NameSpace
   FunctionName
       Version (1.0.0)
         _data
            failData.ts // Data for the tests to run correctly
            successData.ts // Data for the tests to run correctly
         helpers
            Add Any Files you want in here that assist with your main function. This keeps the file structure tidy.
         index.ts // This is the main function file
         index.spec.ts // This is the test file that uses Jest.
         README.md // Where you document the endpoint purpose
         types.ts // Where you add all the types

# File Structures
### index.ts
This is the file that will be called through the wappi server
```Typescript
import { DB } from '../../../CoreHelpers/types'
import { createLog } from '../../../CoreHelpers/logging'
import { standardErrorLog, standardInitialLog } from '../../../CoreHelpers/logging'
import { Response, Body } from './types'

export async function NameSpace_FunctionName_1_0_0(
  body: Body,
  { prisma }: DB,
): Promise<Response> {
  try {
    if (!body) throw new Error('No Body')

    standardInitialLog(body, 'NameSpace_FunctionName_1_0_0')

    // CODE GOES HERE

    return {
      success: true,
    }
    // eslint-disable-next-line no-unreachable
  } catch (err) {
    standardErrorLog(body, err, 'NameSpace_FunctionName_1_0_0')
    return {
      success: false,
      error: 'Fatal error in NameSpace_FunctionName_1_0_0. Investigate.',
      message: err.message,
    }
  }
}
```

### index.spec.ts
```Typescript
import { db, disconnectPrisma } from '../../../CoreHelpers/JestPrisma'
import { NameSpace_FunctionName_1_0_0 } from './index'
import { successResponse, successBody } from './_data/successData'
import { failBody, failResponse } from './_data/failData'

afterAll(async () => { disconnectPrisma() })

it('Should be successful - NameSpace_FunctionName_1_0_0 Basic Test', async () => {
  const { success } = await NameSpace_FunctionName_1_0_0(successBody, db)
  expect(success).toBeTruthy()
})

it('Should be successful - NameSpace_FunctionName_1_0_0 Basic Test', async () => {
  const response = await NameSpace_FunctionName_1_0_0(successBody)
  expect(response).toEqual(successResponse)
})

it('Should fail - NameSpace_FunctionName_1_0_0 Basic Test', async () => {
  // @ts-expect-error
  const test = await NameSpace_FunctionName_1_0_0(failBody, db)
  expect(test.success).toBeFalsy()
  expect(test).toEqual(failResponse)
})
```

### types.ts
This is the types file for the endpoint.

```Typescript
import { WappiBaseAuthenticatedBody } from '../../../CoreHelpers/types'

export interface EndPointBody {
   // Add any types you want the endpoint to validate here
   // These types are passed through in the body.
}

export interface SuccessResponse {
   success: true
   // Any items in the response object need to be typed here.
}

export interface ErrorResponse {
   success: false
   error: string
   message: string
}

export type Response = SuccessResponse | ErrorResponse

export interface Body extends EndPointBody, WappiBaseAuthenticatedBody {}
```
### failData.ts
This file is for all the Jest test fail data to be passed into the body of the endpoint during the test. Or as the response of the endpoint.
```Typescript
import { BASE_AUTH_TEST_BODY } from '../../../../CoreHelpers/testBody'

export const failBody = {
   ...BASE_AUTH_TEST_BODY,
   // Add in any incorrect data to test the endpoints failure
}

export const failResponse = {
  success: false,
  error: 'Fatal error in Users_CreateUser_1_0_0. Investigate.',
  message: 'No Body',
}
```

### successData.ts
```Typescript
import { BASE_NO_AUTH_TEST_BODY } from '../../../../CoreHelpers/testBody' // Note this is different depending on if AUTH is required
import { Body, SuccessResponse } from '../types'

export const successBody: Body = {
   ...BASE_NO_AUTH_TEST_BODY,
   // add in any data to the body to make the function work.
}

export const successResponse: SuccessResponse = {
  success: true,
}

```

# DataLayer
There is a main folder called `DataLayer`
This folder is for all the data calls. Like Prisma, SQL, NoSQL, File Storage, etc.
These are reusable files that can be used in every endpoint. 
Functions in these files should be written like this: 
```Typescript
export const functionName = async (
  prisma: PrismaDB,
  userId: string,
) => {
  try {
    return prisma.user.findFirst({
      where: {
        id: userId,
      },
      // ... etc..
    })
  } catch (error) {
    console.error('functionName ERROR: ', error)
    throw new Error('Error functionName')
  }
}
```
OR like this: 
```Typescript
export const upsertUser = async (
   prisma: PrismaDB,
   {
      userId,
      name,
      phone,
   }: {
      userId?: string
      name: string
      phone: number
   },
) => {
   try {
      return prisma.users.upsert({
         where: { id: userId || '' },
         update: {
         userId,
         name,
         phone,
         },
         create: {
         userId,
         name,
         phone,
         },
         select: {
         id: true,
         },
      })
   } catch (error) {
      standardDataLayerErrorLog('upsertUser', error)
      throw new Error('Error Getting upsertUser')
   }
}
```

# Authentication
Authentication is done out of the box. 
If you want a user to be able to log in, or authenticate into your service. Just update the `Core_Login_[version]` function.
It will always use the highest version of the Login Endpoint to authenticate.
Inside the `Core_Login_1.0.0` endpoint you can add whatever logic you want to verify a user. Whether thats OAuth or Email login, or whatever your service needs. 
The object that is returned from this endpoint will be encrypted, timestamped, your encryption key will be salted into it, and will be added to the login response as an `auth` token.
The `auth` token is required to be sent with every 'loginRequired' endpoint.
When a new endpoint is created, the question is asked: `Does this function require a user to have authenticated JWT to access it?`
If the answer to this is yes. Then the `auth` token is required to be sent.

This auth token will be decrypted by wappi and returned into the endpoint body in the object: 
```Typescript
{
   ...body,
   auth: // Decrypted auth token from Core_Login_1.0.0 response
}
```
This means anything added to the `Core_Login_1.0.0` endpoint will be available in every authenticated endpoint. 
Which means you don't need to send that information through in the body as well.


# Webhooks
Sometimes your service or app will require you to receive a webhook. This is usually sent by another service so you can't control the body or auth, etc. You get what you get and don't get upset. 
Therefor Webhooks are handled slightly differently.  
Your function must be in a nameSpace called `Webhook`!!
The URL matters ==> A webhook must point at ```/Webhook_FunctionName_V_V_V ```
Similarly, all endpoints for webhooks must be namespaced in ```API/Webhook```  
The entire body of the payload will get passed through. 
WARNING:: It is up to you to manually verify the authenticity of the payload.

# Setting up your .env file
Every wappi repo should have a `env.example`
This file should be committed with no data in it.
So with the CI/CD process, it uses the .env.example to generate the env file. 
To run on your CI/CD or locally use: `wappi envfile` or `wappi e`.
This command will generate a `.env` file based from the `.env.example` and will get any environment variables that are available and populate the `.env` file with those values. 
So locally, nothing will happen. But in a CI/CD pipeline, like GitLab. If you have variables set in your repo, they will be populated automatically. Pretty cool!

# Lint
An eslint file comes with wappi. 
To lint your project based on the rules in the `.eslintrc.json` file, run `wappi lint` or `wappi l`
Best practice: If everyone has eslint installed, they rules are committed and the project always stays consistent. 


# Main index.ts file
There is a main API/index.ts file. 
This file has a very strict format and should not be changed by you, unless you know what you are doing!
If this file is incorrect, then the wappi server won't work.
When running `wappi c` it will automatically add the endpoints to this file. 
Running `wappi d` will delete the endpoint you choose from this file automatically.
YOU SHOULD NOT TOUCH THIS FILE.
If you make an internal only endpoint, it will not be in this file.
```Typescript
// DO NOT REMOVE ANY COMMENTS!
exports.Endpoints = {
  Core_TestNoAuthentication_1_0_0: true, // DO NOT DELETE THIS METHOD
  Core_TestAuth_1_0_0: true, // DO NOT DELETE THIS METHOD
  Core_Login_1_0_0: true, // DO NOT DELETE THIS METHOD
  NewEndpoints_AddedHere_1_0_0: true,
  // NewEndpointGoesHere
}

exports.LoginRequired = {
  Core_TestAuth_1_0_0: true, // DO NOT DELETE THIS METHOD
  Core_Login_1_0_0: true, // DO NOT DELETE THIS METHOD
  NewAuthEndpoints_AddedHere_1_0_0: true,
  // NewLoginRequiredGoesHere
}
```

# How to test if your service is running? Health Checks!
When you have this running on a server, you may want to do a health check to make sure it's all happy and working.  
To do a health check, simply hit the URL: `/healthcheck`   
All http methods will return a 200 response.   
Try it locally after running `wappi run` > then go to the url: `http://localhost:3030/healthcheck`
Just hit `{DOMAIN_URL}/healthcheck`
If it's working, this will return: 
```JSON
{
   "complete": true,
   "statusCode": 200,
   "message": "Healthy",
   "success": true,
   "devMessages": [] // This depends what environment you are in.
}
```

# Response dev messages
in your `.env` file, you can set the ENVIRONMENT
ENVIRONMENT - Either: 'localhost' | 'dev' | 'stage' | 'prod' - Rebuild when you change this
Like this `ENVIRONMENT='localhost'`
If you are running on `localhost` | `dev` | `stage`. You will see an additional key in the response called `"devMessages": []`
This is an array of developer only messages that tell you why things are not working within wappi.
They are wappi server specific and have nothing to do with your code within an endpoint.

# Testing authentication
In a service you might need to test if someone is authenticated or not.
Wappi has an out of the box function called: `Core_TestAuth_1_0_0`
If the user has a authenticated auth token, it will return:
```JSON
{
   "complete": true,
   "statusCode": 200,
   "message": "No message",
   "success": true,
   "authenticated": true,
   "results": {
      "success": true,
      "message": "If you see this, the authentication is working.",
      "auth": {}, // A decrypted response of the auth token (The response from Core_Login_1.0.0)
      "environment": "localhost"
   },
   "devMessages": []
}
```
OR
```JSON
{
    "complete": true,
    "statusCode": 200,
    "message": "No message",
    "success": true,
    "authenticated": false,
    "devMessages": [
        "Your auth key is not authorized. Reissue it and try again.",
        "Your access is denied. Login again if the function required authentication, or change the required auth to false."
    ]
}
```

# Testing

## Testing everything
To run all the `index.spec.ts` files run `wappi t`

## To test 1 endpoint
You can test 1 specific endpoint very easily by just running the following.
`wappi t NameSpace_FunctionName_1_0_0`
This will run the `index.spec.ts` file for the endpoint name passed in.

# Running Locally
To run the wappi server locally, just run:
`wappi run` or `wappi r`
This will build all the endpoints and run the server on the port in your config (wappi.json) file.
This will NOT install your npm packages. So you need to run `npm install` or `yarn` before running it.
Running wappi will also send a compiled `wappiTypes.ts` file to the paths in your `.env` file.
Example: 
```bash
FRONTEND_PROJECT_PATHS=["/Users/JamesBond/Project007/CasinoRoyale/src/"]
```

# Types that will be excluded from the wappiTypes.ts file are:
`EndPointBody`,  `SuccessResponse`, `ErrorResponse`, `Response`, `CreateLogProps`, `DB`, `PrismaDB`, `WappiBaseNoAuthBody`, `WappiBaseAuthenticatedBody`, `Body`
This means that these are restricted to use. Or if they are used, they will not propagate through to your frontend `wappiTypes.ts` file.

# Using types on the front end
Many projects have types on the backend that need to be propagated through to the front end project.
With wappi you can write your types in the `types.ts` file in the endpoint and it will be compiled into a single types file that will be automatically sent to your frontend project with the following set up in your `.env` file. 
Example: 
```bash
FRONTEND_PROJECT_PATHS=["/Users/JamesBond/Project007/CasinoRoyale/src/"] # The local path you want it to go.
```
The end result will be 1 file with every type from the backend that is used in: 
1. Endpoint Body
2. Endpoint SuccessResponse
3. Endpoint ErrorResponse
Here is an example: 
```Typescript
type FunPersonOptions = 'fun' | 'notFun' | 'boring'

export interface NameSpace_FunctionName_1_0_0__Body {
   userId: number
   userName: string
   funPerson: FunPersonOptions
}

export interface NameSpace_FunctionName_1_0_0__SuccessResponse {
   success: true
   matchName: string
   toSexyForMyShirt: boolean
}

export interface NameSpace_FunctionName_1_0_0__ErrorResponse {
   success: false
   error: string
   message: string
}
```

# How to call it from the front end?
Always use the `POST` Method
The URL doesn't matter. You can send this to `domain.com/` or `domain.com/ThisIsAMadeUpURL`
It might help with logging if you have a url, but it's not needed. Except for a webhook. See webhooks.

### How the type is structured within the query

```javaScript
{
   name: "camelCaseName",
   env: "PROD",
   version: "1.0.0",
   auth: "JWT_String",
   query: "PascalCase_With_Underscores_For_NameSpacing",
   data: {
      ...
   },
   results: { // Default is "result"
      name: "camelCaseNameForResults",
      include: [
            {
               name: "camelCaseName",
               key: "keyNameToInclude"
            }
      ],
      exclude: [
            {
               key: "keyNameToExclude"
            }
      ]
   },
}
```

### Query Structure

**name** : *optional*   
This is the specific name of the information you want returned. As multiple requests can be made, this breaks them up.   

**version** : *required*   
The code version you want.   
Options:  **1.0.0**

**auth** : *optionally required - if necessary*   
The auth token of the user, which identifies they are authenticated.   

**query** : *required*    
This is the name of the function on the server.   
This function will be the one called.   

**data** : *optional*   
This is an object of the all the data required for the function.       
Such as all the user data to add a user.   
This data is specific to the function and should be typed in the `NameSpace_FunctionName_1_0_0__Body` type.
So look in the `types.ts` file of the function your calling to see whats required.

**results** : *optional*     
This determines what results you receive.   
If you leave it as a blank obj or don't include it, it will return everything the function returns.   
Otherwise you can specify what to include or exclude.    
    **name** : *optional* The name you want the results to return as. Default = results   
    **include** : *optional* An array of objects, with    
        **name** : *required* This is the name that you want it to be called. So you can rename it to match your frontend app.   
        **key** : *required* This is the backend key of the object that you want to include   
    **exclude** : *optional* An array of objects, with    
        **key** : *required* This is the backend key of the object that you want to exclude from the results. This allows the returned object to be clean to what you need, to help with clarity.   
Logic for including and excluding is as follows:   
If nothing is defined in **include**, then everything will be included.   
If nothing is defined in **exclude**, then nothing will be excluded.   
If Nothing is defined in **include**, but something is definded in **exclude**, everything defined in **exclude** will be removed.  
If a key is defined in **include** and in **exclude**, then it will be included   

**Why do this?**   
Usually the processing of the endpoint is faster than the Latency of sending and receiving the data. So you might have an endpoint that can return a lot of data, but you only want 1 piece of it. So instead of creating a new function, you can just define what you want returned and get the same result.

# wappi_frontend
There is a package called `wappi_frontend` which is built for use on the frontend to call your backend wappi server.
Which has all the types in that and uses native js fetch. So no other packages required.
So... Just use that..

# Prisma
We recommend using prisma, as it's awesome...
So any database requests. Use Prisma ORM by default.

# Updating wappi
When a new version of wappi is available, it will tell you in the terminal.
To update just run `wappi update` or `wappi u`
This will switch you to the latest version of wappi.
If it's a major version, there might be some rework of your code to get to the latest practices.

# Updating npm packages
This is a little risky to run, so be careful.
If you want to update ALL your node modules to teh latest versions run `wappi update` or `wappi u`
This will prompt you to confirm you want to update ALL your packages.. 
And then it will update everything in your `package.json`

# Building wappi 
To build run `wappi build` or `wappi b`
You would use this in your CI/CD pipeline. 
Locally, you can use it, but you don't need to. 
Running `wappi run` or `wappi r` will build it.

# CI/CD Pipelines
All wappi commands also allow helper flags. 
Currently `ciPipeline` is the only one. 
This can be used to run a light version of wappi

# wappi help
Either use the wappi GPT that is publicly available here: `https://chat.openai.com/g/g-wIGNHod1U-wappi-gpt`
or to quickly find the wappi commands, run `wappi help` or `wappi h`


