I have created a library which consumes the Imgur API.
This is the main file.
var request = require('request-promise'),
fs = require('fs'),
path = require('path');
var Imgur = function(options) {
this.options = {};
if (typeof options.clientId === 'undefined' || !options.clientId) {
throw new Error('No client ID specified, register one at "https://api.imgur.com/oauth2/addclient"');
}
if (typeof options.clientSecret !== 'undefined') {
this.options.clientSecret = options.clientSecret;
}
this.options.clientId = options.clientId;
this.options.version = options.version || 3;
this.options.endPoint = 'https://api.imgur.com/' + this.options.version + '/';
fs
.readdirSync(path.join(__dirname, './endpoints'))
.forEach(function(file) {
if (~file.indexOf('.js')) {
this[file.substring(0, file.indexOf('.'))] = require(path.join(__dirname, './endpoints', file))(this);
}
}.bind(this));
};
Imgur.prototype.extend = function(target, source) {
var extended = {};
for (var prop in target) {
if (Object.prototype.hasOwnProperty.call(target, prop)) {
extended[prop] = target[prop];
}
}
for (var prop in source) {
if (Object.prototype.hasOwnProperty.call(source, prop)) {
extended[prop] = source[prop];
}
}
return extended;
};
Imgur.prototype.request = function(method, path, params) {
params = params || {};
if (typeof method === 'undefined' || ['get', 'post', 'head', 'delete'].indexOf(method.toLowerCase()) === -1) {
throw new Error('no method specified or method isn\'t in [get, post, head, delete]');
}
var options = {
method: method,
headers: {
Authorization: 'Client-ID ' + this.options.clientId
},
json: true
};
if (typeof params.data !== 'undefined' && method.toLowerCase() !== 'get') {
options.form = params.data;
}
if (typeof params.file !== 'undefined') {
options.formData = params.file;
}
if (typeof params.access_token !== 'undefined') {
options.headers.Authorization = 'Bearer ' + params.access_token;
}
for (var key in params) {
if (['data', 'file', 'access_token'].indexOf(key) != -1)
continue;
if (params.hasOwnProperty(key)) {
path = path.replace(new RegExp('\{' + key + '\}', 'g'), params[key]);
}
}
options.url = /^https?:\/\//.test(path) ? path : this.options.endPoint + path;
return request(options);
};
module.exports = Imgur;
It is then extended with endpoints such as:
var stream = require('stream');
var Image = function(imgur) {
this.image = function(params) {
params = params || {};
if (!params.hasOwnProperty('id')) {
throw new Error('id required');
}
var options = imgur.extend(params, {
id: params.id
});
return imgur.request('get', 'image/{id}', options);
};
this.upload = function(params) {
params = params || {};
if (!params.hasOwnProperty('image')) {
throw new Error('image required, can be a binary file, base64 data or a URL');
}
var options = imgur.extend(params, {
data: params.data,
file: params.file
});
if (typeof params.file === 'string') {
if (/https?:\/\//.test(params.file)) {
options.data.file = 'URL';
} else {
options.data.type = 'base64';
}
} else if (params.file instanceof stream.Stream) {
options.data.type = 'file'
}
return imgur.request('post', 'image', options);
};
this.delete = function(params) {
params = params || {};
if (!params.hasOwnProperty('id')) {
throw new Error('id required');
}
var options = imgur.extend(params, {
id: params.id
});
return imgur.request('delete', 'image/{id}', options);
};
this.update = function(params) {
params = params || {};
if (!params.hasOwnProperty('id')) {
throw new Error('id required');
}
var options = imgur.extend(params, {
id: params.id,
data: params.data
});
return imgur.request('post', 'image/{id}', options);
};
this.favorite = function(params) {
params = params || {};
if (!params.hasOwnProperty('id')) {
throw new Error('id required');
}
var options = imgur.extend(params, {
id: params.id
});
return imgur.request('post', 'image/{id}/favorite', options);
};
return this;
};
module.exports = Image;
There is an oauth endpoint that looks like this.
var Oauth = function(imgur) {
this.getOauthUrl = function() {
return 'https://api.imgur.com/oauth2/authorize?client_id=' + imgur.options.clientId + '&response_type=code';
};
this.authorizeCallback = function(authCode) {
if (typeof authCode === "undefined" || !authCode) {
throw new Error('authorization code required');
}
var options = {
data: {
client_id: imgur.options.clientId,
client_secret: imgur.options.clientSecret,
grant_type: 'authorization_code',
code: authCode
}
};
return imgur.request('post', 'https://api.imgur.com/oauth2/token', options).then(function(data) {
data.expiresAt = Math.round(Date.now() + (data.expires_in * 1000));
return data;
});
};
this.refreshToken = function(refreshToken) {
if (typeof refreshToken === "undefined" || !refreshToken) {
throw new Error('No refresh token defined');
}
var options = {
data: {
client_id: imgur.options.clientId,
client_secret: imgur.options.clientSecret,
grant_type: 'refresh_token',
refresh_token: refreshToken
}
};
return imgur.request('post', 'https://api.imgur.com/oauth2/token', options).then(function(data) {
data.expiresAt = Math.round(Date.now() + (data.expires_in * 1000));
return data;
});
};
return this;
};
module.exports = Oauth;
It is all pulled together with the following code.
var Imgur = require('./imgurjs');
var imgur = new Imgur({
clientId: '<clientid>',
clientSecret: '<clientsecret>'
});
An example of using it would be
imgur.image.favourite({id: 1})
.then(function(response) {
console.log(response);
})
.catch(function(error) {
console.log(error);
});
You can authenticate using the oauth like so (example uses express)
app.get('/login', function(req, res) {
res.redirect(imgur.oauth.getOauthUrl());
});
app.get('/loginCallback', function(req, res) {
imgur.oauth.authorizeCallback(req.query.code).then(function(user) {
req.session.user = user;
res.redirect('/');
});
});
Then you can access authenticated routes by passing in the token stored in the session
imgur.image.delete(imgur.extend({id: 1}, req.session.user))
//or imgur.image.delete({id: 1, access_token: req.session.user.access_token})
.then(function(response) {
});
I feel that there's a better way to do this rather than passing the oauth token to every method, but I can't get my head around it.
Is there a way to have it so I only need to call the constructor once to instantiate the client id and secret, but also have an instance for each user's session which would store their oauth token.