retrosheet

Introduction: Turn Google Spreadsheet to JSON endpoint (for Android and JVM) for FREE (100%)
More: Author   ReportBugs   
Tags:

Turn Google Spreadsheet to JSON endpoint.

https://github.com/theapache64/notes

🤝 Benefits

  • 🔥 Free analytics via Google forms
  • 🔄 Migrate to your REST API with minimal code changes.
  • 📊 You get a easy to use and real time interface for your data (GoogleSheet) ;)
  • 🏃‍♂️ Speed up development of your POC or MVP with this library.

🚀 Platform Supported

Android iOS Java JavaScript

🤝 Install

latestVersion

repositories {
    mavenCentral()
}

dependencies {
    implementation("io.github.theapache64:retrosheet:<latest.version>")
}

⌘️ Setup (using wizard)

image

Using the wizard, you can setup the backend in ~3 mins

Steps:

  1. Create a Google Form (if you want to write data) or a Google Sheet (if you want to read data) or both.
  2. Publish as usual and fill up the form in wizard 🧙🏼‍♂️
  3. Press Generate Code and copy paste it into your project

📹 Video Demo: https://www.youtube.com/watch?v=eWDquVbsebg

⌘️ Setup (manual)

✍️ Writing Data

📝 Step 1: Create a Google Form

Create a form with required fields.
Google Form

🎯 Step 2: Set Response Destination

Choose a Google Sheet to save responses.
Response Destination
Sheet Selection

📊 Step 3: Customize Sheet

Rename sheet and columns (optional).
Before
After

Press Send and copy the link.
Form Link

🔧 Step 5: Create RetrosheetConfig and attach it to the client

val config = RetrosheetConfig.Builder()
    .setLogging(true)
    // For reading from sheet
    .addSheet(
        "notes", // sheet name
        "created_at", "title", "description" // columns in same order
    )
    // For writing to sheet
    .addForm(
        "add_note",
        "https://docs.google.com/forms/d/e/1FAIpQLSdmavg6P4eZTmIu-0M7xF_z-qDCHdpGebX8MGL43HSGAXcd3w/viewform?usp=sf_link" // form link
    )
    .build()

val ktorClient = HttpClient {
    install(createRetrosheetPlugin(config)) {}
    ...
}

🌐 Step 6: Create API Interface

interface NotesApi {
    @Read("SELECT *")
    @GET("notes")
    suspend fun getNotes(): List<Note>

    @Write
    @POST("add_note")
    suspend fun addNote(@Body note: Note): Note
}

@Write is used for writing data and @Read for reading data.

Query Language Guide

📚 Reading Data

🔄 Step 7: Share Sheet

Open a sheet and copy its shareable link.
Copy Link

Trim the link after the last '/'.

https://docs.google.com/spreadsheets/d/1IcZTH6-g7cZeht_xr82SHJOuJXD_p55QueMrZcnsAvQ/edit?usp=sharing

🔗 Step 9: Set Base URL

Use the trimmed link as baseUrl in Ktorfit.

val retrofit = Ktorfit.Builder()
    // Like this 👇🏼
    .baseUrl("https://docs.google.com/spreadsheets/d/1YTWKe7_mzuwl7AO1Es1aCtj5S9buh3vKauKCMjx1j_M/")
    .httpClient(ktorClient)
    .converterFactories(RetrosheetConverter(config))
    .build()

Done 👍

🌠 Full Example

build.gradle.kts

plugins {
    kotlin("jvm") version "2.1.10"
    id("org.jetbrains.kotlin.plugin.serialization") version "2.1.10"
    id("com.google.devtools.ksp") version "2.1.10-1.0.31"
    id("de.jensklingenberg.ktorfit") version "2.5.1"
}
...
dependencies {
    implementation("io.ktor:ktor-client-content-negotiation:3.1.3")
    implementation("io.ktor:ktor-serialization-kotlinx-json:3.1.3")
    implementation("de.jensklingenberg.ktorfit:ktorfit-lib:2.5.1")
    implementation("io.github.theapache64:retrosheet:3.0.0-alpha02")
    testImplementation(kotlin("test"))
}
...

NotesApi.kt

interface NotesApi {
    @Read("SELECT *")
    @GET("notes")
    suspend fun getNotes(): List<Note>

    @Write
    @POST("add_note")
    suspend fun addNote(@Body note: Note): Note
}

Main.kt

@Serializable
data class Note(
    @SerialName("Title")
    val title: String,
    @SerialName("Description")
    val description: String?,
    @SerialName("Timestamp")
    val createdAt: String? = null,
)


suspend fun main() {
    val notesApi = createMyNotesApi()
    println(notesApi.getNotes())

    // Adding sample order
    val newNote = notesApi.addNote(
        Note(
            createdAt = null,
            title = "Dynamic com.sample.Note 1",
            description = "Dynámic Desc 1: ${Date()}"
        )
    )

    println(newNote)
}


fun createMyNotesApi(
    configBuilder: RetrosheetConfig.Builder.() -> Unit = {}
): NotesApi {
    val config = RetrosheetConfig.Builder()
        .apply { this.configBuilder() }
        .setLogging(true)
        // To Read
        .addSheet(
            "notes", // sheet name
            "created_at", "title", "description" // columns in same order
        )
        // To write
        .addForm(
            "add_note",
            // Google form name
            "https://docs.google.com/forms/d/e/1FAIpQLSdmavg6P4eZTmIu-0M7xF_z-qDCHdpGebX8MGL43HSGAXcd3w/viewform?usp=sf_link"
        )
        .build()

    val ktorClient = HttpClient {
        install(createRetrosheetPlugin(config)) {}
        install(ContentNegotiation) {
            json()
        }
    }

    val ktorfit = Ktorfit.Builder()
        // GoogleSheet Public URL
        .baseUrl("https://docs.google.com/spreadsheets/d/1YTWKe7_mzuwl7AO1Es1aCtj5S9buh3vKauKCMjx1j_M/")
        .httpClient(ktorClient)
        .converterFactories(RetrosheetConverter(config))
        .build()

    return ktorfit.createNotesApi()
}

🔄 Migration

  • Want to migrate from v1 or v2?Here's the guide

Must Read ✋🏼

Retrosheet is great for prototyping and not recommended to be used in production for real apps. This is because the library makes direct calls to Google APIs—so if those APIs go down, your app goes down with them.

That said, I do use it in production for a few of my side projects :P, and it has been working fine for over 5 years now. (So if things break, I’ll be right there, drowning in tears with you.)

✍️ Author

  • theapache64
Apps
About Me
GitHub: Trinea
Facebook: Dev Tools