Repository: hiowenluke/noapi
Branch: master
Commit: 1b674c17c886
Files: 72
Total size: 30.1 KB
Directory structure:
gitextract_cri7q73q/
├── .editorconfig
├── .gitignore
├── LICENSE
├── README.md
├── examples/
│ ├── 01-hello-world/
│ │ ├── biz/
│ │ │ ├── about.js
│ │ │ └── say/
│ │ │ └── hi.js
│ │ └── index.js
│ ├── 02-complex-url-params/
│ │ ├── biz/
│ │ │ └── foo/
│ │ │ └── bar.js
│ │ └── index.js
│ ├── 03-returning-an-error/
│ │ ├── biz/
│ │ │ └── do/
│ │ │ └── somethingIsWrong.js
│ │ └── index.js
│ ├── 04-throwing-an-error/
│ │ ├── biz/
│ │ │ └── do/
│ │ │ └── say/
│ │ │ └── hi.js
│ │ └── index.js
│ ├── 05-api-directory/
│ │ ├── api/
│ │ │ ├── about.js
│ │ │ └── say/
│ │ │ └── hi.js
│ │ ├── biz/
│ │ │ ├── __lib/
│ │ │ │ └── reverse.js
│ │ │ ├── about.js
│ │ │ └── say/
│ │ │ ├── __lib/
│ │ │ │ ├── decode.js
│ │ │ │ └── encode.js
│ │ │ └── hi.js
│ │ └── index.js
│ ├── 06-with-database/
│ │ ├── biz/
│ │ │ ├── about.js
│ │ │ └── user/
│ │ │ ├── get.js
│ │ │ ├── kill.js
│ │ │ ├── list.js
│ │ │ ├── login.js
│ │ │ ├── logout.js
│ │ │ └── register.js
│ │ ├── db/
│ │ │ └── index.js
│ │ └── index.js
│ ├── 07-static-resources/
│ │ ├── biz/
│ │ │ └── about.js
│ │ ├── index.js
│ │ └── public/
│ │ ├── images/
│ │ │ └── 1.jsx
│ │ ├── index.html
│ │ └── nav/
│ │ ├── index.html
│ │ └── main.html
│ ├── 99-options/
│ │ ├── config.js
│ │ ├── index.js
│ │ └── src/
│ │ └── about.js
│ └── noapi.js
├── index.js
├── package.json
├── src/
│ ├── biz/
│ │ ├── do.js
│ │ ├── index.js
│ │ └── paramsCache.js
│ ├── config.js
│ ├── data.js
│ ├── index.js
│ └── server/
│ ├── cross.js
│ ├── index.js
│ ├── public/
│ │ ├── index.js
│ │ └── mime.js
│ └── routes/
│ ├── index.js
│ └── parseQueryStr.js
└── test/
├── forExamples/
│ ├── index.js
│ └── testCases/
│ ├── 01-hello-world.js
│ ├── 02-complex-url-params.js
│ ├── 03-returning-an-error.js
│ ├── 04-throwing-an-error.js
│ ├── 05-api-directory.js
│ ├── 06-with-database.js
│ ├── 07-static-resources.js
│ └── 99-options.js
├── index.js
└── tests/
├── api-folder/
│ ├── api/
│ │ └── about.js
│ ├── index.js
│ └── test/
│ └── cases.js
├── default/
│ ├── biz/
│ │ ├── get.js
│ │ └── post.js
│ ├── index.js
│ └── test/
│ └── cases.js
└── index.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
root = true
# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true
# Matches multiple files with brace expansion notation
# Set default charset
[*.{js,jsx,html,sass}]
charset = utf-8
indent_style = tab
indent_size = 4
trim_trailing_whitespace = true
================================================
FILE: .gitignore
================================================
node_modules/
module_test/
coverage/
.DS_Store
.idea
package-lock.json
.temp/
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2019 Owen Luke
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# Noapi
A high-performance and easy-to-use web API framework for [Node.js](https://nodejs.org). Noapi loads directory "biz" as a web API server, each file in it defines and handles an API, so that you can focus on writing business function code. Noapi is simple enough that you just take it "out of the box".
## Install
```sh
npm install noapi --save
```
## TRY IT! (in under 3 minutes)
### 0. Initialize a demo project
```sh
mkdir ./noapi-demo && cd ./noapi-demo
npm init -y
npm install noapi --save
```
Create the core directory "**biz**"
```sh
mkdir biz
```
### 1. Create a biz file
Create file "./biz/say/hi.js". It defines an api "/say/hi" and handles it.
```js
module.exports = async (name, age) => {
return {msg: `Hi, I am ${name}, ${age} years old.`};
};
```
### 2. Create index.js
```js
require('noapi')();
```
### 3. Run
```sh
node index.js
Server default is listening on port 3000
```
Visit the url [http://localhost:3000/say/hi?age=100&name=owen]() to see the result:
```json
{
"success": true,
"data": {
"msg": "Hi, I am owen, 100 years old."
}
}
```
The order of the parameters in the url can be arbitrary.
## Examples
* [01 hello world](./examples/01-hello-world)
* [02 complex url params](./examples/02-complex-url-params)
* [03 returning an error](./examples/03-returning-an-error)
* [04 throwing an error](./examples/04-throwing-an-error)
* [05 api directory](./examples/05-api-directory)
* [06 with database](./examples/06-with-database)
* [07 static resources](./examples/07-static-resources)
* [99 options](./examples/99-options)
## Options
It can be omitted if it is the default value as below.
```js
const name = 'default';
const dir = './biz';
const host = 'localhost';
const port = 3000;
const isSilence = false;
// The number and order of parameters can be arbitrary
noapi(name, dir, host, port, isSilence);
// It is equivalents to:
// const options = {
// name: 'default',
// dir: './biz',
// host: 'localhost',
// port: 3000,
// isSilence: true,
// };
//
// noapi(options);
```
See "[examples/99-options](./examples/99-options)" to learn about it.
## Biz directory
Each file in the biz directory defines and handles an API. All files in the biz directory make up the API list.
```js
/root
/biz
/say
hi.js // api: /say/hi
about.js // api: /about
index.js
```
If there are some non-api files (only be used internally) in the biz directory, this will make the API list unclear. Then you should use the **api directory**, just create an empty file (or with the description of this api) to define an api. See "[examples/05-api-directory](./examples/05-api-directory)".
```js
/root
/api
/say
hi.js // api: /say/hi
about.js // api: /about
/biz
/__lib
/say
/__lib
hi.js
tools.js // non-api
check.js // non-api
about.js
init.js // non-api
index.js
```
## Performance
Noapi is a high-performance web API framework, faster than [Koa](https://github.com/koajs/koa), [Express](https://github.com/expressjs/express). See [API Framework Performance PK](https://github.com/hiowenluke/api-frameworks-performance-pk)
## Test
```sh
git clone https://github.com/hiowenluke/noapi
cd noapi
npm install
npm test
```
## License
[MIT](LICENSE)
Copyright (c) 2019, Owen Luke
================================================
FILE: examples/01-hello-world/biz/about.js
================================================
// Test
// http://localhost:3000/about
// Result
// {
// "success": true,
// "data": {
// "version": "1.0.0"
// }
// }
const fn = async () => {
return {version: '1.0.0'};
};
module.exports = fn;
================================================
FILE: examples/01-hello-world/biz/say/hi.js
================================================
// Test
// http://localhost:3000/say/hi?age=100&name=owen
// Result
// {
// "success": true,
// "data": {
// "msg": "Hi, I am owen, 100 years old."
// }
// }
// The order of the function parameters does NOT have to be
// the same as the order of the url parameters.
const fn = async (name, age) => {
return {msg: `Hi, I am ${name}, ${age} years old.`};
};
module.exports = fn;
================================================
FILE: examples/01-hello-world/index.js
================================================
require('../noapi')();
================================================
FILE: examples/02-complex-url-params/biz/foo/bar.js
================================================
// Test
// http://localhost:3000/foo/bar?name=owen&obj={"date":"2019-05-01"}&arr=[1,"abc",{"tel":12345678}]
// Result
// {
// "success": true,
// "data": {
// "name": "owen",
// "obj": {
// "date": "2019-05-01"
// },
// "arr": [
// 1,
// "abc",
// {
// "tel": 12345678
// }
// ]
// }
// }
const fn = async (name, obj, arr) => {
return {name, obj, arr};
};
module.exports = fn;
================================================
FILE: examples/02-complex-url-params/index.js
================================================
require('../noapi')();
================================================
FILE: examples/03-returning-an-error/biz/do/somethingIsWrong.js
================================================
// Test
// http://localhost:3000/do/somethingIsWrong
// Result
// {
// "success": false,
// "error": "something is wrong"
// }
const fn = async () => {
return {error: 'something is wrong'};
};
module.exports = fn;
================================================
FILE: examples/03-returning-an-error/index.js
================================================
require('../noapi')();
================================================
FILE: examples/04-throwing-an-error/biz/do/say/hi.js
================================================
// Test
// http://localhost:3000/do/say/hi
const fn = async () => {
throw new Error('something is wrong');
};
module.exports = fn;
================================================
FILE: examples/04-throwing-an-error/index.js
================================================
require('../noapi')();
================================================
FILE: examples/05-api-directory/api/about.js
================================================
// This file can be empty, or with the description of this api.
// See ./say/hi.js
================================================
FILE: examples/05-api-directory/api/say/hi.js
================================================
/**
* @test
http://localhost:3000/say/hi?age=100&name=owen
* @returns
{
"success": true,
"data": {
"msg": "Hi, I am owen, 100 years old.",
"reversed": ".dlo sraey 001 ,newo ma I ,iH",
"encoded": "SGksIEkgYW0gb3dlbiwgMTAwIHllYXJzIG9sZC4="
}
}
* @param {String} name user's name with a maximum length of 32
* @param {Integer} age user's age with a maximum of 200
* */
================================================
FILE: examples/05-api-directory/biz/__lib/reverse.js
================================================
const fn = (str) => {
return str.split("").reverse().join("");
};
module.exports = fn;
================================================
FILE: examples/05-api-directory/biz/about.js
================================================
const fn = async () => {
return {version: '1.0.0'};
};
module.exports = fn;
================================================
FILE: examples/05-api-directory/biz/say/__lib/decode.js
================================================
const fn = (b64Encoded) => {
return Buffer.from(b64Encoded, 'base64').toString();
};
module.exports = fn;
================================================
FILE: examples/05-api-directory/biz/say/__lib/encode.js
================================================
const fn = (str) => {
return Buffer.from(str).toString('base64');
};
module.exports = fn;
================================================
FILE: examples/05-api-directory/biz/say/hi.js
================================================
const reverse = require('../__lib/reverse');
const encode = require('./__lib/encode');
const fn = async (name, age) => {
const msg = `Hi, I am ${name}, ${age} years old.`;
const reversed = reverse(msg);
const encoded = encode(msg);
return {msg, reversed, encoded};
};
module.exports = fn;
================================================
FILE: examples/05-api-directory/index.js
================================================
require('../noapi')();
================================================
FILE: examples/06-with-database/biz/about.js
================================================
// Test
// http://localhost:3000/about
// Result
// {
// "success": true,
// "data": {
// "version": "1.0.0"
// }
// }
const fn = async () => {
return {version: '1.0.0'};
};
module.exports = fn;
================================================
FILE: examples/06-with-database/biz/user/get.js
================================================
// Test url
// http://localhost:3000/user/get?username=owen
const db = require('../../db');
const fn = async (username) => {
return await db.user.select(username);
};
module.exports = fn;
================================================
FILE: examples/06-with-database/biz/user/kill.js
================================================
// Test url
// http://localhost:3000/user/kill?username=owen
const db = require('../../db');
const fn = async (username) => {
return await db.user.delete(username);
};
module.exports = fn;
================================================
FILE: examples/06-with-database/biz/user/list.js
================================================
// Test url
// http://localhost:3000/user/list
const db = require('../../db');
const fn = async () => {
return await db.user.select();
};
module.exports = fn;
================================================
FILE: examples/06-with-database/biz/user/login.js
================================================
// Test url
// http://localhost:3000/user/login?username=owen&password=123
const db = require('../../db');
const fn = async (username, password) => {
const result = await db.user.update({username, password}, {isOnline: 1});
return !!result;
};
module.exports = fn;
================================================
FILE: examples/06-with-database/biz/user/logout.js
================================================
// Test url
// http://localhost:3000/user/logout?username=owen
const db = require('../../db');
const fn = async (username) => {
const result = await db.user.update(username, {isOnline: 0});
return !!result;
};
module.exports = fn;
================================================
FILE: examples/06-with-database/biz/user/register.js
================================================
// Test url
// http://localhost:3000/user/register?username=owen&password=123
const db = require('../../db');
const fn = async (username, password) => {
return await db.user.insert({username, password});
};
module.exports = fn;
================================================
FILE: examples/06-with-database/db/index.js
================================================
const data = {
user: [
{
id: 1,
username: 'admin',
password: '123456',
isOnline: 0,
}
]
};
// Fake database
const db = {
user: {
async insert(userInfo) {
const id = data.user.length + 1;
userInfo.isOnline = 0;
userInfo.id = id;
data.user.push(userInfo);
return id;
},
async update(where, filedValues) {
const userInfo = await this.select(where);
if (!userInfo) {
return 0;
}
const keys = Object.keys(filedValues);
keys.forEach(key => {
userInfo[key] = filedValues[key];
});
return 1;
},
async delete(where) {
const userInfoId = await this.select(where, true);
if (userInfoId) {
data.user.splice(userInfoId - 1, 1);
return 1;
}
return 0;
},
async select(where, isReturnId) {
if (!where) {
return data.user;
}
else {
if (typeof where === 'string') {
where = {username: where};
}
const whereKeys = Object.keys(where);
const userInfo = data.user.find(item => {
const index = whereKeys.findIndex(key => where[key] !== item[key]);
if (index === -1) {
return item;
}
});
if (userInfo) {
return isReturnId ? userInfo.id : userInfo;
}
else {
return null;
}
}
}
}
};
module.exports = db;
================================================
FILE: examples/06-with-database/index.js
================================================
require('../noapi')();
================================================
FILE: examples/07-static-resources/biz/about.js
================================================
// Test
// http://localhost:3000/about
// Result
// {
// "success": true,
// "data": {
// "version": "1.0.0"
// }
// }
const fn = async () => {
return {version: '1.0.0'};
};
module.exports = fn;
================================================
FILE: examples/07-static-resources/index.js
================================================
require('../noapi')();
================================================
FILE: examples/07-static-resources/public/images/1.jsx
================================================
// File type .jsx is not supported
================================================
FILE: examples/07-static-resources/public/index.html
================================================
Hello, world!
================================================
FILE: examples/07-static-resources/public/nav/index.html
================================================
/nav/index.html
================================================
FILE: examples/07-static-resources/public/nav/main.html
================================================
/nav/main.html
================================================
FILE: examples/99-options/config.js
================================================
module.exports = {
name: 'myApi',
dir: './src',
host: '127.0.0.1',
port: 3001,
isSilence: true,
};
================================================
FILE: examples/99-options/index.js
================================================
const noapi = require('../noapi');
const config = require('./config');
noapi(config);
================================================
FILE: examples/99-options/src/about.js
================================================
// Test
// http://localhost:3001/about
// Result
// {
// "success": true,
// "data": {
// "version": "1.0.0"
// }
// }
const fn = async () => {
return {version: '1.0.0'};
};
module.exports = fn;
================================================
FILE: examples/noapi.js
================================================
module.exports = require('..');
================================================
FILE: index.js
================================================
module.exports = require('./src');
================================================
FILE: package.json
================================================
{
"name": "noapi",
"version": "2.2.2",
"description": "A high-performance and easy-to-use web API framework for Node.js. Noapi loads directory \"biz\" as a web API server, each file in it defines and handles an API, so that you can focus on writing business function code. Noapi is simple enough that you just take it \"out of the box\".",
"main": "index.js",
"scripts": {
"test": "node test"
},
"keywords": [
"node.js",
"api",
"microservices",
"microservices framework"
],
"author": "hi.owen.luke@gmail.com",
"license": "MIT",
"dependencies": {
"caller": "^1.0.1",
"kdo": "^1.8.1",
"keypaths": "^0.1.4",
"lodash": "^4.17.15",
"qs": "^6.9.1"
},
"devDependencies": {
"testor": "^0.6.5"
},
"repository": {
"type": "git",
"url": "https://github.com/hiowenluke/noapi.git"
},
"bugs": {
"url": "https://github.com/hiowenluke/noapi/issues"
},
"homepage": "https://github.com/hiowenluke/noapi#readme"
}
================================================
FILE: src/biz/do.js
================================================
const paramsCache = require('./paramsCache');
const data = require('../data');
const keyPaths = require('keypaths');
const fn = async (api, query) => {
const apiX = data.apisX[api]; // "/say/hi" => "say.hi"
const func = keyPaths.get(data.handlers, apiX); // say.hi()
if (typeof func !== 'function') {
return {error: `The handler ./biz${api}.js does not exists.`};
}
const names = paramsCache.getByApi(api);
const args = names.map(name => query[name]);
const result = await func(...args);
return result;
};
module.exports = fn;
================================================
FILE: src/biz/index.js
================================================
const paramsCache = require('./paramsCache');
const me = {
init() {
paramsCache.init();
}
};
module.exports = me;
================================================
FILE: src/biz/paramsCache.js
================================================
const fs = require('fs');
const data = require('../data');
const config = require('../config');
const parseParamsNames = (str) => { // "(a, b = '', c) => {}"
const temp = str.match(/\((.*?)\)/); // "a, b = '', c"
if (!temp) return null;
const params = temp[1].split(','); // ['a', " b = ''", ' c']
const names = params.map(item => item.replace(/(^\s*?(?=\b))|(\s*?$)/g, '').split('=')[0]); // ['a', 'b', 'c']
return names;
};
const me = {
data: {},
init() {
const root = config.webServiceRoot;
const bizDir = config.dir.replace(/^./, ''); // ./biz => /biz
const apis = data.apis;
apis.forEach(api => {
let filePath = root + bizDir + api;
// The filePath will be exists if it is a directory
if (!fs.existsSync(filePath)) {
// If it it not exists, then check for .js file
filePath += '.js';
// If not found, then ignore it
if (!fs.existsSync(filePath)) return;
}
// Require the directory or js file
const bizFile = require(filePath);
const content = bizFile.toString(); // "(a, b = '', c) => {}"
const names = parseParamsNames(content);
this.data[api] = names;
});
},
getByApi(api) {
return this.data[api] || [];
}
};
module.exports = me;
================================================
FILE: src/config.js
================================================
const fs = require('fs');
const path = require('path');
const me = {
name: 'default',
dir: './biz',
host: 'localhost',
port: 3000,
isSilence: false, // Do not print logs if it is true
// 0 print "Internal Server Error"
// 1 print error message and stack 1
// 2 print full error stack
onerror: 1,
webServiceRoot: '', // The root path of web service
enablePublic: '', // Enable public if the directory "public" is exists
init(pathToCaller, args = []) {
this.webServiceRoot = path.resolve(pathToCaller, '..');
this.enablePublic = fs.existsSync(this.webServiceRoot + '/public');
let name, dir, host, port, isSilence;
if (typeof args[0] === 'object') {
({name, dir, host, port, isSilence} = args[0]);
}
else {
args.forEach(arg => {
if (!arg) return;
const type = typeof arg;
if (type === 'number') {
port = arg;
}
else if (type === 'boolean') {
isSilence = arg;
}
else if (type === 'string') {
if (arg.substr(0, 1) === '.') {
dir = arg;
}
else if (arg !== 'localhost' && arg.indexOf('.') === -1) {
name = arg;
}
else {
host = arg;
}
}
});
}
name && (this.name = name);
dir && (this.dir = dir);
host && (this.host = host);
port && (this.port = port);
isSilence && (this.isSilence = isSilence);
},
};
module.exports = me;
================================================
FILE: src/data.js
================================================
const fs = require('fs');
const path = require('path');
const kdo = require('kdo');
const config = require('./config');
const keyPaths = require('keypaths');
const loadFolder = (folderName) => {
let obj;
const root = config.webServiceRoot;
const folderPath = path.resolve(root + '/' + folderName);
if (fs.existsSync(folderPath)) {
const indexJs = folderPath + '/index.js';
if (fs.existsSync(indexJs)) {
obj = require(folderPath);
}
else {
const simulateIndexJs = {filename: indexJs};
obj = kdo(simulateIndexJs);
}
}
return obj;
};
const loadFolders = () => {
const core = {};
const bizDirName = config.dir.replace(/^.\//, ''); // ./biz => biz
core.api = loadFolder('api');
core.biz = loadFolder(bizDirName);
const obj = core.api || core.biz || {};
const apiPaths = keyPaths.toPaths(obj); // ["say.hi"]
// "say.hi" => "/say/hi"
const apis = apiPaths.map(item => '/' + item.replace(/\./g, '/'));
const apisX = {};
// {"/say/hi": "say.hi"}
apis.forEach((api, index) => {apisX[api] = apiPaths[index]});
return {apis, apisX, handlers: core.biz};
};
const me = {
apis: [], // ["/say/hi"]
aha: {}, // {"/say/hi": "say.hi"}
handlers: {}, // {say: {hi: f()}}
init() {
const {apis, apisX, handlers} = loadFolders();
this.apis = apis;
this.apisX = apisX;
this.handlers = handlers;
},
};
module.exports = me;
================================================
FILE: src/index.js
================================================
const caller = require('caller');
const config = require('./config');
const data = require('./data');
const biz = require('./biz');
const server = require('./server');
const fn = (...args) => {
config.init(caller(), args);
data.init();
biz.init();
server.start();
};
module.exports = fn;
================================================
FILE: src/server/cross.js
================================================
const fn = (res) => {
// Website you wish to allow to connect
res.setHeader('Access-Control-Allow-Origin', '*');
// Request methods you wish to allow
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
// Request headers you wish to allow
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');
// Set to true if you need the website to include cookies in the requests sent
// to the API (e.g. in case you use sessions)
res.setHeader('Access-Control-Allow-Credentials', true);
};
module.exports = fn;
================================================
FILE: src/server/index.js
================================================
const http = require('http');
const config = require('../config');
const routes = require('./routes');
const processPublic = require('./public');
const done = (res, result) => {
if (typeof result === 'undefined') {
result = {success: false, error: "Internal Server Error"};
}
else if (result && result.error) {
result = {success: false, error: result.error};
}
else {
result = {success: true, data: result};
}
write(res, 'json', JSON.stringify(result));
};
const write = (res, type, str) => {
const types = {
json: 'application/json',
html: 'text/html',
};
if (type === 'html' && !/^<html>/i.test(str)) {
str = `<html><body>${str}</body></html>`;
}
res.writeHead(200, {'Content-Type': types[type], 'X-Powered-By': 'Noapi'});
res.write(str);
res.end();
};
const getQueryStr = (req) => {
return new Promise(resolve => {
let str = '';
req.on('data', (chunk) => {
str += chunk;
});
req.on('end',()=>{
resolve(str);
});
})
};
const getErrorMessages = (arr) => {
const messages = [];
arr.forEach(a => {
if (a.substr(0, 7 ) !== ' at ') {
messages.push(a);
}
});
return messages;
};
const me = {
start() {
const server = new http.Server();
server.on('request',async (req, res) => {
const url = req.url;
let [api, queryStr] = url.split('?');
if (config.enablePublic) {
const done = processPublic(api, res);
if (done) return;
}
else {
if (api === '/favicon.ico') {
return done(res, null);
}
if (api === '/') {
return done(res, 'Welcome to Noapi.');
}
}
const method = req.method.toLowerCase();
if (method === 'post') {
queryStr = await getQueryStr(req);
}
try {
let result = await routes(api, queryStr);
if (result && result.error) {
!config.isSilence && console.log(result.error);
}
done(res, result);
}
catch(e) {
const onerror = config.onerror;
!config.isSilence && console.log(e);
if (onerror === 0) {
done(res);
}
else {
const arr = e.stack.split('\n');
// print error message with stack 1
if (onerror === 1) {
const messages = getErrorMessages(arr);
const message = messages.join('\n');
let script = arr.find(a => a.substr(0, 7) === ' at ');
script = script
.replace(' at fn (', '')
.replace(config.webServiceRoot, '.')
.replace(/\)$/, '')
;
done(res, {error: {message, script}});
}
else {
// print full error stack
let str = arr.join('<br/>');
str = str.replace(/ {4}/g, ' ');
write(res, 'html', str);
}
}
}
});
const {name, host, port, isSilence} = config;
server.listen(port, () => {
if (isSilence) return;
console.log(`Server ${name} running at http://${host}:${port}`);
});
}
};
module.exports = me;
================================================
FILE: src/server/public/index.js
================================================
const fs = require('fs');
const path = require('path');
const config = require('../../config');
const mime = require('./mime');
function readFile(filePath, contentType, res) {
res.writeHead(200, { 'content-type': contentType });
const stream = fs.createReadStream(filePath);
stream.on('error', () => {
res.writeHead(500, { 'content-type': contentType });
res.end('<h1>500 Server Error</h1>')
});
stream.pipe(res);
}
const response = (res, html) => {
res.writeHead(200, { 'content-type': 'text/html' });
res.end(html);
};
const getFilePath = (pathName) => {
return path.resolve(config.webServiceRoot + '/public' + pathName);
};
const fn = (pathName, res) => {
if (pathName === '/') {
pathName = '/index.html';
}
let ext = path.extname(pathName);
ext = ext ? ext.slice(1) : '';
// non-static resources
const contentType = mime[ext];
if (!contentType) {
const filePath = getFilePath(pathName);
if (fs.existsSync(filePath)) {
// It is a directory
if (fs.statSync(filePath).isDirectory()) {
const index = filePath + '/index.html';
// no index.html
if (!fs.existsSync(index)) {
response(res, 'Directory listing is not supported');
}
else {
// load index.html
readFile(index, mime['html'], res);
}
}
else {
// It is a file
// That means its file type is not be supported
response(res, 'File type .' + ext + ' is not supported');
}
return true;
}
else {
// It is an api, then do nothing
return false;
}
}
const filePath = getFilePath(pathName);
if (!fs.existsSync(filePath)) {
// not found
response(res, pathName + ' is not found');
}
else {
// static resources
readFile(filePath, contentType, res);
}
return true;
};
module.exports = fn;
================================================
FILE: src/server/public/mime.js
================================================
const me = {
css: 'text/css',
gif: 'image/gif',
html: 'text/html',
ico: 'image/x-icon',
jpeg: 'image/jpeg',
jpg: 'image/jpeg',
js: 'text/javascript',
json: 'application/json',
pdf: 'application/pdf',
png: 'image/png',
svg: 'image/svg+xml',
swf: 'application/x-shockwave-flash',
tiff: 'image/tiff',
txt: 'text/plain',
wav: 'audio/x-wav',
wma: 'audio/x-ms-wma',
wmv: 'video/x-ms-wmv',
xml: 'text/xml'
};
module.exports = me;
================================================
FILE: src/server/routes/index.js
================================================
const config = require('../../config');
const data = require('../../data');
const bizDo = require('../../biz/do');
const parseQueryStr = require('./parseQueryStr');
const fn = async (api, queryStr) => {
const isApiExists = data.apis.indexOf(api) >= 0;
if (!isApiExists) {
return {error: `${api} is not found`};
}
const query = parseQueryStr.do(queryStr);
if (query && query.error) {
return query;
}
const result = await bizDo(api, query);
return result;
};
module.exports = fn;
================================================
FILE: src/server/routes/parseQueryStr.js
================================================
const url = require('url');
const qs = require('qs');
const parseQueryStr = (str) => {
let query;
let errArg;
let isComplexQueryStr;
try {
// From post
// name=owen&obj%5Bdate%5D=2019-05-01&arr%5B0%5D=1&arr%5B1%5D=abc&arr%5B2%5D%5Btel%5D=12345678
if (/(^|&).+?\b((%5B)|(\[))/.test(str)) {
isComplexQueryStr = 1;
query = qs.parse(str);
}
else {
// From get
// name=owen&obj={%22date%22:%222019-05-01%22}&arr=[1,%22abc%22,{%22tel%22:12345678}]
// Simulate a complete url-string so that we can use url.parse() to parse it
query = url.parse('/?' + str, true).query;
const keys = Object.keys(query);
keys.forEach(key => {
const val = query[key];
if (!/[[{]/.test(val)) return;
errArg = key;
// Processes only standard json strings, regardless of object.
query[key] = JSON.parse(val);
});
}
}
catch(e) {
console.log(e);
if (isComplexQueryStr) {
query = {error: `${str} has invalid json string.`}
}
else {
query = {error: `${errArg} is an invalid json string.`}
}
}
return query;
};
const me = {
cache: {},
do(queryStr) {
if (!queryStr) {
return {};
}
if (!this.cache[queryStr]) {
const query = parseQueryStr(queryStr);
if (query && query.error) {
return query;
}
// ECMAScript 2016 (ed. 7) established a maximum length of 2^53 - 1 elements
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/length
this.cache[queryStr] = query;
}
return this.cache[queryStr];
},
};
module.exports = me;
================================================
FILE: test/forExamples/index.js
================================================
const fs = require('fs');
const path = require('path');
const cp = require('child_process');
const main = () => {
const testorPath = path.resolve(__dirname, '../../node_modules/testor/bin/testor');
const examplesPath = path.resolve(__dirname, '../../examples');
const testCasesPath = path.resolve(__dirname, './testCases');
const exampleNames = fs.readdirSync(examplesPath);
exampleNames.forEach(exampleName => {
if (exampleName.substr(0, 1) === '.') return;
const examplePath = examplesPath + '/' + exampleName;
if (!fs.statSync(examplePath).isDirectory()) return;
const exampleTestPath = examplePath + '/test';
const targetCasesFile = exampleTestPath + '/cases.js';
fs.mkdirSync(exampleTestPath);
fs.copyFileSync(testCasesPath + '/' + exampleName + '.js', targetCasesFile);
const args = [testorPath, examplePath];
const userConfigFile = fs.existsSync(examplePath + '/config.js') ? '--config' : '';
if (userConfigFile) args.push(userConfigFile);
cp.spawnSync('node', args, {stdio: "inherit"});
fs.unlinkSync(exampleTestPath + '/cases.js');
fs.rmdirSync(exampleTestPath);
});
};
main();
================================================
FILE: test/forExamples/testCases/01-hello-world.js
================================================
const me = [
'/say/hi',
{
params: {
name: 'owen',
age: 100
},
result: {
success: true,
data: {
msg: "Hi, I am owen, 100 years old."
}
}
},
'/about',
{
result: {
success: true,
data: {
"version": "1.0.0"
}
}
},
];
module.exports = me;
================================================
FILE: test/forExamples/testCases/02-complex-url-params.js
================================================
const me = [
'/foo/bar',
{
params: {
name: "owen",
obj: {
date: "2019-05-01"
},
arr: [
1,
"abc",
{
tel: 12345678
}
]
},
result: {
success: true,
data: {
"name": "owen",
"obj": {
"date": "2019-05-01"
},
"arr": [
"1",
"abc",
{
"tel": "12345678"
}
]
}
}
},
];
module.exports = me;
================================================
FILE: test/forExamples/testCases/03-returning-an-error.js
================================================
const me = [
'/do/somethingIsWrong',
{
result: {
success: false,
error: "something is wrong"
}
},
];
module.exports = me;
================================================
FILE: test/forExamples/testCases/04-throwing-an-error.js
================================================
const me = [
'/do/say/hi',
{
verify(result) {
return result.error.message.indexOf('something is wrong') >= 0;
}
},
];
module.exports = me;
================================================
FILE: test/forExamples/testCases/05-api-directory.js
================================================
const me = [
'/say/hi',
{
params: {
name: 'owen',
age: 100
},
result: {
"success": true,
"data": {
"msg": "Hi, I am owen, 100 years old.",
"reversed": ".dlo sraey 001 ,newo ma I ,iH",
"encoded": "SGksIEkgYW0gb3dlbiwgMTAwIHllYXJzIG9sZC4="
}
}
},
'/about',
{
result: {
success: true,
data: {
"version": "1.0.0"
}
}
},
];
module.exports = me;
================================================
FILE: test/forExamples/testCases/06-with-database.js
================================================
const me = [
'/user/login?username=owen&password=123',
{
before: [
'/user/register?username=owen&password=123',
],
after: [
'/user/kill?username=owen',
],
verify(result) {
return result.data === true;
}
},
'/user/logout?username=owen',
{
before: [
'/user/register?username=owen&password=123',
'/user/login?username=owen&password=123',
],
resultUrl: '/user/get?username=owen',
after: [
'/user/kill?username=owen',
],
verify(result) {
return result.data.isOnline === 0;
}
},
];
module.exports = me;
================================================
FILE: test/forExamples/testCases/07-static-resources.js
================================================
const me = [
'/',
{
verify(result) {
return result.indexOf('Hello, world!') >= 0;
}
},
'/images',
{
verify(result) {
return result.indexOf('Directory listing is not supported') >= 0;
}
},
'/images/1.jsx',
{
verify(result) {
return result.indexOf('File type .jsx is not supported') >= 0;
}
},
'/images/guitar.png',
{
verify(result) {
return result.indexOf('\u0000') >= 0;
}
},
'/nav',
{
verify(result) {
return result.indexOf('/nav/index.html') >= 0;
}
},
'/nav/main.html',
{
verify(result) {
return result.indexOf('/nav/main.html') >= 0;
}
},
'/about',
{
success: true,
data: {
"version": "1.0.0"
}
},
];
module.exports = me;
================================================
FILE: test/forExamples/testCases/99-options.js
================================================
const me = [
'/about',
{
result: {
success: true,
data: {
"version": "1.0.0"
}
}
},
];
module.exports = me;
================================================
FILE: test/index.js
================================================
require('./forExamples');
require('./tests');
================================================
FILE: test/tests/api-folder/api/about.js
================================================
// Test
// http://localhost:3000/about
================================================
FILE: test/tests/api-folder/index.js
================================================
const noapi = require('../../..');
noapi();
================================================
FILE: test/tests/api-folder/test/cases.js
================================================
const me = [
'/about',
{
result: {
"success": false,
"error": "The handler ./biz/about.js does not exists."
}
},
];
module.exports = me;
================================================
FILE: test/tests/default/biz/get.js
================================================
const fn = async (name, obj, arr) => {
return {name, obj, arr};
};
module.exports = fn;
================================================
FILE: test/tests/default/biz/post.js
================================================
const fn = async (name, obj, arr) => {
return {name, obj, arr};
};
module.exports = fn;
================================================
FILE: test/tests/default/index.js
================================================
const noapi = require('../../..');
noapi();
================================================
FILE: test/tests/default/test/cases.js
================================================
const me = [
'/',
{
result: {
"success": true,
"data": "Welcome to Noapi."
}
},
'/xxx',
{
result: {
"success": false,
"error": "/xxx is not found"
}
},
'/get',
{
method: 'GET',
params: {
"name": "owen",
"obj": {
"date": "2019-05-01"
},
"arr": [
1,
"abc",
{
"tel": 12345678
}
]
},
result: {
"success": true,
"data": {
"name": "owen",
"obj": {
"date": "2019-05-01"
},
"arr": [
"1",
"abc",
{
"tel": "12345678"
}
]
}
}
},
'/post',
{
method: 'POST',
params: {
"name": "owen",
"obj": {
"date": "2019-05-01"
},
"arr": [
1,
"abc",
{
"tel": 12345678
}
]
},
result: {
"success": true,
"data": {
"name": "owen",
"obj": {
"date": "2019-05-01"
},
"arr": [
"1",
"abc",
{
"tel": "12345678"
}
]
}
}
}
];
module.exports = me;
================================================
FILE: test/tests/index.js
================================================
const fs = require('fs');
const path = require('path');
const cp = require('child_process');
const main = () => {
const testorPath = path.resolve(__dirname, '../../node_modules/testor/bin/testor');
const examplesPath = __dirname;
const exampleNames = fs.readdirSync(examplesPath);
exampleNames.forEach(exampleName => {
if (exampleName.substr(0, 1) === '.') return;
const examplePath = examplesPath + '/' + exampleName;
if (!fs.statSync(examplePath).isDirectory()) return;
cp.spawnSync('node', [testorPath, examplePath], {stdio: "inherit"});
});
};
main();
gitextract_cri7q73q/
├── .editorconfig
├── .gitignore
├── LICENSE
├── README.md
├── examples/
│ ├── 01-hello-world/
│ │ ├── biz/
│ │ │ ├── about.js
│ │ │ └── say/
│ │ │ └── hi.js
│ │ └── index.js
│ ├── 02-complex-url-params/
│ │ ├── biz/
│ │ │ └── foo/
│ │ │ └── bar.js
│ │ └── index.js
│ ├── 03-returning-an-error/
│ │ ├── biz/
│ │ │ └── do/
│ │ │ └── somethingIsWrong.js
│ │ └── index.js
│ ├── 04-throwing-an-error/
│ │ ├── biz/
│ │ │ └── do/
│ │ │ └── say/
│ │ │ └── hi.js
│ │ └── index.js
│ ├── 05-api-directory/
│ │ ├── api/
│ │ │ ├── about.js
│ │ │ └── say/
│ │ │ └── hi.js
│ │ ├── biz/
│ │ │ ├── __lib/
│ │ │ │ └── reverse.js
│ │ │ ├── about.js
│ │ │ └── say/
│ │ │ ├── __lib/
│ │ │ │ ├── decode.js
│ │ │ │ └── encode.js
│ │ │ └── hi.js
│ │ └── index.js
│ ├── 06-with-database/
│ │ ├── biz/
│ │ │ ├── about.js
│ │ │ └── user/
│ │ │ ├── get.js
│ │ │ ├── kill.js
│ │ │ ├── list.js
│ │ │ ├── login.js
│ │ │ ├── logout.js
│ │ │ └── register.js
│ │ ├── db/
│ │ │ └── index.js
│ │ └── index.js
│ ├── 07-static-resources/
│ │ ├── biz/
│ │ │ └── about.js
│ │ ├── index.js
│ │ └── public/
│ │ ├── images/
│ │ │ └── 1.jsx
│ │ ├── index.html
│ │ └── nav/
│ │ ├── index.html
│ │ └── main.html
│ ├── 99-options/
│ │ ├── config.js
│ │ ├── index.js
│ │ └── src/
│ │ └── about.js
│ └── noapi.js
├── index.js
├── package.json
├── src/
│ ├── biz/
│ │ ├── do.js
│ │ ├── index.js
│ │ └── paramsCache.js
│ ├── config.js
│ ├── data.js
│ ├── index.js
│ └── server/
│ ├── cross.js
│ ├── index.js
│ ├── public/
│ │ ├── index.js
│ │ └── mime.js
│ └── routes/
│ ├── index.js
│ └── parseQueryStr.js
└── test/
├── forExamples/
│ ├── index.js
│ └── testCases/
│ ├── 01-hello-world.js
│ ├── 02-complex-url-params.js
│ ├── 03-returning-an-error.js
│ ├── 04-throwing-an-error.js
│ ├── 05-api-directory.js
│ ├── 06-with-database.js
│ ├── 07-static-resources.js
│ └── 99-options.js
├── index.js
└── tests/
├── api-folder/
│ ├── api/
│ │ └── about.js
│ ├── index.js
│ └── test/
│ └── cases.js
├── default/
│ ├── biz/
│ │ ├── get.js
│ │ └── post.js
│ ├── index.js
│ └── test/
│ └── cases.js
└── index.js
SYMBOL INDEX (21 symbols across 11 files)
FILE: examples/06-with-database/db/index.js
method insert (line 16) | async insert(userInfo) {
method update (line 26) | async update(where, filedValues) {
method delete (line 40) | async delete(where) {
method select (line 50) | async select(where, isReturnId) {
FILE: src/biz/index.js
method init (line 5) | init() {
FILE: src/biz/paramsCache.js
method init (line 19) | init() {
method getByApi (line 46) | getByApi(api) {
FILE: src/config.js
method init (line 20) | init(pathToCaller, args = []) {
FILE: src/data.js
method init (line 56) | init() {
FILE: src/server/index.js
method start (line 64) | start() {
FILE: src/server/public/index.js
function readFile (line 8) | function readFile(filePath, contentType, res) {
FILE: src/server/routes/parseQueryStr.js
method do (line 54) | do(queryStr) {
FILE: test/forExamples/testCases/04-throwing-an-error.js
method verify (line 6) | verify(result) {
FILE: test/forExamples/testCases/06-with-database.js
method verify (line 14) | verify(result) {
method verify (line 32) | verify(result) {
FILE: test/forExamples/testCases/07-static-resources.js
method verify (line 6) | verify(result) {
method verify (line 13) | verify(result) {
method verify (line 20) | verify(result) {
method verify (line 27) | verify(result) {
method verify (line 34) | verify(result) {
method verify (line 41) | verify(result) {
Condensed preview — 72 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (39K chars).
[
{
"path": ".editorconfig",
"chars": 302,
"preview": "root = true\n\n# Unix-style newlines with a newline ending every file\n[*]\nend_of_line = lf\ninsert_final_newline = true\n\n\n#"
},
{
"path": ".gitignore",
"chars": 78,
"preview": "node_modules/\nmodule_test/\ncoverage/\n.DS_Store\n.idea\npackage-lock.json\n.temp/\n"
},
{
"path": "LICENSE",
"chars": 1066,
"preview": "MIT License\n\nCopyright (c) 2019 Owen Luke\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\n"
},
{
"path": "README.md",
"chars": 3521,
"preview": "\n# Noapi\n\nA high-performance and easy-to-use web API framework for [Node.js](https://nodejs.org). Noapi loads directory "
},
{
"path": "examples/01-hello-world/biz/about.js",
"chars": 215,
"preview": "\n// Test\n// http://localhost:3000/about\n\n// Result\n//\t\t{\n// \t\t\t\"success\": true,\n// \t\t\t\"data\": {\n//\t\t\t\t\"version\": \"1.0.0\""
},
{
"path": "examples/01-hello-world/biz/say/hi.js",
"chars": 399,
"preview": "\n// Test\n// http://localhost:3000/say/hi?age=100&name=owen\n\n// Result\n// \t\t{\n// \t\t\t\"success\": true,\n// \t\t\t\"data\": {\n// \t"
},
{
"path": "examples/01-hello-world/index.js",
"chars": 24,
"preview": "\nrequire('../noapi')();\n"
},
{
"path": "examples/02-complex-url-params/biz/foo/bar.js",
"chars": 446,
"preview": "\n// Test\n// http://localhost:3000/foo/bar?name=owen&obj={\"date\":\"2019-05-01\"}&arr=[1,\"abc\",{\"tel\":12345678}]\n\n// Result\n"
},
{
"path": "examples/02-complex-url-params/index.js",
"chars": 24,
"preview": "\nrequire('../noapi')();\n"
},
{
"path": "examples/03-returning-an-error/biz/do/somethingIsWrong.js",
"chars": 229,
"preview": "\n// Test\n// http://localhost:3000/do/somethingIsWrong\n\n// Result\n// \t\t{\n// \t\t\t\"success\": false,\n// \t\t\t\"error\": \"somethin"
},
{
"path": "examples/03-returning-an-error/index.js",
"chars": 24,
"preview": "\nrequire('../noapi')();\n"
},
{
"path": "examples/04-throwing-an-error/biz/do/say/hi.js",
"chars": 135,
"preview": "\n// Test\n// http://localhost:3000/do/say/hi\n\nconst fn = async () => {\n\tthrow new Error('something is wrong');\n};\n\nmodule"
},
{
"path": "examples/04-throwing-an-error/index.js",
"chars": 24,
"preview": "\nrequire('../noapi')();\n"
},
{
"path": "examples/05-api-directory/api/about.js",
"chars": 84,
"preview": "\n// This file can be empty, or with the description of this api.\n// See ./say/hi.js\n"
},
{
"path": "examples/05-api-directory/api/say/hi.js",
"chars": 395,
"preview": "/**\n * @test\n \thttp://localhost:3000/say/hi?age=100&name=owen\n\n * @returns\n\t{\n\t\t\"success\": true,\n\t\t\"data\": {\n\t\t\t\"msg\": \""
},
{
"path": "examples/05-api-directory/biz/__lib/reverse.js",
"chars": 90,
"preview": "\nconst fn = (str) => {\n\treturn str.split(\"\").reverse().join(\"\");\n};\n\nmodule.exports = fn;\n"
},
{
"path": "examples/05-api-directory/biz/about.js",
"chars": 79,
"preview": "\nconst fn = async () => {\n\treturn {version: '1.0.0'};\n};\n\nmodule.exports = fn;\n"
},
{
"path": "examples/05-api-directory/biz/say/__lib/decode.js",
"chars": 109,
"preview": "\nconst fn = (b64Encoded) => {\n\treturn Buffer.from(b64Encoded, 'base64').toString();\n};\n\nmodule.exports = fn;\n"
},
{
"path": "examples/05-api-directory/biz/say/__lib/encode.js",
"chars": 93,
"preview": "\nconst fn = (str) => {\n\treturn Buffer.from(str).toString('base64');\n};\n\nmodule.exports = fn;\n"
},
{
"path": "examples/05-api-directory/biz/say/hi.js",
"chars": 297,
"preview": "\nconst reverse = require('../__lib/reverse');\nconst encode = require('./__lib/encode');\n\nconst fn = async (name, age) =>"
},
{
"path": "examples/05-api-directory/index.js",
"chars": 24,
"preview": "\nrequire('../noapi')();\n"
},
{
"path": "examples/06-with-database/biz/about.js",
"chars": 215,
"preview": "\n// Test\n// http://localhost:3000/about\n\n// Result\n//\t\t{\n// \t\t\t\"success\": true,\n// \t\t\t\"data\": {\n//\t\t\t\t\"version\": \"1.0.0\""
},
{
"path": "examples/06-with-database/biz/user/get.js",
"chars": 193,
"preview": "\n// Test url\n// http://localhost:3000/user/get?username=owen\n\nconst db = require('../../db');\n\nconst fn = async (usernam"
},
{
"path": "examples/06-with-database/biz/user/kill.js",
"chars": 194,
"preview": "\n// Test url\n// http://localhost:3000/user/kill?username=owen\n\nconst db = require('../../db');\n\nconst fn = async (userna"
},
{
"path": "examples/06-with-database/biz/user/list.js",
"chars": 164,
"preview": "\n// Test url\n// http://localhost:3000/user/list\n\nconst db = require('../../db');\n\nconst fn = async () => {\n\treturn await"
},
{
"path": "examples/06-with-database/biz/user/login.js",
"chars": 271,
"preview": "\n// Test url\n// http://localhost:3000/user/login?username=owen&password=123\n\nconst db = require('../../db');\n\nconst fn ="
},
{
"path": "examples/06-with-database/biz/user/logout.js",
"chars": 237,
"preview": "\n// Test url\n// http://localhost:3000/user/logout?username=owen\n\nconst db = require('../../db');\n\nconst fn = async (user"
},
{
"path": "examples/06-with-database/biz/user/register.js",
"chars": 233,
"preview": "\n// Test url\n// http://localhost:3000/user/register?username=owen&password=123\n\nconst db = require('../../db');\n\nconst f"
},
{
"path": "examples/06-with-database/db/index.js",
"chars": 1279,
"preview": "\nconst data = {\n\tuser: [\n\t\t{\n\t\t\tid: 1,\n\t\t\tusername: 'admin',\n\t\t\tpassword: '123456',\n\t\t\tisOnline: 0,\n\t\t}\n\t]\n};\n\n// Fake d"
},
{
"path": "examples/06-with-database/index.js",
"chars": 24,
"preview": "\nrequire('../noapi')();\n"
},
{
"path": "examples/07-static-resources/biz/about.js",
"chars": 215,
"preview": "\n// Test\n// http://localhost:3000/about\n\n// Result\n//\t\t{\n// \t\t\t\"success\": true,\n// \t\t\t\"data\": {\n//\t\t\t\t\"version\": \"1.0.0\""
},
{
"path": "examples/07-static-resources/index.js",
"chars": 24,
"preview": "\nrequire('../noapi')();\n"
},
{
"path": "examples/07-static-resources/public/images/1.jsx",
"chars": 35,
"preview": "// File type .jsx is not supported\n"
},
{
"path": "examples/07-static-resources/public/index.html",
"chars": 14,
"preview": "Hello, world!\n"
},
{
"path": "examples/07-static-resources/public/nav/index.html",
"chars": 16,
"preview": "/nav/index.html\n"
},
{
"path": "examples/07-static-resources/public/nav/main.html",
"chars": 15,
"preview": "/nav/main.html\n"
},
{
"path": "examples/99-options/config.js",
"chars": 105,
"preview": "\nmodule.exports = {\n\tname: 'myApi',\n\tdir: './src',\n\thost: '127.0.0.1',\n\tport: 3001,\n\tisSilence: true,\n};\n"
},
{
"path": "examples/99-options/index.js",
"chars": 88,
"preview": "\nconst noapi = require('../noapi');\nconst config = require('./config');\n\nnoapi(config);\n"
},
{
"path": "examples/99-options/src/about.js",
"chars": 215,
"preview": "\n// Test\n// http://localhost:3001/about\n\n// Result\n//\t\t{\n// \t\t\t\"success\": true,\n// \t\t\t\"data\": {\n//\t\t\t\t\"version\": \"1.0.0\""
},
{
"path": "examples/noapi.js",
"chars": 33,
"preview": "\nmodule.exports = require('..');\n"
},
{
"path": "index.js",
"chars": 36,
"preview": "\nmodule.exports = require('./src');\n"
},
{
"path": "package.json",
"chars": 989,
"preview": "{\n \"name\": \"noapi\",\n \"version\": \"2.2.2\",\n \"description\": \"A high-performance and easy-to-use web API framework for No"
},
{
"path": "src/biz/do.js",
"chars": 543,
"preview": "\nconst paramsCache = require('./paramsCache');\nconst data = require('../data');\nconst keyPaths = require('keypaths');\n\nc"
},
{
"path": "src/biz/index.js",
"chars": 121,
"preview": "\nconst paramsCache = require('./paramsCache');\n\nconst me = {\n\tinit() {\n\t\tparamsCache.init();\n\t}\n};\n\nmodule.exports = me;"
},
{
"path": "src/biz/paramsCache.js",
"chars": 1216,
"preview": "\nconst fs = require('fs');\nconst data = require('../data');\nconst config = require('../config');\n\nconst parseParamsNames"
},
{
"path": "src/config.js",
"chars": 1361,
"preview": "\nconst fs = require('fs');\nconst path = require('path');\n\nconst me = {\n\tname: 'default',\n\tdir: './biz',\n\thost: 'localhos"
},
{
"path": "src/data.js",
"chars": 1365,
"preview": "\nconst fs = require('fs');\nconst path = require('path');\nconst kdo = require('kdo');\n\nconst config = require('./config')"
},
{
"path": "src/index.js",
"chars": 298,
"preview": "\nconst caller = require('caller');\nconst config = require('./config');\n\nconst data = require('./data');\nconst biz = requ"
},
{
"path": "src/server/cross.js",
"chars": 577,
"preview": "\nconst fn = (res) => {\n\n\t// Website you wish to allow to connect\n\tres.setHeader('Access-Control-Allow-Origin', '*');\n\n\t/"
},
{
"path": "src/server/index.js",
"chars": 2870,
"preview": "\nconst http = require('http');\n\nconst config = require('../config');\nconst routes = require('./routes');\nconst processPu"
},
{
"path": "src/server/public/index.js",
"chars": 1770,
"preview": "\nconst fs = require('fs');\nconst path = require('path');\n\nconst config = require('../../config');\nconst mime = require('"
},
{
"path": "src/server/public/mime.js",
"chars": 443,
"preview": "\nconst me = {\n\tcss: 'text/css',\n\tgif: 'image/gif',\n\thtml: 'text/html',\n\tico: 'image/x-icon',\n\tjpeg: 'image/jpeg',\n\tjpg: "
},
{
"path": "src/server/routes/index.js",
"chars": 496,
"preview": "\nconst config = require('../../config');\nconst data = require('../../data');\nconst bizDo = require('../../biz/do');\ncons"
},
{
"path": "src/server/routes/parseQueryStr.js",
"chars": 1558,
"preview": "\nconst url = require('url');\nconst qs = require('qs');\n\nconst parseQueryStr = (str) => {\n\tlet query;\n\tlet errArg;\n\tlet "
},
{
"path": "test/forExamples/index.js",
"chars": 1129,
"preview": "\nconst fs = require('fs');\nconst path = require('path');\nconst cp = require('child_process');\n\nconst main = () => {\n\tcon"
},
{
"path": "test/forExamples/testCases/01-hello-world.js",
"chars": 289,
"preview": "\nconst me = [\n\n\t'/say/hi',\n\t{\n\t\tparams: {\n\t\t\tname: 'owen',\n\t\t\tage: 100\n\t\t},\n\n\t\tresult: {\n\t\t\tsuccess: true,\n\t\t\tdata: {\n\t\t"
},
{
"path": "test/forExamples/testCases/02-complex-url-params.js",
"chars": 393,
"preview": "\nconst me = [\n\n\t'/foo/bar',\n\t{\n\t\tparams: {\n\t\t\tname: \"owen\",\n\t\t\tobj: {\n\t\t\t\tdate: \"2019-05-01\"\n\t\t\t},\n\t\t\tarr: [\n\t\t\t\t1,\n\t\t\t\t"
},
{
"path": "test/forExamples/testCases/03-returning-an-error.js",
"chars": 138,
"preview": "\nconst me = [\n\n\t'/do/somethingIsWrong',\n\t{\n\t\tresult: {\n\t\t\tsuccess: false,\n\t\t\terror: \"something is wrong\"\n\t\t}\n\t},\n];\n\nmod"
},
{
"path": "test/forExamples/testCases/04-throwing-an-error.js",
"chars": 152,
"preview": "\nconst me = [\n\n\t'/do/say/hi',\n\t{\n\t\tverify(result) {\n\t\t\treturn result.error.message.indexOf('something is wrong') >= 0;\n\t"
},
{
"path": "test/forExamples/testCases/05-api-directory.js",
"chars": 403,
"preview": "\nconst me = [\n\n\t'/say/hi',\n\t{\n\t\tparams: {\n\t\t\tname: 'owen',\n\t\t\tage: 100\n\t\t},\n\n\t\tresult: {\n\t\t\t\"success\": true,\n\t\t\t\"data\": "
},
{
"path": "test/forExamples/testCases/06-with-database.js",
"chars": 559,
"preview": "\nconst me = [\n\n\t'/user/login?username=owen&password=123',\n\t{\n\t\tbefore: [\n\t\t\t'/user/register?username=owen&password=123',"
},
{
"path": "test/forExamples/testCases/07-static-resources.js",
"chars": 707,
"preview": "\nconst me = [\n\n\t'/',\n\t{\n\t\tverify(result) {\n\t\t\treturn result.indexOf('Hello, world!') >= 0;\n\t\t}\n\t},\n\n\t'/images',\n\t{\n\t\tver"
},
{
"path": "test/forExamples/testCases/99-options.js",
"chars": 130,
"preview": "\nconst me = [\n\t'/about',\n\t{\n\t\tresult: {\n\t\t\tsuccess: true,\n\t\t\tdata: {\n\t\t\t\t\"version\": \"1.0.0\"\n\t\t\t}\n\t\t}\n\t},\n];\n\nmodule.expo"
},
{
"path": "test/index.js",
"chars": 47,
"preview": "\nrequire('./forExamples');\nrequire('./tests');\n"
},
{
"path": "test/tests/api-folder/api/about.js",
"chars": 40,
"preview": "\n// Test\n// http://localhost:3000/about\n"
},
{
"path": "test/tests/api-folder/index.js",
"chars": 45,
"preview": "\nconst noapi = require('../../..');\nnoapi();\n"
},
{
"path": "test/tests/api-folder/test/cases.js",
"chars": 152,
"preview": "\nconst me = [\n\t'/about',\n\t{\n\t\tresult: {\n\t\t\t\"success\": false,\n\t\t\t\"error\": \"The handler ./biz/about.js does not exists.\"\n\t"
},
{
"path": "test/tests/default/biz/get.js",
"chars": 91,
"preview": "\nconst fn = async (name, obj, arr) => {\n\treturn {name, obj, arr};\n};\n\nmodule.exports = fn;\n"
},
{
"path": "test/tests/default/biz/post.js",
"chars": 91,
"preview": "\nconst fn = async (name, obj, arr) => {\n\treturn {name, obj, arr};\n};\n\nmodule.exports = fn;\n"
},
{
"path": "test/tests/default/index.js",
"chars": 45,
"preview": "\nconst noapi = require('../../..');\nnoapi();\n"
},
{
"path": "test/tests/default/test/cases.js",
"chars": 970,
"preview": "\nconst me = [\n\t'/',\n\t{\n\t\tresult: {\n\t\t\t\"success\": true,\n\t\t\t\"data\": \"Welcome to Noapi.\"\n\t\t}\n\t},\n\n\t'/xxx',\n\t{\n\t\tresult: {\n\t"
},
{
"path": "test/tests/index.js",
"chars": 575,
"preview": "\nconst fs = require('fs');\nconst path = require('path');\nconst cp = require('child_process');\n\nconst main = () => {\n\tcon"
}
]
About this extraction
This page contains the full source code of the hiowenluke/noapi GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 72 files (30.1 KB), approximately 11.5k tokens, and a symbol index with 21 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.