A hard story

3–1. get user info & refresh token, for the first time(The code will no longer be used after acquiring user refresh token)

client := &http.Client{}// The API is called without code parameter at first, then it redirects to the uri with code parameter. So, when code parameter does not exist, it calls user authorization API, which shows Google…

in-app purchase for Android and iOS

But one thing to note:

final bool available = await InAppPurchaseConnection.instance.isAvailable();

When this code does not work, then try connecting once more, like this:

(this tip is from https://stackoverflow.com/questions/59527864/iap-is-not-working-after-published-in-to-play-store-in-alpha-release)

if(!available){//try to connect againawait InAppPurchaseConnection.instance.isAvailable();}

and this solved my issue.

3. in-app-purchase setting for Android

3–1. go to Google play console > app dashboard > Monetize > Products > in- app products, and add…

Conclusion only

Your time is important. So, I will only write the conclusion here.


Getting image from S3 Cloudfront took too long, so it harmed user experience.


Images displayed on my app were uploaded by users, and the sizes of images were often larger than expected: more than 1MB, and some images were larger than 5MB.

Three alternatives


No one told me how to solve this

WHEN I tried to upload my iOS app to app store, Apple rejected my app with many reasons, including SNS sign-in design problem.

But what mattered most was this:

suppose the app name was JAME, and after Apple team reviewed that, I got this message.

The app must be published under a seller and company name that is associated with the organization or company providing the services. In this case, your app must be published under a seller name and company name that reflects the [appname, e.g. JAME] name.

Conclusion first:


From Cloudinary To AWS S3

There was a problem with image uploading in my Flutter project.

I initially used Cloudinary, because it was easy to apply to Flutter.

The code for Cloudinary was like the following:

Future<String> uploadFile(String filePath) async {try {final bytes = File(filePath).readAsBytesSync();String base64Encode(List<int> bytes) => base64.encode(bytes);String JPEGPrefix = ‘data:image/jpeg;base64,’;// print(base64Encode(bytes).substring(0, 100));const URL = ‘https://api.cloudinary.com/v1_1/account_name/image/upload/';Response response = await Dio().post(URL, data: {‘file’: JPEGPrefix + base64Encode(bytes),‘upload_preset’: ‘preset_name',‘folder’: ‘folder1/userProfileImages’,});print(response.data[“url”]);return response.data[“url”];} catch (e) {print(e);return e;}}

It worked well, with one problem. …

All you need to know to run Go in ubuntu 18.04

It takes time to gather information from Internet. That’s why I’m writing this article. Wish this might help someone who wants to run Go in AWS first time.

Step1. Launch EC2 instance

I will not give instruction on this. Launch instance on AWS EC2(in my case, I used ubuntu 18.04 Ver.), and associate elastic IPs with it.

Then, connect to that instance via ssh and .pem key, like

ssh -i path/to/key.pem ubuntu@

probable error


The secret of bcrypt(which only I didn’t know)


api to sign in user

mutation { signInUser(input: { email: “some_user_email” password: “some_password expoToken: “some_expo_token”}) {tokenString}}

The code to sign in user(Go-version)

var user model.Uservar token model.Tokenvar result pg.Resultvar wg = sync.WaitGroup{}_, err := pgdb.Con.Query(&user, `select id, password from users where email = ?`, input.Email)if user.ID == “” {return &token, errors.New(“No User with such email”)}if err != nil {return &token, err}noMatch := bcrypt.CompareHashAndPassword([]byte(*user.Password), []byte(input.Password))if noMatch != nil {return &token, errors.New(“Wrong password”)}// update last_visited_at…

What I learned today

How to export Database variable to other packages using go-pg


// package pgdbfunc DB() (Con *pg.DB) {Con = pg.Connect(options)if Con == nil {fmt.Printf(“Problem Occurred”)} else {fmt.Printf(“DB connected”)}return}// call from other packages_, err := pgdb.DB().Query(...)

This way had one drawback: Calling database by function is tiresome, because I have to type parenthesis.


// package pgdbvar Con *pg.DBfunc init() {// here are the same as before.}// call from other packages_, err := pgdb.Con.Query(..)

Official document is not too kind

I prefer raw queries to orm-based queries, but there is a sure advantage in using orm-based query, especially when inserting, updating, and deleting: Easy to read the code.

But, I still think it’s not a good idea to use orm-based queries when selecting, because it sends requests too many times when using ‘JOIN’. I use graphql to communicate with client, and if I use orm-based query, the ‘N+1’ problem will arise-which will not happen if I use raw query.

So, I will introduce orm-based queries on CUD/Mutation, and raw queries on CRUD of go-pg.

Be careful for difference between JSON array and pg array


When you defined User type like this on schema.graphqls

type User { ID: String! profileImages: [String]}

And after you generate by

go run github.com/99designs/gqlgen generate

There comes the generated model like this in models_gen.go:

type User struct {  ID string `json:”ID”`  ProfileImages []string`json:”profileImages”`}

And if you define CreateUser function like this in schema.resolvers.go, as usual

func CreateUser (profileImages []string) {  user := &model.User{    ID: uuid.New().String(),    ProfileImages: profileImages,}

There comes an error with this Mutation.

mutation { createUser(profileImages: [ “url1”, “url2” ]) {  ID}}

Error: malformed array literal

So, let’s see what happened.


The error happened because structs and slices…

Jason Choi

Backend Developer, Pingpong

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store