Building NodeJS REST service and consuming it from within Android application
 
Introduction
In this tutorial I will walk you through the process of building REST service in NodeJS. That service will enable Android users to authenticate to the back-end and set their profile photo.Using the code
The application consist of two parts:- Backend: RESTful service written in NodeJS
- Mobile: Android app written in Java
Part 1. - NodeJS back-end
Back-end part will enable users to:- Login using email/password combination
- Upload their profile picture
app.js
var express = require('express');
var http = require('http');
var path = require('path');
var app = express();
var util = require('util');
var multiparty = require('multiparty');
var multer = require('multer');
var upload = multer({ dest: './uploads/' });
app.use(multer({ dest: './uploads/' }));
var fs = require('fs');
 
var bodyParser = require('body-parser');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
// all environments
app.set('port', process.env.PORT || 3000);
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.json());
app.use(express.urlencoded());
app.use(express.methodOverride());
app.use(app.router);
app.use(require('stylus').middleware(path.join(__dirname, 'public')));
app.use(express.static(path.join(__dirname, 'public')));
app.use('/uploads', express.static(__dirname + '/uploads'));
app.use('/uploads', express.directory(__dirname + '/uploadsimages'));
// development only test
if ('development' == app.get('env')) {
    app.use(express.errorHandler());
}
var testEmail = "test@email.com";
var testPassword = "123456";
var testuserid = "my_userid";
app.get('/', function (req,res) {
  res.send("BCG REST service v2 by Jalle");
  });
var type = upload.single('image');
//working
app.post('/sessions/new', function (req, res) {
  var email = req.body.email;
  var password = req.body.password;
  if (email == testEmail && password == testPassword) {
    res.json({ userid: "my_userid", token: "token" });
  } else {
    res.json({ userid: "", token: "" });
  }
});
//working
app.get('/users/:userid', function (req, res) {
  var userid = req.params.userid;
  if (userid == testuserid ) {
    res.json({ email: testEmail, avatar_url: "uploads/pic.jpg" });
  } else {
    res.json({ email: "", avatar_url: "" });
  }
});
//upload avatar image
app.post('/users/:userid/avatar/:avatar', type,
  function(req, res) {
     
    var userid = req.params.userid;
    if (userid == testuserid || true) {
      var tmp_path = req.file.path;
      /** The original name of the uploaded file      stored in the variable "originalname". **/
      var target_path = 'uploads/' + req.file.originalname;
      /** A better way to copy the uploaded file. **/
      var src = fs.createReadStream(tmp_path);
      var dest = fs.createWriteStream(target_path);
      src.pipe(dest);
      src.on('end', function () { res.end('file uploaded'); });
      src.on('error', function (err) { res.end('error'); });
      res.json({ avatar_url: target_path });
    } else {
      res.json({ avatar_url: "" });
    }
  });
//File upload test
app.get('/upload', function (req, res) {
  res.render('upload', {
    title: 'Upload Images'
  });
});
app.post('/upload', type, function (req, res, next) {
  /** When using the "single"      data come in "req.file" regardless of the attribute "name". **/
  var tmp_path = req.file.path;
  /** The original name of the uploaded file      stored in the variable "originalname". **/
  var target_path = 'uploads/' + req.file.originalname;
  /** A better way to copy the uploaded file. **/
  var src = fs.createReadStream(tmp_path);
  var dest = fs.createWriteStream(target_path);
  src.pipe(dest);
  src.on('end', function () { res.end('file uploaded'); });
  src.on('error', function (err) { res.end('error'); });
});
http.createServer(app).listen(app.get('port'), function () {
    console.log('Express server listening on port ' + app.get('port'));
});
Now we can test the backend by using Postman. Use test account (credentials are in the code) to login and submit profile picture.
Source code available at Github
Part 2. - Android application
Mobile app will contain these classes
- LoginActivity
- MainActivity
- APIInterface
LoginActivity
package com.bcg.loginexample;
/**
 * A login screen that offers login via email/password.
 */
