{"id":16193555,"url":"https://github.com/pyramation/upload-example","last_synced_at":"2025-04-07T15:19:36.789Z","repository":{"id":66351157,"uuid":"294352891","full_name":"pyramation/upload-example","owner":"pyramation","description":null,"archived":false,"fork":false,"pushed_at":"2020-09-10T20:25:09.000Z","size":615,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-13T02:03:53.618Z","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":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pyramation.png","metadata":{"files":{"readme":"README.md","changelog":null,"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}},"created_at":"2020-09-10T08:41:10.000Z","updated_at":"2020-09-10T20:25:12.000Z","dependencies_parsed_at":"2023-02-26T23:47:06.555Z","dependency_job_id":null,"html_url":"https://github.com/pyramation/upload-example","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pyramation%2Fupload-example","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pyramation%2Fupload-example/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pyramation%2Fupload-example/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pyramation%2Fupload-example/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pyramation","download_url":"https://codeload.github.com/pyramation/upload-example/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247675597,"owners_count":20977378,"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":[],"created_at":"2024-10-10T08:15:27.272Z","updated_at":"2025-04-07T15:19:36.766Z","avatar_url":"https://github.com/pyramation.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# postgraphile-upload-example\n\nThis app demonstrates how to add file upload support to PostGraphile using the [GraphQL Multipart Request Spec](https://github.com/jaydenseric/graphql-multipart-request-spec).\n\nServer:\n- PostGraphile\n- [postgraphile-plugin-upload-field](https://github.com/mattbretl/postgraphile-plugin-upload-field)\n- [graphql-upload](https://github.com/jaydenseric/graphql-upload)\n\nClient:\n- create-react-app\n- [apollo-client](https://github.com/apollographql/apollo-client)\n- [apollo-upload-client](https://github.com/jaydenseric/apollo-upload-client)\n\n## Quick Start\n\nClone this repo.\n\nIn one terminal:\n\n```bash\ncd server\ncreatedb upload_example\npsql -d upload_example -f schema.sql\nyarn\nyarn start\n```\n\nIn another terminal:\n\n```bash\ncd client\nyarn\nyarn start\n```\n\nThe app should now be fully functional at localhost:3000. Uploaded files will be stored locally in `/server/uploads`.\n\n## How does it work?\n\nThe [server](https://github.com/mattbretl/postgraphile-upload-example/blob/master/server/src/index.js) code should be relatively straightforward if you're familiar with PostGraphile. The [graphql-upload](https://github.com/jaydenseric/graphql-upload) middleware handles the multipart requests using [busboy](https://github.com/mscdex/busboy). The [postgraphile-plugin-upload-field](https://github.com/mattbretl/postgraphile-plugin-upload-field) plugin for PostGraphile is minimally documented, but briefly, `match` is a function used to specify the file upload metadata columns and `resolve` is a function that handles the actual file upload stream.\n\nThe client is full of React/Apollo boilerplate. The unique parts are:\n- [These lines in clients/src/index.js](https://github.com/mattbretl/postgraphile-upload-example/blob/master/client/src/index.js#L26-28) where createUploadLink replaces the usual createHttpLink in the ApolloClient constructor; and\n- [All of client/src/CreatePost.js](https://github.com/mattbretl/postgraphile-upload-example/blob/master/client/src/CreatePost.js), which is the actual upload form. It uses the `Query` and `Mutation` components that were [added in React Apollo 2.1](https://dev-blog.apollodata.com/introducing-react-apollo-2-1-c837cc23d926).\n\n## Preserving metadata\n\nBy default, the example app only stores the local file path to Postgres. To preserve additional metadata, change the `header_image_file` column type to JSONB and replace the resolveUpload function with the following:\n\n```js\nasync function resolveUpload(upload) {\n  const { filename, mimetype, encoding, createReadStream } = upload;\n  const stream = createReadStream();\n  // Save file to the local filesystem\n  const { id, path } = await saveLocal({ stream, filename });\n  // Return metadata to save it to Postgres\n  return {\n    id,\n    path,\n    filename,\n    mimetype,\n    encoding\n  };\n}\n```\n\nAfter making this change, you'll also need to update the client app to use the `path` property of the object.\n\nFor a more robust solution, consider using something like [postgraphile-plugin-derived-field](https://github.com/mattbretl/postgraphile-plugin-derived-field) to expose URLs through GraphQL instead of exposing the raw path/metadata.\n\nIf you're streaming file uploads to an object storage service such as S3, you can also use the derived field plugin to generate pre-signed URLs for clients.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpyramation%2Fupload-example","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpyramation%2Fupload-example","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpyramation%2Fupload-example/lists"}