Skip to content

Commit 4c3659b

Browse files
committed
More cli commands
1 parent 8a16ba9 commit 4c3659b

File tree

4 files changed

+152
-12
lines changed

4 files changed

+152
-12
lines changed

README.md

+28-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Simple API Router
22

33
## Overview
4-
**Simple API Router** is a lightweight npm package that simplifies setting up Express APIs. It automatically detects all API routes in a defined directory and binds them to an Express app.
4+
**Simple API Router** is a lightweight npm package that simplifies setting up Express APIs. It automatically detects all API routes in a defined directory and binds them to an Express app. Additionally, it provides an API client generator for frontend integration and automatic API documentation generation.
55

66
## Installation
77

@@ -37,7 +37,7 @@ app.listen(PORT, () => {
3737
The package allows easy creation of a sample project with a predefined structure:
3838

3939
```bash
40-
npx n-sar my-api-project
40+
npx n-sar create my-api-project
4141
```
4242

4343
This creates a new folder `my-api-project` with the following structure:
@@ -59,6 +59,32 @@ npm install
5959
node server.js
6060
```
6161

62+
### 3. Generate an API Client for Frontend Integration
63+
64+
With `n-sar`, you can automatically generate a frontend API client that connects to your backend (localhost or your server domain/IP):
65+
66+
```bash
67+
npx n-sar export-routes http://localhost:3000
68+
```
69+
70+
This creates an `apiClient.js` file with functions for each detected route, making API calls simple:
71+
72+
```javascript
73+
import APIClient from './apiClient';
74+
75+
APIClient.user_get().then(response => console.log(response));
76+
```
77+
78+
### 4. Generate API Documentation
79+
80+
You can automatically generate API documentation based on your routes:
81+
82+
```bash
83+
npx n-sar generate-docs
84+
```
85+
86+
This will create an `API_DOCS.md` file containing all detected API endpoints and their methods.
87+
6288
## API Definitions
6389

6490
The package expects API files to be located in the `api/` directory. Each file corresponds to a route. Example:

bin/cli.js

+104-4
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,15 @@ module.exports = (app) => {
4343
'api/user.js': `const express = require('express');
4444
const router = express.Router();
4545
46+
/**
47+
* @openapi
48+
* /user:
49+
* get:
50+
* description: Returns a user endpoint
51+
* responses:
52+
* 200:
53+
* description: A user object
54+
*/
4655
router.get('/', (req, res) => {
4756
res.json({ message: 'User endpoint' });
4857
});
@@ -55,21 +64,112 @@ function createProject() {
5564
console.error('Error: Project folder already exists.');
5665
process.exit(1);
5766
}
58-
67+
5968
fs.mkdirSync(projectPath, { recursive: true });
6069
fs.mkdirSync(path.join(projectPath, 'api'));
61-
70+
6271
for (const [file, content] of Object.entries(files)) {
6372
const filePath = path.join(projectPath, file);
6473
fs.mkdirSync(path.dirname(filePath), { recursive: true });
6574
fs.writeFileSync(filePath, content);
6675
}
67-
76+
6877
console.log('Example project created!');
6978
console.log(`Run:
7079
cd ${projectName}
7180
npm install
7281
node server.js`);
7382
}
7483

75-
createProject();
84+
function scanRoutes(directory, baseRoute = '') {
85+
let routes = [];
86+
fs.readdirSync(directory).forEach(file => {
87+
const fullPath = path.join(directory, file);
88+
const routePath = `${baseRoute}/${file.replace('.js', '')}`;
89+
90+
if (fs.statSync(fullPath).isDirectory()) {
91+
routes = routes.concat(scanRoutes(fullPath, routePath));
92+
} else if (file.endsWith('.js') && file !== 'router.js') {
93+
routes.push(routePath);
94+
}
95+
});
96+
return routes;
97+
}
98+
99+
function getRouteMethod(routeFile) {
100+
if (!fs.existsSync(routeFile)) return [];
101+
102+
const content = fs.readFileSync(routeFile, 'utf8');
103+
let methods = [];
104+
105+
if (content.includes("router.get(")) methods.push("get");
106+
if (content.includes("router.post(")) methods.push("post");
107+
if (content.includes("router.put(")) methods.push("put");
108+
if (content.includes("router.delete(")) methods.push("delete");
109+
110+
return methods;
111+
}
112+
113+
function generateAPIClient(routes, outputPath, baseURL) {
114+
const clientCode = `class APIClient {
115+
constructor(baseURL) {
116+
this.baseURL = baseURL;
117+
}
118+
119+
async request(endpoint, options = {}) {
120+
const response = await fetch(
121+
\`\${this.baseURL}\${endpoint}\`,
122+
{ ...options, headers: { 'Content-Type': 'application/json', ...options.headers } }
123+
);
124+
return response.json();
125+
}
126+
127+
${routes
128+
.map(route => {
129+
const routeFile = path.join(process.cwd(), 'api', route.replace(/\//g, path.sep) + '.js');
130+
const methods = getRouteMethod(routeFile);
131+
132+
return methods
133+
.map(method => ` async ${route.replace(/\//g, '_').replace('-', '_')}_${method}() {
134+
return this.request('${route}', { method: '${method.toUpperCase()}' });
135+
}`)
136+
.join('\n');
137+
})
138+
.join('\n')}
139+
}
140+
141+
export default new APIClient('${baseURL}');`;
142+
143+
fs.writeFileSync(outputPath, clientCode);
144+
console.log(`API client generated at ${outputPath}`);
145+
}
146+
147+
function generateDocs() {
148+
const apiDir = path.join(process.cwd(), 'api');
149+
if (!fs.existsSync(apiDir)) {
150+
console.error('Error: API directory does not exist.');
151+
process.exit(1);
152+
}
153+
154+
const routes = scanRoutes(apiDir);
155+
let docContent = '# API Documentation\n\n## Endpoints\n';
156+
157+
routes.forEach(route => {
158+
docContent += `### \`${route}\`\n- **GET**: Fetch data from ${route}\n- **POST**: Send data to ${route}\n- **PUT**: Update data at ${route}\n- **DELETE**: Remove data at ${route}\n\n`;
159+
});
160+
161+
fs.writeFileSync(path.join(process.cwd(), 'API_DOCS.md'), docContent);
162+
console.log('API_DOCS.md generated successfully.');
163+
}
164+
165+
if (process.argv[2] === 'export-routes') {
166+
const apiDir = path.join(process.cwd(), 'api');
167+
const routes = scanRoutes(apiDir);
168+
const outputPath = path.join(process.cwd(), 'apiClient.js');
169+
const baseURL = process.argv[3] || 'http://localhost:3000';
170+
generateAPIClient(routes, outputPath, baseURL);
171+
} else if (process.argv[2] === 'generate-docs') {
172+
generateDocs();
173+
} else {
174+
createProject();
175+
}

package-lock.json

+18-5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@notnexx/n-sar",
3-
"version": "1.0.1",
3+
"version": "1.0.2",
44
"description": "A simple API router for Express.js",
55
"main": "index.js",
66
"keywords": [
@@ -11,6 +11,7 @@
1111
"author": "NotNexx",
1212
"license": "MIT",
1313
"dependencies": {
14+
"@notnexx/n-sar": "^1.0.2",
1415
"express": "^4.21.2"
1516
},
1617
"devDependencies": {

0 commit comments

Comments
 (0)