public class LoginActivity extends AppCompatActivity {
    public Settings settings;
    public static final String BASE_URL = ApiInterface.ENDPOINT;
    // UI references.
    private EditText mEmailView;
    private EditText mPasswordView;
    private TextView failedLoginMessage;
    View focusView = null;
    private String email;
    private String password;
     @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
         settings =new Settings(getApplicationContext());
         settings = settings.getSettings();
     //  settings.saveSettings("","","","");
        // Set up the login form.
        mEmailView = (EditText) findViewById(R.id.email);
        failedLoginMessage = (TextView)findViewById(R.id.failed_login);
        mPasswordView = (EditText) findViewById(R.id.password);
        mPasswordView.setOnEditorActionListener(new TextView.OnEditorActionListener() {
            @Override
            public boolean onEditorAction(TextView textView, int id, KeyEvent keyEvent) {
                if (id == R.id.login || id == EditorInfo.IME_NULL) {
                    attemptLogin();
                    return true;
                }
                return false;
            }
        });
        Button mEmailSignInButton = (Button) findViewById(R.id.email_sign_in_button);
        mEmailSignInButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
              attemptLogin();
            }
        });
         if(settings.token!="")
             startMain();
        // uploadAvatar("my_userid")
       //getuser("my_userid");
    }
    private void startMain() {
        Intent main = new Intent(LoginActivity.this, MainActivity.class);
        startActivity(main);
    }
    private ApiInterface getInterfaceService() {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        final ApiInterface mInterfaceService = retrofit.create(ApiInterface.class);
        return mInterfaceService;
    }
    private void loginProcessWithRetrofit(final String email, String password){
      ApiInterface mApiService = ApiInterface.retrofit.create(ApiInterface.class);
          Call mService=  mApiService.newsession(email,password);
        mService.enqueue(new Callback() {
            @Override
            public void onResponse(Call call, Response response) {
                Session mSession = response.body();
                if (mSession.token!="")
                {
                    settings =new Settings(getApplicationContext(), email, mSession.userid,mSession.token,null);
                    startMain();
                }else
                {
                    //  show error
                    failedLoginMessage.setText("Wrong username or password. Try again");
                    mEmailView.requestFocus();
                }
            }
            @Override
            public void onFailure(Call call, Throwable t) {
                call.cancel();
                Toast.makeText(LoginActivity.this, "Please check your network connection", Toast.LENGTH_LONG).show();
            }
        });
    }
    
    private void attemptLogin(){
        email=mEmailView.getText().toString();
        password=mPasswordView.getText().toString();
        loginProcessWithRetrofit(email,password);
    }
}
     
