{"id":18895030,"url":"https://github.com/dolthub/react-graphql-dolt-sample-app","last_synced_at":"2025-09-04T12:31:08.277Z","repository":{"id":185465562,"uuid":"673103555","full_name":"dolthub/react-graphql-dolt-sample-app","owner":"dolthub","description":"A sample React + GraphQL + Dolt (RGD) Web Application","archived":false,"fork":false,"pushed_at":"2024-12-02T18:12:55.000Z","size":2019,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2024-12-02T18:55:17.631Z","etag":null,"topics":["apollo","database-version-control","dolt","graphql","nestjs","nodejs","react"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","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/dolthub.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}},"created_at":"2023-07-31T22:05:55.000Z","updated_at":"2024-12-02T18:12:57.000Z","dependencies_parsed_at":null,"dependency_job_id":"c3ba8f8f-63a8-481c-a446-e97fc1de7857","html_url":"https://github.com/dolthub/react-graphql-dolt-sample-app","commit_stats":null,"previous_names":["dolthub/react-graphql-dolt-sample-app"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dolthub%2Freact-graphql-dolt-sample-app","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dolthub%2Freact-graphql-dolt-sample-app/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dolthub%2Freact-graphql-dolt-sample-app/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dolthub%2Freact-graphql-dolt-sample-app/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dolthub","download_url":"https://codeload.github.com/dolthub/react-graphql-dolt-sample-app/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":231959602,"owners_count":18452112,"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":["apollo","database-version-control","dolt","graphql","nestjs","nodejs","react"],"created_at":"2024-11-08T08:26:09.599Z","updated_at":"2024-12-31T07:40:51.146Z","avatar_url":"https://github.com/dolthub.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# React + GraphQL + Dolt (RDG) Sample Application\n\nThis sample application uses the RDG web stack. It shows how you can set up a GraphQL\nserver using a Dolt database and connect it to a React front end.\n\n## Getting started\n\n### 1. Start by installing dependencies and compiling the code:\n\n```shell\n~ % yarn \u0026\u0026 yarn compile\n```\n\n### 2. Add database configuration\n\nIn order to start your GraphQL server, you need to provide your database configuration. Add a `.development.env` file that looks like this:\n\n```shell\nHOST=\"dolthub-us-jails.dbs.hosted.doltdb.com\"\nPORT=3306\nUSERNAME=\"myusername\"\nPASSWORD=\"mypassword\"\nDATABASE=\"us_jails\"\n```\n\n_Note: we are using a cloud-hosted Dolt database from [Hosted Dolt](https://hosted.doltdb.com/)_\n\n### 3. Start GraphQL server\n\nYou'll know if your database is configured correctly if you start your local GraphQL server and get no errors.\n\nIn `packages/graphql-server`:\n\n```shell\ngraphql-server % yarn dev\n[3:00:17 PM] Starting compilation in watch mode...\n\n[3:00:21 PM] Found 0 errors. Watching for file changes.\n\n[Nest] 5310  - 07/31/2023, 3:00:24 PM     LOG [NestFactory] Starting Nest application...\n[Nest] 5310  - 07/31/2023, 3:00:24 PM     LOG [InstanceLoader] TypeOrmModule dependencies initialized +47ms\n[Nest] 5310  - 07/31/2023, 3:00:24 PM     LOG [InstanceLoader] ConfigHostModule dependencies initialized +1ms\n[Nest] 5310  - 07/31/2023, 3:00:24 PM     LOG [InstanceLoader] TerminusModule dependencies initialized +0ms\n[Nest] 5310  - 07/31/2023, 3:00:24 PM     LOG [InstanceLoader] ConfigModule dependencies initialized +0ms\n[Nest] 5310  - 07/31/2023, 3:00:24 PM     LOG [InstanceLoader] ConfigModule dependencies initialized +0ms\n[Nest] 5310  - 07/31/2023, 3:00:24 PM     LOG [InstanceLoader] GraphQLSchemaBuilderModule dependencies initialized +92ms\n[Nest] 5310  - 07/31/2023, 3:00:24 PM     LOG [InstanceLoader] GraphQLModule dependencies initialized +3ms\n[Nest] 5310  - 07/31/2023, 3:00:24 PM     LOG [InstanceLoader] TypeOrmCoreModule dependencies initialized +564ms\n[Nest] 5310  - 07/31/2023, 3:00:24 PM     LOG [InstanceLoader] TypeOrmModule dependencies initialized +1ms\n[Nest] 5310  - 07/31/2023, 3:00:24 PM     LOG [InstanceLoader] DoltBranchesModule dependencies initialized +5ms\n[Nest] 5310  - 07/31/2023, 3:00:24 PM     LOG [InstanceLoader] AppModule dependencies initialized +0ms\n[Nest] 5310  - 07/31/2023, 3:00:24 PM     LOG [RoutesResolver] DoltBranchesController {/doltBranches}: +24ms\n[Nest] 5310  - 07/31/2023, 3:00:24 PM     LOG [RouterExplorer] Mapped {/doltBranches, GET} route +4ms\n[Nest] 5310  - 07/31/2023, 3:00:24 PM     LOG [GraphQLModule] Mapped {/graphql, POST} route +146ms\n[Nest] 5310  - 07/31/2023, 3:00:24 PM     LOG [NestApplication] Nest application successfully started +5ms\n```\n\nYou can test your queries in the GraphQL playground from http://localhost:9000/graphql.\n\n### 4. Start web server\n\nIn another terminal, start the local web server. Go to `packages/web` and run:\n\n```shell\nweb % yarn dev\n- ready started server on 0.0.0.0:3000, url: http://localhost:3000\n- info Loaded env from /Users/me/dern-sample-app/packages/web/.env.development\n- event compiled client and server successfully in 1816 ms (18 modules)\n```\n\nAnd then navigate to http://localhost:3000 in your browser.\n\n## Architecture\n\n### Dolt\n\n[Dolt](https://doltdb.com) is a MySQL-compatible version-controlled database, which\nincludes things like branches, commits, diffs, and merges. Dolt can be used with any MySQL\nclient, including [Node MySQL](https://www.npmjs.com/package/mysql), which is used in this\napplication.\n\nDolt has a cloud-hosted option called [Hosted Dolt](https://hosted.doltdb.com), which is\ngreat for creating production applications.\n\n### GraphQL\n\nThe [GraphQL](https://graphql.org/) server layer sits between Dolt and the React web\napplication. We use the [Nest.js](https://docs.nestjs.com) framework to integrate with a\nDolt database via [TypeORM](https://github.com/typeorm/typeorm), as well as expose GraphQL\nendpoints via the [GraphQL integration](https://docs.nestjs.com/graphql/quick-start).\n\n### React\n\nThis application uses [Next.js](https://nextjs.org/) (a React framework that abstracts and\nautomatically configures tooling needed for React, like bundling, compiling), [Apollo\nClient](https://www.apollographql.com/docs/react/) (manages local and remote data with\nGraphQL), and [GraphQL Code Generator](https://the-guild.dev/graphql/codegen) (generates\nTypescript types and React query and mutation hooks based on our GraphQL schema).\n\n## graphql-server\n\nWe use the [NestJS](https://docs.nestjs.com/) framework to build an efficient, scalable\n[Node.js](https://nodejs.org/) server-side application. It provides a level of abstraction\nabove common Node.js frameworks like Express, which makes it easy to support both\nTypescript and GraphQL and to integrate with any database.\n\nWe use two built-in Nest integrations to build our GraphQL server:\n\n1. [TypeORM](https://docs.nestjs.com/techniques/database#typeorm-integration), the most\n   mature ORM available for Typescript. We use the [MySQL database\n   driver](https://www.npmjs.com/package/mysql2) with TypeORM to connect to our Dolt SQL\n   server.\n2. [GraphQL](https://docs.nestjs.com/graphql/quick-start), a powerful query language for\n   APIs and a runtime for fulfilling those queries with your existing data. We can\n   configure the GraphQL module to use [Apollo](https://www.apollographql.com/) server, a\n   service that processes GraphQL operations from application clients. We'll come back to\n   Apollo when we set up the React portion of our application.\n\nYou can follow [these steps](https://docs.nestjs.com/first-steps) to set up a new project.\n\n### TypeORM\n\nWe use the [TypeORM\nmodule](https://docs.nestjs.com/techniques/database#typeorm-integration) to populate\nconnectivity information of our Dolt database into the root `AppModule`. You can find this\ninformation in the Connectivity tab of your Hosted Dolt deployment.\n\n```ts\n// src/app.module.ts\nimport { Module } from \"@nestjs/common\";\nimport { TerminusModule } from \"@nestjs/terminus\";\nimport { TypeOrmModule } from \"@nestjs/typeorm\";\n\n@Module({\n  imports: [\n    TypeOrmModule.forRoot({\n      type: \"mysql\",\n      host: \"dolthub-us-jails.dbs.hosted.doltdb.com\",\n      port: 3306,\n      username: \"myusername\",\n      password: \"xxxxxx\",\n      database: \"us_jails\",\n      ssl: {\n        rejectUnauthorized: false,\n      },\n      autoLoadEntities: true,\n      synchronize: false,\n    }),\n    TerminusModule,\n  ],\n})\nexport class AppModule {}\n```\n\nThe example in this repository uses the [config\nmodule](https://docs.nestjs.com/techniques/configuration) here instead to get your\ndatabase information from an env file.\n\nTypeORM supports the repository design pattern, so each entity will have its own\nrepository. `Entity` is a class that maps to a database table. We will use the\n[`dolt_branches`](https://docs.dolthub.com/sql-reference/version-control/dolt-system-tables#dolt_branches)\nsystem table in this example.\n\n```sql\nmysql\u003e select * from dolt_branches;\n+-------------+----------------------------------+---------------------+------------------------+-------------------------+-------------------------+\n| name        | hash                             | latest_committer    | latest_committer_email | latest_commit_date      | latest_commit_message   |\n+-------------+----------------------------------+---------------------+------------------------+-------------------------+-------------------------+\n| delete-rows | u8s83gapv7ghnbmrtpm8q5es0dbl7lpd | taylorb             | taylor@dolthub.com     | 2022-06-14 19:11:58.402 | Accept PR 44            |\n| new-branch  | sqjm4s0f2m48rjc97hr6cbpv2hqga00d | Dolt System Account | doltuser@dolthub.com   | 2022-09-14 19:30:41.132 | delete row              |\n+-------------+----------------------------------+---------------------+------------------------+-------------------------+-------------------------+\n2 rows in set (0.06 sec)\n```\n\nThe `DoltBranch` entity that maps to the `dolt_branches` system table schema.\n\n```ts\n// src/doltBranches/doltBranch.entity.ts\nimport { Column, Entity, PrimaryColumn } from \"typeorm\";\n\n@Entity()\nexport class DoltBranches {\n  @PrimaryColumn()\n  name: string;\n\n  @Column()\n  hash: string;\n\n  @Column()\n  latest_committer: string;\n\n  @Column()\n  latest_committer_email: string;\n\n  @Column()\n  latest_commit_message: string;\n\n  @Column()\n  latest_commit_date: Date;\n}\n```\n\nSince we set `autoLoadEntities` to `true` in our TypeORM module, we can begin using this\nentity automatically.\n\nThen we create a `DoltBranchesModule` and add it to the `imports` in `AppModule`.\n\n```ts\n// src/doltBranches/doltBranch.module.ts\nimport { Module } from \"@nestjs/common\";\nimport { TypeOrmModule } from \"@nestjs/typeorm\";\nimport { DoltBranches } from \"./doltBranch.entity\";\nimport { DoltBranchesService } from \"./doltBranch.service\";\n\n@Module({\n  imports: [TypeOrmModule.forFeature([DoltBranches])],\n  providers: [DoltBranchesService],\n  exports: [DoltBranchesService],\n})\nexport class DoltBranchesModule {}\n```\n\nThe `forFeature` method injects the `DoltBranchesRepository` into the\n`DoltBranchesService` using the `@InjectRepository()` decorator. This lets us use\ndifferent [methods](https://typeorm.io/working-with-entity-manager) to query or mutate\ndata in that database table.\n\n```ts\n// src/doltBranches/doltBranch.service.ts\nimport { Injectable } from \"@nestjs/common\";\nimport { InjectRepository } from \"@nestjs/typeorm\";\nimport { Repository } from \"typeorm\";\nimport { DoltBranches } from \"./doltBranch.entity\";\n\nexport class CreateBranchArgs {\n  newBranchName: string;\n  refName: string;\n}\n\n@Injectable()\nexport class DoltBranchesService {\n  constructor(\n    @InjectRepository(DoltBranches)\n    private doltBranchesRepository: Repository\u003cDoltBranches\u003e\n  ) {}\n\n  findAll(): Promise\u003cDoltBranches[]\u003e {\n    return this.doltBranchesRepository.find();\n  }\n}\n```\n\n### GraphQL models and resolvers\n\nSince we're using the [code first](https://docs.nestjs.com/graphql/quick-start#code-first)\napproach, we use decorators and Typescript classes to generate the corresponding\nGraphQL schema.\n\nThe `GraphQLModule` is imported in `AppModule`.\n\n```ts\n// src/app.module.ts\nimport { ApolloDriver, ApolloDriverConfig } from \"@nestjs/apollo\";\nimport { Module } from \"@nestjs/common\";\nimport { GraphQLModule } from \"@nestjs/graphql\";\nimport { TerminusModule } from \"@nestjs/terminus\";\nimport { TypeOrmModule } from \"@nestjs/typeorm\";\n\n@Module({\n  imports: [\n    GraphQLModule.forRoot\u003cApolloDriverConfig\u003e({\n      autoSchemaFile: \"schema.gql\",\n      context: (ctx) =\u003e ctx,\n      driver: ApolloDriver,\n    }),\n    TypeOrmModule.forRoot({\n      // ...database config\n    }),\n    TerminusModule,\n  ],\n})\nexport class AppModule {}\n```\n\nEach object type you\ndefine should represent a domain object that an application client might need to interact\nwith. Our `Branch`\n[model](https://docs.nestjs.com/graphql/resolvers#object-types) looks like this:\n\n```ts\n// src/branches/branch.model.ts\nimport { Field, GraphQLTimestamp, ID, ObjectType } from \"@nestjs/graphql\";\n\n@ObjectType()\nexport class Branch {\n  @Field((_type) =\u003e ID)\n  name: string;\n\n  @Field()\n  hash: string;\n\n  @Field()\n  latestCommitter: string;\n\n  @Field()\n  latestCommitterEmail: string;\n\n  @Field()\n  latestCommitMessage: string;\n\n  @Field((_type) =\u003e GraphQLTimestamp)\n  latestCommitDate: Date;\n}\n```\n\nA `Resolver` class is a way for our client to interact with the `Branch` object.\n\n```ts\n// src/branches/branch.resolver.ts\nimport { Query, Resolver } from \"@nestjs/graphql\";\nimport { DoltBranchesService } from \"../doltBranches/doltBranch.service\";\nimport { Branch, fromDoltBranchesRow } from \"./branch.model\";\n\n@Resolver((_of) =\u003e Branch)\nexport class BranchResolver {\n  constructor(private doltBranchService: DoltBranchesService) {}\n\n  @Query((_returns) =\u003e [Branch])\n  async branches(): Promise\u003cBranch[]\u003e {\n    const branches = await this.doltBranchService.findAll();\n    return branches.map(fromDoltBranchesRow);\n  }\n}\n```\n\nThis resolver needs to be added to `AppModule` as a provider. The full `AppModule` looks like this:\n\n```ts\n// src/app.module.ts\n\n@Module({\n  imports: [\n    GraphQLModule.forRoot\u003cApolloDriverConfig\u003e({\n      autoSchemaFile: \"schema.gql\",\n      context: (ctx) =\u003e ctx,\n      driver: ApolloDriver,\n    }),\n    TypeOrmModule.forRoot({\n      // ...database config\n    }),\n    DoltBranchesModule,\n    TerminusModule,\n  ],\n  providers: [BranchResolver],\n})\nexport class AppModule {}\n```\n\nWhen you make changes to your models or resolvers and run your GraphQL development server\n(`yarn run dev`), you should see a `schema.gql` file updated with the new models and\nresolver definitions.\n\nThis file is used to generate Typescript object types and hooks that can be used by React.\n\n## web\n\nWe build the React portion of this web application using [Next.js](https://nextjs.org/).\nNext.js is a React framework that abstracts and automatically configures tooling needed\nfor React, like bundling, compiling, and more.\n\nYou can follow [these instructions](https://nextjs.org/docs/getting-started/installation)\nto install the required packages and [create a `pages`\ndirectory](https://nextjs.org/docs/getting-started/installation#the-pages-directory-optional).\nOnce you have `index.tsx` and `_app.tsx` components within `pages`, you can run the\ndevelopment server (`yarn run dev`). You should see your home page when you navigate to\nlocalhost.\n\nOur Next.js application uses [Apollo Client](https://www.apollographql.com/docs/react/) to\nmanage our local and remote data with GraphQL. You can follow [these\nsteps](https://www.apollographql.com/docs/react/get-started) to get started.\n\nWe created a [custom Apollo\nwrapper](https://github.com/dolthub/dolt-graphql-react-sample-app/blob/main/packages/web/lib/apollo.tsx),\nwhich we use to wrap every page component via `_app.tsx`.\n\n```tsx\n// pages/_app.tsx\nimport { withApollo } from \"@lib/apollo\";\nimport App from \"next/app\";\nimport Head from \"next/head\";\n\nexport default class SampleApp extends App {\n  public render() {\n    const { Component } = this.props;\n\n    const WrappedPage = withApollo()(Component);\n    return (\n      \u003c\u003e\n        \u003cHead\u003e{/* include various meta tags and scripts here */}\u003c/Head\u003e\n        \u003cWrappedPage {...pageProps} /\u003e\n      \u003c/\u003e\n    );\n  }\n}\n```\n\n### Page components\n\nWe have a page component lists our Dolt branches. From here we can click on a branch to\nview more branch information or delete a branch.\n\nEach folder or file in the `pages` directory creates a [new\nroute](https://nextjs.org/docs/app/building-your-application/routing/defining-routes). The\n`branches/index.tsx` file shows a list of branches in our Dolt database and forms the\n`/branches` route. The `branches/[name].tsx` file shows more information about a\nparticular branch and form the [dynamic\nroute](https://nextjs.org/docs/app/building-your-application/routing/dynamic-routes)\n`/branches/[name]`.\n\n```tsx\n// pages/branches/index.tsx\nimport { NextPage } from \"next\";\nimport Link from \"next/link\";\nimport BranchList from \"../../components/BranchList\";\nimport Page from \"../../layouts/page\";\n\nconst Branches: NextPage = () =\u003e {\n  return (\n    \u003cPage title=\"Branches\"\u003e\n      \u003cBranchList /\u003e\n      \u003cLink href=\"/\"\u003eBack to home\u003c/Link\u003e\n    \u003c/Page\u003e\n  );\n};\n\nexport default Branches;\n```\n\n### Using GraphQL queries from components\n\nThe `BranchList` component fetches a list of Dolt branches in your database and renders\nthe branch names. This is the GraphQL\n[query](https://www.apollographql.com/docs/react/data/queries).\n\n```ts\n// components/BranchList/queries.ts\nimport { gql } from \"@apollo/client\";\n\nexport const LIST_BRANCHES = gql`\n  query ListBranches {\n    branches {\n      name\n    }\n  }\n`;\n```\n\nSince we're using Typescript, we use an additional package called [GraphQL Code\nGenerator](https://the-guild.dev/graphql/codegen). This generates Typescript types and\nReact query and mutation hooks based on our GraphQL schema.\n\nAfter you\n[install](https://the-guild.dev/graphql/codegen/docs/getting-started/installation) and\n[configure](https://the-guild.dev/graphql/codegen/docs/config-reference/codegen-config)\nthe code generator, you can simply run `yarn generate-types` to generate code that you'll\nfind in\n[`gen/graphql-types.tsx`](https://github.com/dolthub/dolt-graphql-react-sample-app/blob/main/packages/web/gen/graphql-types.tsx).\n\nWe can use the generated `useListBranchesQuery` hook to fetch our database's branches.\n\n```tsx\n// components/BranchList/index.tsx\nimport Link from \"next/link\";\nimport ReactLoader from \"react-loader\";\nimport { useListBranchesQuery } from \"../../gen/graphql-types\";\n\nexport default function BranchList() {\n  const res = useListBranchesQuery();\n  if (res.loading) {\n    return \u003cReactLoader loaded={false} /\u003e;\n  }\n  if (res.error) {\n    return (\n      \u003cdiv className=\"error-msg\"\u003e\n        Error loading branches: {res.error.message}\n      \u003c/div\u003e\n    );\n  }\n  if (!res.data?.branches.length) {\n    return \u003cdiv\u003eNo branches found\u003c/div\u003e;\n  }\n  return (\n    \u003cul\u003e\n      {res.data.branches.map((b) =\u003e (\n        \u003cli key={b.name}\u003e\n          \u003cLink\n            href=\"/branches/[name]\"\n            as={`/branches/${encodeURIComponent(b.name)}`}\n          \u003e\n            {b.name}\n          \u003c/Link\u003e\n        \u003c/li\u003e\n      ))}\n    \u003c/ul\u003e\n  );\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdolthub%2Freact-graphql-dolt-sample-app","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdolthub%2Freact-graphql-dolt-sample-app","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdolthub%2Freact-graphql-dolt-sample-app/lists"}