2

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?

5
  • Why don't you use promise. If you do not want it to be async, then use library like wait.for which makes your code execution sequential.But i will prefer to use promise any day. Commented Oct 5, 2016 at 2:03
  • Take a look at this: stackoverflow.com/questions/20315434/… Answer describes several great patterns. Ultimately you'll have to use one of them, no way to avoid a callback (or promise as above comment suggests). But it does not necessarily have to make your code messy I don't think. Commented Oct 5, 2016 at 2:07
  • why you need to make a post request when setting the variables? Commented Oct 5, 2016 at 3:40
  • @Orlando these variables are login and password, in order to check if they are valid I POST and address and get a token that I can use for future requests. Commented Oct 5, 2016 at 19:30
  • @LevonTamrazov your link was helpfull thanks Commented Oct 5, 2016 at 21:33

2 Answers 2

2

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:

  1. 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.

  2. 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).

  3. 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
});
Sign up to request clarification or add additional context in comments.

1 Comment

Why the downvote? Downvotes without offering a reason don't help people make better answers.
0

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);

8 Comments

Answers are always better if they also include some explanation about the OP's issue rather than just code.
I'm choosing this as a correct answer because await seems to be the best solution for my question, so the only requirement is that the user use await with the functions. However, I must note that I'm not doing this on my SDK, I decided that every function will check the token before run, and if it's null, the validation process will be called before running the function itself.
@ErnanideSãoThiago that's a very bad requirement to use an SDK. Unless you are planning to use this in an environment you own, I don't think this is a good idea (ex. you won't be able to use this in a normal JavaScript environment). async/await is a ES2016 API, and is not supported without babel or other transpiler. You are better delaying the verification of the username/password until the first request, or just using Promises
@Orlando - I agree with your comments. One correction is that async/await is ES7 syntax, not ES6. Yes, you need Babel to use it now. This particular issue seems like an API designed to be used today without Babel should be using promises which will extend just fine later to use async/await if desired.
@jfriend00 ES2016 is the actual name for ES7. ES2016 !== ES6
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.