Reputation: 1049
I'm building a SDK for an API using NodeJS, which can be found here. My problem is that when the user declare the module, it gives an username and password that I need to validate, and a token that must be used for future calls. So, this token is stored at irecarga.token, and for every future call I'll have to use it in order to identify the user. My problem is that if the user calls another function straight after the declaration, the declaration will probably not finish in time (because it does a HTTP POST) and the attribute token will be null.
// module declaration which requires a HTTP call and updates irecarga.token
var irecarga = require('../')({
username: process.env.IRECARGA_USERNAME,
password: process.env.IRECARGA_PASSWORD
})
// function called straight after declaration which uses irecarga.token
irecarga.getServiceProviders(48, function(err, data){
// this code won't even run because the token = null will break the code
console.log('err: ', err)
console.log('data', data)
})
So, I saw plenty of solutions for creating blocking functions with Node, I could use callbacks or other modules that would require to send the functions I want to execute as parameter for other functions.
These solutions would most likely work, but the code will be ugly and messy. Besides, I don't think I'm innovating, actually this is the way I saw big companies like Microsoft and Google to declare their API keys.
Am I missing something here? Is there anything I could add inside of the validation function that would make any method of iRecarga wait until validation is done?
Upvotes: 2
Views: 2312
Reputation: 6377
Using await
you can add a one-liner to each of your API methods that will wait for the initializatoin (authentication) to complete by waiting for a promise to resolve. Here is one way you can do it. I use latest syntax with babel
.
// myapi.js
import login from './auth';
import {query, insert} from './db';
let authenticated = null, user = null;
async function getProviders({regionId}) {
await authenticated;
return await query({region:regionId});
}
async function order({provider, service}) {
await authenticated;
return await insert({entity:'orders'}, {service, user});
}
export default function ({username, password}) {
authenticated = new Promise( async (resolve, reject) => {
const valid = await login({username, password});
if (valid) {
user = username;
resolve();
} else {
reject();
}
});
return {getProviders, order};
}
// test/myapi.js
import myapi from '../myapi';
async function test() {
let api = myapi({username:'tom', password:'1234'});
let providers = await api.getProviders({regionId:48});
console.log(providers);
let providers2 = await api.getProviders({regionId:5});
console.log(providers2);
}
test().catch(console.error);
Upvotes: 0
Reputation: 707158
In node.js, you don't make async things into blocking things. Instead, you use them as async and create an async interface on top of them.
So, you need to provide an async interface for your initialization so the caller knows when the initialization is done and when it is safe or possible to call other methods. There are a lot of different ways to do that:
Return a promise from require()()
with the resolved value being your module object. Then, the caller does a .then()
and within that callback can use your properly initialized module.
Pass a callback into the module initialization and require all use of the module to be from within that callback (same concept as the promise above, just using regular callbacks).
Don't pass the credentials to the constructor. Instead, create an async .login()
method that returns a promise and instruct the callers not to use the interface except within the resolved login promise.
For example, it could look like this:
require('../')({
username: process.env.IRECARGA_USERNAME,
password: process.env.IRECARGA_PASSWORD
}).then(function(irecarga) {
// function called straight after declaration which uses irecarga.token
// this method should probably be changed to use promises
irecarga.getServiceProviders(48, function(err, data){
console.log('err: ', err)
console.log('data', data)
});
}).catch(function(err) {
// handle intiialization error here
});
Upvotes: 2