MainActivity
package com.bcg.loginexample;
public class MainActivity extends AppCompatActivity {
    private ImageButton imgAvatar;
    private de.hdodenhof.circleimageview.CircleImageView profile_image;
    private Button btnLogout, btnUseAvatar, btnTakePhoto;
    public Settings settings;
    MarshMallowPermission marshMallowPermission = new MarshMallowPermission(this);
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        settings =new Settings(getApplicationContext()).getSettings();
        String email =settings.getEmail();
        TextView loginInformation = (TextView)findViewById(R.id.login_email);
        if(email != null || !email.equals("")  ){
            loginInformation.setText("Welcome!!! You have logged in as " + email);
        }else {
            loginInformation.setText("Your login email is missing");
        }
        profile_image =(de.hdodenhof.circleimageview.CircleImageView) findViewById(R.id.profile_image);
        imgAvatar = (ImageButton) findViewById(R.id.imgAvatar);
        imgAvatar.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                getImage();
            }
        });
        btnLogout = (Button) findViewById(R.id.btnLogout);
        btnLogout.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                settings.saveSettings(null,null,"",null);
                Toast.makeText(MainActivity.this, "Signing out! Token deleted.", Toast.LENGTH_LONG).show();
                Intent i = new Intent(MainActivity.this, LoginActivity.class);
                startActivity(i);
            }
        });
        btnUseAvatar = (Button) findViewById(R.id.btnUseAvatar);
        btnUseAvatar.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String hash = MD5Util.md5Hex(settings.getEmail());
                String gravatarUrl = "http://www.gravatar.com/avatar/" + hash + "?s=600&d=600";
                settings.setAvatar_url(gravatarUrl);
                LoadImage();
            }
        });
        btnTakePhoto = (Button) findViewById(R.id.btnTakePhoto);
        btnTakePhoto.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                getImage();
            }
        });
        getuser(settings.getUserid());
        //LoadImage( );
    }
    private void getImage() {
        if (!marshMallowPermission.checkPermissionForCamera()) {
            marshMallowPermission.requestPermissionForCamera();
        } else {
            if (!marshMallowPermission.checkPermissionForExternalStorage()) {
                marshMallowPermission.requestPermissionForExternalStorage();
            } else {
                Intent chooseImageIntent = ImagePicker.getPickImageIntent(getApplicationContext());
                startActivityForResult(chooseImageIntent, PICK_IMAGE_ID);
            }}
    }
    private void getuser(final String userId){
         ApiInterface mApiService = ApiInterface.retrofit.create(ApiInterface.class);
        Call mService= mApiService.getuser(userId);
       mService.enqueue(new Callback() {
            @Override
            public void onResponse(Call call, Response response) {
                User userObject = response.body();
                settings.setAvatar_url(ApiInterface.ENDPOINT +userObject.avatar_url.replaceFirst("^/", "") );
                LoadImage( );
                Toast.makeText(MainActivity.this, "Returned " + userObject, Toast.LENGTH_LONG).show();
             }
            @Override
            public void onFailure(Call call, Throwable t) {
                call.cancel();
                Toast.makeText(MainActivity.this, "Please check your network connection", Toast.LENGTH_LONG).show();
            }
        });
    }
    private static final int PICK_IMAGE_ID = 234; // the number doesn't matter
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch(requestCode) {
            case PICK_IMAGE_ID:
                Bitmap bitmap = ImagePicker.getImageFromResult(this, resultCode, data);
                File file = null;
                try {
                    file = savebitmap(bitmap, "pic.jpeg");
                } catch (IOException e) {
                    e.printStackTrace();
                }
                uploadAvatar("my_userid", file );
                break;
            default:
                super.onActivityResult(requestCode, resultCode, data);
                break;
        }
    }
    public static File savebitmap(Bitmap bmp, String fName) throws IOException {
        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
        bmp.compress(Bitmap.CompressFormat.JPEG, 60, bytes);
        File f = new File(Environment.getExternalStorageDirectory()
                + File.separator + fName);
        f.createNewFile();
        FileOutputStream fo = new FileOutputStream(f);
        fo.write(bytes.toByteArray());
        fo.close();
        return f;
    }
    private void uploadAvatar(final String userid,File file){
        //   File file = new File("/storage/emulated/0/Android/data/com.bcg.loginexample/files/pic.jpg");
        // File file = new File("/storage/emulated/0/DCIM/camera/pic.jpg");
       // File file = new File(filePath);
        RequestBody body = RequestBody.create(MediaType.parse("image/*"), file);
        RequestBody description = RequestBody.create(MediaType.parse("text/plain"), "image");
        ApiInterface mApiService = ApiInterface.retrofit.create(ApiInterface.class);
        Call mService=  mApiService.postAvatar(body,description );
        mService.enqueue(new Callback() {
            @Override
            public void onResponse(Call call, Response response) {
                Avatar avatar = response.body();
                settings.setAvatar_url(ApiInterface.ENDPOINT  + avatar.avatar_url);
                LoadImage();
            }
            @Override
            public void onFailure(Call call, Throwable t) {
                call.cancel();
                Toast.makeText(getApplicationContext(), "Error: " + t.getMessage(), Toast.LENGTH_LONG).show();
            }
        });
    }
    public void LoadImage() {
        String  url=settings.getAvatar_url();
         //load image from server if exists
        if (url!=""){
            Picasso.with(getApplicationContext())
                    .load(url)
                  .networkPolicy(NetworkPolicy.NO_CACHE)
                    .memoryPolicy(MemoryPolicy.NO_CACHE)
                    .placeholder(R.drawable.empty_avatar)
                    .noFade()
                    .into(profile_image);
        }
    }
 }
          
ApiInterface
package com.bcg.loginexample.Rest;
public interface ApiInterface {
    String ENDPOINT = "URL-TO-YOUR-REST-SERVICE/";
     @FormUrlEncoded
    @POST("sessions/new")
    Call newsession(@Field("email") String email, @Field("password")  String password);
    @GET("users/{userid}")
    Call getuser(@Path("userid") String userid);
    @Multipart
    @POST ("users/{userid}/avatar/{avatar}")
    Call postAvatar (@Part("image\"; filename=\"pic.jpg\" ") RequestBody file , @Part("FirstName") RequestBody description);
    public static final Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(ENDPOINT)
            .addConverterFactory(GsonConverterFactory.create())
           .build();
}
   
Source code available at Github
 
Points of Interest
Every modern application today consist of some back-end (REST) service and devices that are using it. NodeJS + Android Java is good example of how to build one.
 
 
 
Comments
Post a Comment