I am developing an interface api that receives a request from a client to that external api, and redirects it to it.
That api has a database file (userData.json) to store the users data, including their api keys.
The api has the routes to the external api, and only one more route to create a new user from the interface api (createUser). This route receives the request to create a new user (createUser) that will generate a unique user api key, and store all the user data, including this key, in an array in userData.json.
Locally, I managed to create that api with success. But when I tried to put it to work in Vercel, I realized Vercel doesn't allow to change existing files from serverless functions.
Then I came up to this article, that explains how to do this with json-server: https://ivo-culic.medium.com/create-restful-api-with-json-server-and-deploy-it-to-vercel-d56061c1157a
To adapt this example to my case, I have put the app requests to the external api (express) inside a server.use() (json-server).
When I start the api with node I am able to redirect the requests to the external api, but receive a 404 for any json-server request.
When I start the api with json server, I am able to go through with json-server requests, but not with the external api requests (app).
I am still working locally, so it has nothing to do with Vercel.
How can I make it go through with both types of requests at the same time?
This is the code of the server.js file. I just left one route to the external api as an example:
// Import Express.js
import http from 'https'
import express from 'express'
import jsonServer from 'json-server'
import path from 'path'
import {authenticateKey, createUser} from './api/apiAuth.js'
import {ixApi} from './ixApi.js'
// This variable defines the port of your computer where the API will be available
const PORT = 3000
// This variable instantiate the Express.js library
const app = express()
app.use(express.json());
app.use(
express.urlencoded({
extended: true,
})
);
// JSON Server module
const server = jsonServer.create();
const router = jsonServer.router("db.json");
//const router = jsonServer.router(path.join("", 'db.json'))
// Make sure to use the default middleware
const middlewares = jsonServer.defaults();
server.use(middlewares);
// Add this before server.use(router)
server.use(
/**
* Welcome message
*/
app.get('/', (request, response) => {
// The string we want to display on http://localhost:3000
response.send('Welcome on the ix interface API!')
}),
/**
* @function create-user - Creates new user
*/
app.post('/create-user', authenticateKey, async (req, res) => {
//create a new user with "user:Username"
let username = req.body.username
let appName = req.body.appName
let userEmail = req.body.userEmail
let IX_ACCOUNT_NAME = req.body.IX_ACCOUNT_NAME
let IX_API_KEY = req.body.IX_API_KEY
let user = await createUser(username, appName, userEmail, IX_ACCOUNT_NAME, IX_API_KEY, req, res)
console.log('USER 1:')
console.log(user)
res.status(201).send({ data: user });
}),
/**
* @function createInvoice - Creates invoice
*
* CALLED BY:
* -
*
* TO IMPROVE:
* -
*/
app.post('/create-invoice', authenticateKey, async (request, response) => {
console.log(response.locals.ixCredentials)
console.log(request.body)
console.log(request.body.invoiceData)
const IX_ACCOUNT_NAME = response.locals.ixCredentials.IX_ACCOUNT_NAME
const IX_API_KEY = response.locals.ixCredentials.IX_API_KEY
const invoiceData = request.body.invoiceData
const items = request.body.invoiceData.items
const creationRequest = () => {
const response = new Promise ((resolve) => {
var options = {
"method": "POST",
"hostname": IX_ACCOUNT_NAME + ".app.ix.com",
"port": null,
"path": "/invoices.json?api_key=" + IX_API_KEY,
"headers": {
"accept": "application/json",
"content-type": "application/json"
}
};
var req = http.request(options, function (res) {
var chunks = [];
res.on("data", function (chunk) {
console.log('CHUNK')
console.log(chunk.toString)
chunks.push(chunk);
});
res.on("end", function () {
var body = Buffer.concat(chunks);
console.log('IX BODY:')
console.log(body.toString());
resolve(body.toString())
});
console.log(res.statusCode)
})
var itemsList = []
for (let i = 0; i < items.length; i++) {
itemsList.push({
name: items[i].name,
description: items[i].description,
unit_price: items[i].price,
quantity: items[i].quantity,
})
console.log(itemsList)
}
req.write(JSON.stringify({ invoice: {
date: invoiceData.date,
due_date: invoiceData.due_date,
client: {
name: invoiceData.client.name,
email: invoiceData.client.email,
country: invoiceData.client.country,
fiscal_id: invoiceData.client.vat,
code: invoiceData.client.vat,
},
items: itemsList,
}}));
req.on('error', function(err) {
console.log('IX ERROR:')
console.log(err)
//return response.json({ success: false })
});
console.log('SENDING REQUEST:')
req.end();
})
console.log('RESPONSE')
console.log(response)
return response
}
const returnedData = await creationRequest()
.then (response => {
console.log('INVOICE CREATED')
var obj = JSON.parse(response)
console.log(obj)
return obj
});
console.log('RETURNED DATA')
console.log(returnedData)
return response.json(returnedData)
})
)
server.use(
jsonServer.rewriter({
"/api/*": "/$1",
})
)
server.use(router);
/**
* The code below starts the API with these parameters:
* 1 - The PORT where your API will be available
* 2 - The callback function (function to call) when your API is ready
*/
server.listen(PORT, () =>
console.log(`The ix API is running on: http://localhost:${PORT}.`)
)
// Export the Server API
export {server}
I tried to use the middleware function of json-server to solve the issue, even though I am not sure if this function is appropriate for that, and found no success.
I would be grateful for your help, since I am for several days now trying to find a solution.
Thanks!
The issue is caused by the middleware created by jsonServer.defaults([options])
. Remove following statements will work:
// const middlewares = jsonServer.defaults({
// static: undefined,
// });
// server.use(middlewares);
For some reason, the json-server
publish the public
directory with index.html
file in their package. See directory structure of node_modules/json-server
:
☁ json-server [master] ⚡ tree -L 2 -I 'node_modules'
.
├── LICENSE
├── README.md
├── lib
│ ├── cli
│ └── server
├── package.json
└── public
├── favicon.ico
├── index.html
├── script.js
└── style.css
4 directories, 7 files
When you use the middlewares created by jsonServer.defaults([options])
, it will serve static files, see v0.17.3/src/server/defaults.js#L35
const defaultDir = path.join(__dirname, '../../public');
// ...
arr.push(express.static(opts.static))
The default static file directory is public
, so when you access the http://localhost:3000/
, the public/index.html
document will be returned to the client instead of hitting the API endpoint.
A working example for both JSON server API and custom routes.
import express, { Router } from 'express';
import jsonServer from 'json-server';
import path from 'path';
const PORT = 3000;
const app = express();
app.use(express.json());
app.use(
express.urlencoded({
extended: true,
})
);
const server = jsonServer.create();
const router = jsonServer.router(path.join(__dirname, 'db.json'));
// const middlewares = jsonServer.defaults({
// static: undefined,
// });
// server.use(middlewares);
const customRouter = Router();
customRouter.get('/', (req, res) => {
res.send('Welcome on the ix interface API');
});
customRouter.get('/create-user', (req, res) => {
res.status(201).json({ data: { name: 'teresa teng' } });
})
server.use(customRouter);
server.use(
jsonServer.rewriter({
"/api/*": "/$1",
})
)
server.use(router);
server.listen(PORT, () => {
console.log('JSON Server is running')
});
db.json
:
{
"posts": [
{ "id": 1, "title": "json-server", "author": "typicode" }
],
"comments": [
{ "id": 1, "body": "some comment", "postId": 1 }
],
"profile": { "name": "typicode" }
}
Testing custom routes:
⚡ curl -X GET http://localhost:3000
Welcome on the ix interface API%
⚡ curl -X GET http://localhost:3000/create-user
{
"data": {
"name": "teresa teng"
}
}%
Testing JSON server API:
⚡ curl -X GET http://localhost:3000/api/posts/1
{
"id": 1,
"title": "json-server",
"author": "typicode"
}%