{"id":18561273,"url":"https://github.com/apostrophecms/minuscule","last_synced_at":"2026-02-16T09:06:48.611Z","repository":{"id":256832794,"uuid":"856547781","full_name":"apostrophecms/minuscule","owner":"apostrophecms","description":"A tiny Express wrapper for fast microservice development","archived":false,"fork":false,"pushed_at":"2025-01-22T18:11:49.000Z","size":47,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-20T11:32:59.978Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/apostrophecms.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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}},"created_at":"2024-09-12T19:04:10.000Z","updated_at":"2025-01-22T18:11:55.000Z","dependencies_parsed_at":"2025-01-21T20:24:28.074Z","dependency_job_id":"f85f24f7-32b0-4ca0-b8fa-d6b4a9aede6a","html_url":"https://github.com/apostrophecms/minuscule","commit_stats":null,"previous_names":["apostrophecms/minuscule"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/apostrophecms/minuscule","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/apostrophecms%2Fminuscule","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/apostrophecms%2Fminuscule/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/apostrophecms%2Fminuscule/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/apostrophecms%2Fminuscule/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/apostrophecms","download_url":"https://codeload.github.com/apostrophecms/minuscule/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/apostrophecms%2Fminuscule/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29504688,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-16T09:05:14.864Z","status":"ssl_error","status_checked_at":"2026-02-16T08:55:59.364Z","response_time":115,"last_error":"SSL_read: 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":[],"created_at":"2024-11-06T22:06:24.211Z","updated_at":"2026-02-16T09:06:48.574Z","avatar_url":"https://github.com/apostrophecms.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# @apostrophecms/minuscule\n\n**A tiny Express wrapper for safe, rapid microservice development**\n\nminuscule seeks to allow developers to write API routes more safely, with less cognitive load and less chance of accidental bugs.\n\nSpecifically, minuscule allows developers to **simply return a value** from route functions, including async functions, and automatically encodes that as a JSON response. Similarly, minuscule allows developers to **simply throw an exception** from both middleware functions and route functions, whether they are async or not. minuscule also provides conveniences to create errors with the status code of the developer's choice, as well as a default 500 error for other exceptions.\n\nContrast this with using Express directly. Express 4 requires the developer to manually manipulate the `res` object, adding extra cognitive load and introducing a risk that the developr will forget to handle `res`, producing a hung request. Express 5 improves on this situation by catching rejected promises, but still does not allow for automatic handling of the \"happy path\" (the success case).\n\nNote that minuscule middleware is different from Express middleware. While Express middleware must invoke `next()`, minuscule middleware just returns normally to continue execution, or throws an exception to end the request with an error.\n\n## \"What about use cases like redirects, static server middleware, etc.?\"\n\nCurrently minuscule does not address these edge cases, because they rarely come up in API development. However if you have a need to implement these you can just use Express middleware and routes for the purpose. We have not ruled out adding support for redirects, etc. in minuscule itself.\n\n## \"Do I have to use Express 5 with minuscule?\"\n\nNo, Express 4 is fine. Express 5 is also supported.\n\n## installation\n\n```bash\nnpm install express\nnpm install @apostrophecms/minuscule\n```\n\n## usage\n\n```javascript\nconst express = require('express');\nconst { WebError, minuscule } = require('@apostrophecms/minuscule');\nconst app = express();\nconst yup = require('yup');\n\n// Example yup schema, see yup documentation\nconst projectSchema = object({\n  shortName: string().required(),\n  longName: string(),\n  prod: boolean()\n});\n\n// Allow traditional form submission format (if you want it)\napp.use(express.urlencoded({ extended: false }));\n\n// Allow JSON submissions (recommended)\napp.use(express.json());\n\nconst {\n  get,\n  post,\n  use\n} = minuscule(app);\n\nuse(expectApiKey);\n\n// async GET API functions that just return a result\n\nget('/projects/:projectId', expectProjectId, async req =\u003e {\n  const result = await myDatabase.findOne({\n    id: req.params.projectId\n  });\n  // returning an object -\u003e automatic JSON response via req.res\n  return result;\n});\n\n// async POST API functions with easy, safe validation\n\npost('/projects', async req =\u003e {\n  const project = await projectSchema.validate(req.body);\n  await myDatabase.insertOne(project);\n  // returning an object -\u003e automatic JSON response via req.res\n  return project;\n});\n\n// Global validation middleware as an async function\n\nasync function expectApiKey(req) {\n  const header = req.headers.authorization;\n  if (!header) {\n    throw new WebError(403, 'API key required');\n  }\n  const matches = header.match(/^ApiKey\\s+(\\S.*)$/i);\n  if (matches[1] !== 'some-api-key') {\n    throw new WebError(403, 'Invalid API key');\n  }\n}\n\n// Route-specific validation middleware as an async function\n\nasync function expectProjectId(req) {\n  if (!req.params.projectId.match(/^\\w+/)) {\n    throw new WebError(400, 'projectId must contain only letters, digits and underscores');\n  }\n  req.projectId = req.params.projectId;\n  // Can also use \"await.\" If no error is thrown execution continues\n}\n\napp.listen(3000);\n```\n\n## Other methods available\n\n`patch`, `put` and `del` are available and wrap `app.patch`, `app.post` and `app.delete` the same way that `get` and `post` wrap `app.get` and `app.post`. `del` was named to avoid conflict with the `delete` keyword when importing.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fapostrophecms%2Fminuscule","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fapostrophecms%2Fminuscule","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fapostrophecms%2Fminuscule/lists"}