api extensions added.

This commit is contained in:
Vahid Al
2022-11-07 17:57:33 +03:30
parent 9b26b84652
commit 1c83ff52a2
10 changed files with 255 additions and 4 deletions

3
.gitignore vendored
View File

@@ -1,2 +1,3 @@
*.db-*
*Chinook*
*Chinook*
_extensions/

View File

@@ -43,6 +43,11 @@ There's also a list of all endpoints examples at [docs/api-examples.md](docs/api
For websocket examples, check [docs/ws-examples.md](docs/ws-examples.md)
## Extending Soul
Soul is able to be extended (e.g. Adding custom APIs) via extensions, you can find a list of extensions at [docs/extensions-examples.md](docs/extensions-examples.md)
## Development
```bash

View File

@@ -1,6 +1,6 @@
{
"name": "soul-cli",
"version": "0.1.0",
"version": "0.2.0",
"description": "A SQLite REST and Realtime server",
"main": "src/server.js",
"bin": {

View File

@@ -34,6 +34,12 @@ if (process.env.NO_CLI !== 'true') {
demandOption: false,
choices: ['console', null],
})
.options('e', {
alias: 'extensions',
describe: 'Extensions directory path to load',
type: 'string',
demandOption: false,
})
.help(true).argv;
}

View File

@@ -24,6 +24,8 @@ const envVarsSchema = Joi.object()
RATE_LIMIT_ENABLED: Joi.boolean().default(false),
RATE_LIMIT_WINDOW_MS: Joi.number().positive().default(1000),
RATE_LIMIT_MAX_REQUESTS: Joi.number().positive().default(10),
EXTENSIONS: Joi.string().default(null),
})
.unknown();
@@ -76,4 +78,8 @@ module.exports = {
windowMs: envVars.RATE_LIMIT_WINDOW,
max: envVars.RATE_LIMIT_MAX,
},
extensions: {
path: argv.extensions || envVars.EXTENSIONS,
},
};

47
core/src/extensions.js Normal file
View File

@@ -0,0 +1,47 @@
const fs = require('fs');
const { extensions: extensionsConfig } = require('./config');
const { path: extensionsPath } = extensionsConfig;
const setupExtensions = async (app, db) => {
if (extensionsPath) {
const extensions = fs.readdirSync(extensionsPath);
extensions.forEach((extension) => {
if (extension === 'api.js') {
const apiExtensions = require(`${extensionsPath}/${extension}`);
console.log('API extensions loaded');
Object.keys(apiExtensions).forEach((key) => {
const api = apiExtensions[key];
switch (api.method) {
case 'GET':
app.get(api.path, (req, res) => api.handler(req, res, db));
break;
case 'POST':
app.post(api.path, api.handler);
break;
case 'PUT':
app.put(api.path, api.handler);
break;
case 'DELETE':
app.delete(api.path, api.handler);
break;
default:
break;
}
console.log(' >', api.path);
});
console.log('\n');
}
});
} else {
console.log('No extensions directory provided');
}
};
module.exports = {
setupExtensions,
};

View File

@@ -14,9 +14,14 @@ const rootRoutes = require('./routes/index');
const tablesRoutes = require('./routes/tables');
const rowsRoutes = require('./routes/rows');
const swaggerFile = require('./swagger/swagger.json');
const { setupExtensions } = require('./extensions');
const app = express();
app.get('/health', (req, res) => {
res.send('OK');
});
app.use(bodyParser.json());
// Activate wal mode
@@ -63,4 +68,6 @@ app.use('/api', rootRoutes);
app.use('/api/tables', tablesRoutes);
app.use('/api/tables', rowsRoutes);
setupExtensions(app, db);
module.exports = app;

View File

@@ -1,9 +1,9 @@
{
"swagger": "2.0",
"info": {
"version": "0.1.0",
"version": "0.2.0",
"title": "Soul API",
"description": "API Documentation for <b>Soul</b>, a simple REST and realtime server. "
"description": "API Documentation for <b>Soul</b>, a SQLite REST and realtime server. "
},
"host": "localhost:8000",
"basePath": "/",
@@ -33,6 +33,17 @@
"application/json"
],
"paths": {
"/health": {
"get": {
"description": "",
"parameters": [],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/api/": {
"get": {
"tags": [

168
docs/extensions-examples.md Normal file
View File

@@ -0,0 +1,168 @@
# Extensions Examples
Soul extensions are a way to extend the functionality of Soul. Extensions are written in JavaScript and can be used to add new endpoints, modify existing endpoints, or add new functionality to Soul.
## Types of Extensions
- API Extensions: Add new endpoints to Soul
## Setup Environment
To follow the below examples we need to download a sample database and also install Soul CLI.
### Download Sample Database
```bash
wget https://raw.githubusercontent.com/lerocha/chinook-database/master/ChinookDatabase/DataSources/Chinook_Sqlite.sqlite # Download sample sqlite database
```
### Using Soul CLI
```bash
npm install -g soul-cli
soul -d ./Chinook_Sqlite.sqlite -p 8000 -e "/path/to/_extensions/"
```
<details>
<summary>Or Using Local Development</summary>
```bash
git clone https://github.com/thevahidal/soul # Clone project
cd core/
npm install # Install dependencies
npm link # might need `sudo`
soul -d ./Chinook_Sqlite.sqlite -p 8000 -e "/path/to/_extensions/"
```
</details>
## Creating an API extension
To create an extension, create a new folder named `_extensions`. Then create a file named `api.js` inside it. This file will contain the extension code.
```js
const hello = {
method: 'GET',
path: '/api/hello-soul',
handler: (req, res, db) => {
res.status(200).json({
message: 'Hello Soul!'
});
},
};
const timestamp = {
method: 'GET',
path: '/api/timestamp',
handler: (req, res, db) => {
res.status(200).json({
timestamp: Date.now(),
});
},
};
const greetings = {
method: 'POST',
path: '/api/greetings/:name',
handler: (req, res, db) => {
const { name } = req.params;
const { greeting } = req.body;
res.status(200).json({
message: `${greeting} ${name}!`,
});
},
}
const searchTables = {
method: 'GET',
path: '/api/search-tables',
handler: (req, res, db) => {
const { q } = req.query;
const sql = `
SELECT name FROM sqlite_master
WHERE type='table'
AND name LIKE $searchQuery
`;
try {
const tables = db.prepare(sql).all({
searchQuery: `%${q}%`,
});
res.status(200).json({
tables,
});
} catch (error) {
res.status(500).json({
error: error.message,
});
}
},
};
module.exports = {
hello,
timestamp,
greetings,
searchTables,
};
```
Alright, now we can test if the extension is working:
```bash
curl http://localhost:8000/api/hello-soul
```
It should return:
```json
{
"message": "Hello Soul!"
}
```
And the same for the `timestamp` endpoint:
```bash
curl http://localhost:8000/api/timestamp
```
It should return:
```json
{
"timestamp": 1620000000000
}
```
And `greetings` endpoint:
```bash
curl -X POST -H "Content-Type: application/json" -d '{"greeting": "Hello"}' http://localhost:8000/api/greetings/John
```
It should return:
```json
{
"message": "Hello John!"
}
```
And `list-tables` endpoint:
```bash
curl http://localhost:8000/api/search-tables?q=al
```
It should return:
```json
{
"tables": [
{
"name": "Album"
}
]
}
```

View File