{"id":28441144,"url":"https://github.com/allisson/psqlqueue","last_synced_at":"2026-02-02T13:13:52.610Z","repository":{"id":214690277,"uuid":"736388670","full_name":"allisson/psqlqueue","owner":"allisson","description":"Simple queue system powered by Golang and PostgreSQL","archived":false,"fork":false,"pushed_at":"2025-12-23T23:27:34.000Z","size":101,"stargazers_count":3,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-12-25T12:52:02.971Z","etag":null,"topics":["hacktoberfest"],"latest_commit_sha":null,"homepage":"","language":"Go","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/allisson.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-12-27T19:07:45.000Z","updated_at":"2024-10-15T18:44:59.000Z","dependencies_parsed_at":"2024-10-17T00:11:33.504Z","dependency_job_id":null,"html_url":"https://github.com/allisson/psqlqueue","commit_stats":null,"previous_names":["allisson/psqlqueue"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/allisson/psqlqueue","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/allisson%2Fpsqlqueue","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/allisson%2Fpsqlqueue/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/allisson%2Fpsqlqueue/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/allisson%2Fpsqlqueue/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/allisson","download_url":"https://codeload.github.com/allisson/psqlqueue/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/allisson%2Fpsqlqueue/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29012691,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-02T12:48:30.580Z","status":"ssl_error","status_checked_at":"2026-02-02T12:46:38.384Z","response_time":58,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["hacktoberfest"],"created_at":"2025-06-06T04:09:01.085Z","updated_at":"2026-02-02T13:13:52.605Z","avatar_url":"https://github.com/allisson.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# psqlqueue\n[![test](https://github.com/allisson/psqlqueue/actions/workflows/test.yml/badge.svg)](https://github.com/allisson/psqlqueue/actions/workflows/test.yml)\n[![Go Report Card](https://goreportcard.com/badge/github.com/allisson/psqlqueue)](https://goreportcard.com/report/github.com/allisson/psqlqueue)\n[![Docker Repository on Quay](https://quay.io/repository/allisson/psqlqueue/status \"Docker Repository on Quay\")](https://quay.io/repository/allisson/psqlqueue)\n\nSimple queue system powered by Golang and PostgreSQL.\n\n## Quickstart\n\nThe idea of this service is to offer a simple queuing system using PostgreSQL as a backend.\n\nFirst, we need a PostgreSQL database, for this, we will use docker:\n\n```bash\ndocker run --name postgres-psqlqueue \\\n    --restart unless-stopped \\\n    -e POSTGRES_USER=psqlqueue \\\n    -e POSTGRES_PASSWORD=psqlqueue \\\n    -e POSTGRES_DB=psqlqueue \\\n    -p 5432:5432 \\\n    -d postgres:15-alpine\n```\n\nBefore running anything, take a look at the possible environment variables that can be used with this service: https://github.com/allisson/psqlqueue/blob/main/env.sample\n\nNow let's run the database migrations before starting the server:\n\n```bash\ndocker run --rm \\\n    -e PSQLQUEUE_DATABASE_URL='postgres://psqlqueue:psqlqueue@host.docker.internal:5432/psqlqueue?sslmode=disable' \\\n    quay.io/allisson/psqlqueue migrate\n```\n\n```json\n{\"time\":\"2023-12-29T21:11:39.516360369Z\",\"level\":\"INFO\",\"msg\":\"migration process started\"}\n{\"time\":\"2023-12-29T21:11:39.54908151Z\",\"level\":\"INFO\",\"msg\":\"migration process finished\"}\n```\n\nStarting the server:\n\n```bash\ndocker run --rm \\\n    -e PSQLQUEUE_DATABASE_URL='postgres://psqlqueue:psqlqueue@host.docker.internal:5432/psqlqueue?sslmode=disable' \\\n    -p 8000:8000 \\\n    quay.io/allisson/psqlqueue server\n```\n\n```json\n{\"time\":\"2023-12-29T21:14:30.898080659Z\",\"level\":\"INFO\",\"msg\":\"http server starting\",\"host\":\"0.0.0.0\",\"port\":8000}\n```\n\nFor creating a new queue we have these fields:\n- \"id\": The identifier of this new queue.\n- \"ack_deadline_seconds\": The maximum time before the consumer should acknowledge the message, after this time the message will be delivered again to consumers.\n- \"message_retention_seconds\": The maximum time in which the message must be delivered to consumers, after this time the message will be marked as expired.\n- \"delivery_delay_seconds\": The number of seconds to postpone the delivery of new messages to consumers.\n\n```bash\ncurl --location 'http://localhost:8000/v1/queues' \\\n--header 'Content-Type: application/json' \\\n--data '{\n    \"id\": \"my-new-queue\",\n    \"ack_deadline_seconds\": 30,\n    \"message_retention_seconds\": 1209600,\n    \"delivery_delay_seconds\": 0\n}'\n```\n\n```json\n{\n    \"id\": \"my-new-queue\",\n    \"ack_deadline_seconds\": 30,\n    \"message_retention_seconds\": 1209600,\n    \"delivery_delay_seconds\": 0,\n    \"created_at\": \"2023-12-29T21:30:58.682194763Z\",\n    \"updated_at\": \"2023-12-29T21:30:58.682194763Z\"\n}\n```\n\nFor creating a new message we have these fields:\n- \"body\": The body of the message.\n- \"label\": A label that allows this message to be filtered.\n- \"attributes\": The message attributes.\n\n```bash\ncurl --location 'http://localhost:8000/v1/queues/my-new-queue/messages' \\\n--header 'Content-Type: application/json' \\\n--data '{\n    \"body\": \"message body\",\n    \"label\": \"my-label\",\n    \"attributes\": {\"attribute1\": \"attribute1\", \"attribute2\": \"attribute2\"}\n}'\n```\n\nFor consuming the messages we have these filters:\n- \"label\": To filter by the message label.\n- \"limit\": To limit the number of messages.\n\n```bash\ncurl --location 'http://localhost:8000/v1/queues/my-new-queue/messages?limit=1'\n```\n\n```json\n{\n    \"data\": [\n        {\n            \"id\": \"01HJVRCQVAD9VBT10MCS74T0EN\",\n            \"queue_id\": \"my-new-queue\",\n            \"label\": \"my-label\",\n            \"body\": \"message body\",\n            \"attributes\": {\n                \"attribute1\": \"attribute1\",\n                \"attribute2\": \"attribute2\"\n            },\n            \"delivery_attempts\": 1,\n            \"created_at\": \"2023-12-29T21:41:25.994731Z\"\n        }\n    ],\n    \"limit\": 1\n}\n```\n\nNow you have 30 seconds to execute the ack or nack for this message, first we can do the nack:\n\n```bash\ncurl --location --request PUT 'http://localhost:8000/v1/queues/my-new-queue/messages/01HJVRCQVAD9VBT10MCS74T0EN/nack' \\\n--header 'Content-Type: application/json' \\\n--data '{\n    \"visibility_timeout_seconds\": 30 \n}'\n```\n\nNow we need to wait 30 seconds before consuming this message again, after this time:\n\n```bash\ncurl --location 'http://localhost:8000/v1/queues/my-new-queue/messages?limit=1'\n```\n\n```json\n{\n    \"data\": [\n        {\n            \"id\": \"01HJVRCQVAD9VBT10MCS74T0EN\",\n            \"queue_id\": \"my-new-queue\",\n            \"label\": \"my-label\",\n            \"body\": \"message body\",\n            \"attributes\": {\n                \"attribute1\": \"attribute1\",\n                \"attribute2\": \"attribute2\"\n            },\n            \"delivery_attempts\": 2,\n            \"created_at\": \"2023-12-29T21:41:25.994731Z\"\n        }\n    ],\n    \"limit\": 1\n}\n```\n\nNow it's time to ack the message:\n\n```bash\ncurl --location --request PUT 'http://localhost:8000/v1/queues/my-new-queue/messages/01HJVRCQVAD9VBT10MCS74T0EN/ack'\n```\n\nLet's try to consume the messages again:\n\n```bash\ncurl --location 'http://localhost:8000/v1/queues/my-new-queue/messages/?limit=1'\n```\n\n```json\n{\n    \"data\": [],\n    \"limit\": 1\n}\n```\n\nAfter the ack, the message remains in the database marked as expired, to remove expired messages we can use the cleanup endpoint:\n\n```bash\ncurl --location --request PUT 'http://localhost:8000/v1/queues/my-new-queue/cleanup'\n```\n\nThis is the basics of using this service, I recommend that you check the swagger documentation at http://localhost:8000/v1/swagger/index.html to see more options.\n\n## Pub/Sub mode\n\nIt's possible to use a Pub/Sub approach with the topics/subscriptions endpoints.\n\nFor this example let's imagine an event system that processes orders, we will have a topic called `orders` and will push some messages into this topic.\n\nFor creating a new topic we have these fields:\n- \"id\": The identifier of this new topic.\n\n```bash\ncurl --location 'http://localhost:8000/v1/topics' \\\n--header 'Content-Type: application/json' \\\n--data '{\n    \"id\": \"orders\"\n}'\n```\n\n```json\n{\n    \"id\": \"orders\",\n    \"created_at\": \"2024-01-02T22:20:43.351647Z\"\n}\n```\n\nNow we will create two new queues:\n- \"all-orders\": For receiving all messages from the orders topic.\n- \"processed-orders\": For receiving only the messages with the `status` attribute equal to `\"processed\"`.\n\n```bash\ncurl --location 'http://localhost:8000/v1/queues' \\\n--header 'Content-Type: application/json' \\\n--data '{\n    \"id\": \"all-orders\",\n    \"ack_deadline_seconds\": 30,\n    \"message_retention_seconds\": 1209600,\n    \"delivery_delay_seconds\": 0\n}'\n```\n\n```json\n{\n    \"id\": \"all-orders\",\n    \"ack_deadline_seconds\": 30,\n    \"message_retention_seconds\": 1209600,\n    \"delivery_delay_seconds\": 0,\n    \"created_at\": \"2024-01-02T22:24:58.219593Z\",\n    \"updated_at\": \"2024-01-02T22:24:58.219593Z\"\n}\n```\n\n```bash\ncurl --location 'http://localhost:8000/v1/queues' \\\n--header 'Content-Type: application/json' \\\n--data '{\n    \"id\": \"processed-orders\",\n    \"ack_deadline_seconds\": 30,\n    \"message_retention_seconds\": 1209600,\n    \"delivery_delay_seconds\": 0\n}'\n```\n\n```json\n{\n    \"id\": \"processed-orders\",\n    \"ack_deadline_seconds\": 30,\n    \"message_retention_seconds\": 1209600,\n    \"delivery_delay_seconds\": 0,\n    \"created_at\": \"2024-01-02T22:25:28.472891Z\",\n    \"updated_at\": \"2024-01-02T22:25:28.472891Z\"\n}\n```\n\nNow we will create two subscriptions to link the topic with the queue.\n\nFor creating a new subscription we have these fields:\n- \"id\": The identifier of this new subscription.\n- \"topic_id\": The id of the topic.\n- \"queue_id\": The id of the queue.\n- \"message_filters\": The filter for use with the message attributes.\n\nCreating the first subscription:\n\n```bash\ncurl --location 'http://localhost:8000/v1/subscriptions' \\\n--header 'Content-Type: application/json' \\\n--data '{\n    \"id\": \"orders-to-all-orders\",\n    \"topic_id\": \"orders\",\n    \"queue_id\": \"all-orders\"\n}'\n```\n\n```json\n{\n    \"id\": \"orders-to-all-orders\",\n    \"topic_id\": \"orders\",\n    \"queue_id\": \"all-orders\",\n    \"message_filters\": null,\n    \"created_at\": \"2024-01-02T22:30:12.628323Z\"\n}\n```\n\nNow creating the second subscription, for this one we will use the message_filters field:\n\n```bash\ncurl --location 'http://localhost:8000/v1/subscriptions' \\\n--header 'Content-Type: application/json' \\\n--data '{\n    \"id\": \"orders-to-processed-orders\",\n    \"topic_id\": \"orders\",\n    \"queue_id\": \"processed-orders\",\n    \"message_filters\": {\"status\": [\"processed\"]}\n}'\n```\n\n```json\n{\n    \"id\": \"orders-to-processed-orders\",\n    \"topic_id\": \"orders\",\n    \"queue_id\": \"processed-orders\",\n    \"message_filters\": {\n        \"status\": [\n            \"processed\"\n        ]\n    },\n    \"created_at\": \"2024-01-02T22:31:26.156692Z\"\n}\n```\n\nNow it's time to publish the first message:\n\n```bash\ncurl --location 'http://localhost:8000/v1/topics/orders/messages' \\\n--header 'Content-Type: application/json' \\\n--data '{\n    \"body\": \"body-of-the-order\",\n    \"attributes\": {\"status\": \"created\"}\n}'\n```\n\nAnd the second message:\n\n```bash\ncurl --location 'http://localhost:8000/v1/topics/orders/messages' \\\n--header 'Content-Type: application/json' \\\n--data '{\n    \"body\": \"body-of-the-order\",\n    \"attributes\": {\"status\": \"processed\"}\n}'\n```\n\nNow we will consume the `all-orders` queue:\n\n```bash\ncurl --location 'http://localhost:8000/v1/queues/all-orders/messages'\n```\n\n```json\n{\n    \"data\": [\n        {\n            \"id\": \"01HK651Q52EZMPKBYZGVK0ZX8S\",\n            \"queue_id\": \"all-orders\",\n            \"label\": null,\n            \"body\": \"body-of-the-order\",\n            \"attributes\": {\n                \"status\": \"created\"\n            },\n            \"delivery_attempts\": 1,\n            \"created_at\": \"2024-01-02T19:35:00.635625-03:00\"\n        },\n        {\n            \"id\": \"01HK652W2HNW53XWV4QBT5MAJY\",\n            \"queue_id\": \"all-orders\",\n            \"label\": null,\n            \"body\": \"body-of-the-order\",\n            \"attributes\": {\n                \"status\": \"processed\"\n            },\n            \"delivery_attempts\": 1,\n            \"created_at\": \"2024-01-02T19:35:38.446759-03:00\"\n        }\n    ],\n    \"limit\": 10\n}\n```\n\nAs expected, this queue has the two published messages.\n\nNow we will consume the `processed-orders` queue:\n\n```bash\ncurl --location 'http://localhost:8000/v1/queues/processed-orders/messages'\n```\n\n```json\n{\n    \"data\": [\n        {\n            \"id\": \"01HK652W2JK8MPN3JDXY9RATS5\",\n            \"queue_id\": \"processed-orders\",\n            \"label\": null,\n            \"body\": \"body-of-the-order\",\n            \"attributes\": {\n                \"status\": \"processed\"\n            },\n            \"delivery_attempts\": 1,\n            \"created_at\": \"2024-01-02T19:35:38.446759-03:00\"\n        }\n    ],\n    \"limit\": 10\n}\n```\n\nAs expected, this queue has only one message that was published with the `status` attribute equal to `\"processed\"`.\n\n## Prometheus metrics\n\nThe Prometheus metrics can be accessed at http://localhost:9090.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fallisson%2Fpsqlqueue","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fallisson%2Fpsqlqueue","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fallisson%2Fpsqlqueue/lists"}