{"id":27165786,"url":"https://github.com/pepijn/google-cloud-functions-clojure","last_synced_at":"2025-12-12T01:28:13.391Z","repository":{"id":49582110,"uuid":"273232155","full_name":"pepijn/google-cloud-functions-clojure","owner":"pepijn","description":"An experiment showing a (Clojure) ring adapter for the Google Cloud Function Java Runtime on Google Cloud Platform","archived":false,"fork":false,"pushed_at":"2021-06-13T17:32:48.000Z","size":131,"stargazers_count":15,"open_issues_count":7,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-02T00:48:50.555Z","etag":null,"topics":["clojure","google-cloud-functions","google-cloud-platform","ring-adapter"],"latest_commit_sha":null,"homepage":"","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pepijn.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-06-18T12:29:28.000Z","updated_at":"2024-10-03T00:29:50.000Z","dependencies_parsed_at":"2022-09-06T03:32:27.963Z","dependency_job_id":null,"html_url":"https://github.com/pepijn/google-cloud-functions-clojure","commit_stats":null,"previous_names":["pepijn/google-cloud-function-ring-adapter"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pepijn%2Fgoogle-cloud-functions-clojure","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pepijn%2Fgoogle-cloud-functions-clojure/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pepijn%2Fgoogle-cloud-functions-clojure/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pepijn%2Fgoogle-cloud-functions-clojure/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pepijn","download_url":"https://codeload.github.com/pepijn/google-cloud-functions-clojure/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247969043,"owners_count":21025906,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["clojure","google-cloud-functions","google-cloud-platform","ring-adapter"],"created_at":"2025-04-09T03:20:53.191Z","updated_at":"2025-12-12T01:28:13.338Z","avatar_url":"https://github.com/pepijn.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"# google-cloud-functions-ring-adapter [![Test](https://github.com/pepijn/google-cloud-functions-clojure/actions/workflows/test.yml/badge.svg)](https://github.com/pepijn/google-cloud-functions-clojure/actions/workflows/test.yml) [![Clojars Project](https://img.shields.io/clojars/v/nl.epij/google-cloud-functions-ring-adapter.svg)](https://clojars.org/nl.epij/google-cloud-functions-ring-adapter)\n\nA (Clojure) [ring](https://github.com/ring-clojure/ring) adapter for the [Google Cloud Function Java Runtime](https://cloud.google.com/functions/docs/concepts/java-runtime) on Google Cloud Platform.\n\n## Usage\n\nYou can run your cloud function locally or deploy it to Google Cloud Functions.\n**Before running or deploying, create a [Java entrypoint](https://cloud.google.com/functions/docs/writing#structuring_source_code) ([example](https://github.com/pepijn/google-cloud-functions-clojure/blob/master/example/src/java/JsonHttpEcho.java)).\nInside the entrypoint, specify your fully-qualified ring handler ([example](https://github.com/pepijn/google-cloud-functions-clojure/blob/f0ed93a7347a35923c3c3f065b9a2d8f145766dc/example/src/java/JsonHttpEcho.java#L5)).**\n\n### Running locally\n\nAdd an alias to your `deps.edn` if you want to run locally, such as:\n\n```clojure\n{:aliases {:run {:extra-deps {nl.epij.gcf/deploy {:git/url   \"https://github.com/pepijn/google-cloud-functions-clojure\"\n                                                  :sha       \"3772d2489d8f590df1b28b87a70d364b6311a0cd\"\n                                                  :deps/root \"deploy\"}}\n                 :exec-fn    nl.epij.gcf.deploy/run-server!\n                 :exec-args  {:nl.epij.gcf/entrypoint   JsonHttpEcho\n                              :nl.epij.gcf/java-paths   [\"src/java\"]\n                              :nl.epij.gcf/compile-path \"target/classes\"\n                              :nl.epij.gcf/jar-path     \"target/artifacts/application.jar\"}}}}\n```\n\nThen run the server:\n\n```bash\nPORT=13337 clojure -X:run\n```\n\nFinally, send HTTP requests to it:\n```bash\ncurl localhost:13337\n```\n\n### Deploying to Cloud Functions\n\nBefore you can do the first HTTP request to your deployed Cloud Function ring handler, you need to take the following steps:\n\n1. Add a JAR assemble alias in your `deps.edn` file that specifies the entrypoint mentioned above ([example](https://github.com/pepijn/google-cloud-functions-clojure/blob/dd081026461daa18d24a38b83404a75918d7b11a/example/deps.edn#L15-L22))\n1. Deploy the cloud function using the [`gcloud` SDK](https://cloud.google.com/sdk/), specifying the directory containing the JAR with `--source`. See the example in the Pathom docs: https://pathom3.wsscode.com/docs/tutorials/serverless-pathom-gcf#gcf-deploy\n\nCheck out the [`example/`](https://github.com/pepijn/google-cloud-functions-clojure/tree/master/example) in this repository for more information.\n\n\n## Rationale\n\nGoogle Cloud Functions (GCF)—like other serverless products such as AWS Lambda—offer a cheap way to run your application.\nYou also don't have to worry about deployment specifics of your code and messing around with Docker containers.\nInstead, you simply tell GCF the function name to invoke when triggered.\n\nClojure enthousiasts can now use this project to have that function be a Clojure function.\nMore specifically, a ring adapter (in the case of an HTTP trigger).\nIn this library you'll find all that's necessary to reach that goal—batteries (deployment, structured logging, etc.) included.\n\n## Extras\n\nThe library includes a [namespace for structured logging](src/clojure/nl/epij/gcf/log.clj) and a [namespace with ring middleware to make working with PubSub-triggered invocations easier](src/clojure/nl/epij/pubsub/middleware.clj). Also, the ring request map has [Google Cloud Functions environment variables](https://cloud.google.com/functions/docs/env-var#newer_runtimes) assoced to it:\n\n- `:nl.epij.gcf.env/function-target`\n- `:nl.epij.gcf.env/function-signature-type`\n- `:nl.epij.gcf.env/k-service`\n- `:nl.epij.gcf.env/k-revision`\n- `:nl.epij.gcf.env/port`\n\n### Structured Logging\n\nIn order to enable structured logging, add the `logback.xml` file to your classpath (e.g. in a `resources/` directory):\n\n```xml\n\u003cconfiguration\u003e\n    \u003cappender name=\"jsonConsoleAppender\" class=\"ch.qos.logback.core.ConsoleAppender\"\u003e\n        \u003cencoder class=\"net.logstash.logback.encoder.LogstashEncoder\"\u003e\n            \u003c!-- Ignore default logging fields --\u003e\n            \u003cfieldNames\u003e\n                \u003ctimestamp\u003e[ignore]\u003c/timestamp\u003e\n                \u003cversion\u003e[ignore]\u003c/version\u003e\n                \u003clogger\u003e[ignore]\u003c/logger\u003e\n                \u003cthread\u003e[ignore]\u003c/thread\u003e\n                \u003clevel\u003e[ignore]\u003c/level\u003e\n                \u003clevelValue\u003e[ignore]\u003c/levelValue\u003e\n            \u003c/fieldNames\u003e\n        \u003c/encoder\u003e\n    \u003c/appender\u003e\n    \u003croot level=\"INFO\"\u003e\n        \u003cappender-ref ref=\"jsonConsoleAppender\"/\u003e\n    \u003c/root\u003e\n\u003c/configuration\u003e\n```\n\n## FAQ\n\n### Why do I need to write the entrypoint class in Java—can't we compile it from Clojure?\n\nCompiling the Java entrypoint from Clojure works when you're running locally, yes.\nBut, during deployment, Google instantiates the entrypoint class while not having Clojure on the classpath.\nThat is a problem since Clojure's compiled Java code contains a `static {}` block that requires Clojure.\nClojure is not available and causes the build to fail.\nTherefore, you need a very minimal Java class that points to your ring handler.\nBy the way, Clojure is only loaded once—even between subsequent Cloud Function invocations—this is good for performance.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpepijn%2Fgoogle-cloud-functions-clojure","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpepijn%2Fgoogle-cloud-functions-clojure","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpepijn%2Fgoogle-cloud-functions-clojure/lists"}