Coder Social home page Coder Social logo

cerinoligutom / graphql-starter Goto Github PK

View Code? Open in Web Editor NEW
98.0 3.0 22.0 5.03 MB

A boilerplate for TypeScript + Node Express + Apollo GraphQL APIs.

License: MIT License

Dockerfile 1.94% JavaScript 6.87% TypeScript 91.09% Shell 0.10%
graphql typescript nodejs express boilerplate boilerplate-template boilerplate-node docker postgresql supertokens

graphql-starter's Introduction

TypeScript Node Express + Apollo GraphQL Starter

A boilerplate for TypeScript + Node Express + Apollo GraphQL APIs.

Table of Contents

Features

  • ESM (ECMAScript Modules) support
  • Login with Email and Password
  • Session Management (Access Token + Rotating Refresh Tokens) with SuperTokens.
    • SuperTokens is only used for Session Management with this setup but it can do more than that. If you need a different kind of Authentication (e.g. Social Login, Passwordless), read more on the SuperTokens docs on how to setup one easily.
  • Node Express REST endpoint examples
  • Apollo GraphQL as middleware for Node Express
  • Centralized error handling
    • See src/errors/base.error.ts and src/errors/error-handler/index.ts.
    • For GraphQL, see src/graphql/index.ts and look for the formatError of Apollo.
    • For REST, see src/middlewares/error.middleware.ts.
  • Automatic type code generation for GraphQL resolvers with GraphQL Code Generator
  • Facebook Dataloader for caching and batching
  • PostgreSQL Database
  • pgAdmin for managing the PostgreSQL Database
  • Redis for Caching and GraphQL Subscription
  • RedisCommander for managing the Redis Database
  • Pre-commit hook for auto formatting files with Husky, Lint-Staged and Prettier
  • Zod for schema validations
  • GraphQL Subscription example
  • Dockerized containers for both development and production
    • Multi-stage build for production
  • Kysely for query building
  • Prisma for database migrations and seeding

Important notes

  • If your project has authorization needs and your frontend also uses JavaScript/TypeScript, I recommend using CASL. If you want to spin your own but not sure how, here's a good article to get the ideas down. If you want a language-agnostic solution, there is Permify.

Setup

architecture

If the nature of your app isn't CRUDy (for a lack of a better word), you probably need domain models and a good amount of layering + mappers. Here's a good article on the topic. Otherwise, proceed.

Prerequisites

  • Docker
  • NodeJS
  • TypeScript
  • supertokens-website on your frontend app.
    • For the client-side session management.
    • Read more here for more info about the specific cookies being used by SuperTokens.
    • If you don't use JS for your frontend app, you'll have to make the HTTP requests that supertokens-website heavy lifts yourself, such as refreshing the session when it expires.

Getting Started

# Install dependencies for the host
pnpm install

# Generate GraphQL Types
pnpm generate:gql-types

# Build the project for the first time or when you add dependencies
docker-compose build

# Start the application (or to restart after making changes to the source code)
docker-compose up

# Or run it in detached mode then listen only to this application's logs
docker-compose up -d
docker-compose logs -f api

Then refer to Database Migrations and Seeding to setup the database.

Note: You might be prompted to share your drive with Docker if you haven't done so previously. The drive letter you need to share in this case would be the drive letter of where this repository resides.


If docker compose have bootstrapped all services successfully, you should be able to access:

GraphQL Endpoint

http://localhost:8080/graphql

pgAdmin endpoint

http://localhost:8888/

Login to the pgAdmin page using the credentials below:

Field Value
email [email protected]
password password

If first time setting up:

  • Right click on the Servers then create a new server.
  • In the General tab, give this server connection a name.
  • Switch over to the Connection tab.
  • Fill in the fields below.
Field Value Notes
Host name/address db Service name in docker-compose.yml file for our Database service is named db
Username postgres Default username is postgres
Password password As defined in the docker-compose.yml config

Redis Commander endpoint

http://localhost:8889

Node Express REST Health Check endpoint

http://localhost:8080/api/v1/maintenance/health-check

Note: If you prefer a different port, container name, or anything docker environment related. Just modify the docker-compose.yml file and adjust to your preferred setup.

Project Structure

Name Description
src/config/* Any app level environment configs should go here.
src/db/schema/index.ts Zod Database schemas goes here.
src/db/types.d.ts Generated types by prisma-kysely.
src/errors/<error-name>.error.ts Custom Errors.
src/generated/**/*.ts Generated files.
src/graphql/scalars/<scalar-name>.scalar.ts Custom Scalars and their resolvers.
src/graphql/enums/index.ts GraphQL Enum resolvers and internal values.
src/graphql/index.ts Apollo GraphQL setup.
src/graphql/schema.ts GraphQL Schema/Resolver builder script.
src/graphql/init-loaders.ts Collect all dataloaders here.
src/graphql/pubsub.ts Initialize pubsub engine here.
src/middlewares/<middleware-name>.middleware.ts Node Express Middleware files.
src/modules/<module-name>/* Your feature modules.
src/modules/_/* Reserved module. Contains root schema for graphql to work along with some sample resolvers.
src/redis/index.ts Default Redis client is initialized here along with some helpers.
src/shared/ Anything that's shared (generic) throughout the app should be placed here.
src/utils/<utility-name>.util.ts Utility files.
src/app.ts Main application file.
.dockerignore Folder and files ignored by Docker.
.eslintignore Folder and files ignored by ESLint.
.eslintrc.js Linter rules are defined here.
.gitignore Folder and files that should be ignored by git.
.huskyrc Husky config. Git hooks made easy.
.lintstagedrc Lint-Staged config. Run commands against staged git files.
.prettierrc Prettier config. An opinionated code formatter.
codegen.yml GraphQL Code Generator (file watcher) config file.
docker-compose.yml Docker compose config file.
Dockerfile Production Docker config file.
Dockerfile.dev Development Docker config file used by docker-compose.
gulpfile.ts Gulp task runner config file.
tsconfig.json Contains typescript config for this project.

Note: This project structure makes use of barrel files, those index.ts you see on most of the folders. Make sure not to forget to export your newly created files to their respective barrel files (index.ts) if applicable!

Sample Environment File

# Make sure to set this to "production" in production environments
NODE_ENV=

# If you want to change the app port. Defaults to 8080.
PORT=

# DB Connection URLs
POSTGRES_CONNECTION_URL=
REDIS_CONNECTION_URL=

# SuperTokens
SUPERTOKENS_CONNECTION_URL=
SUPERTOKENS_API_KEY=
SUPERTOKENS_APP_NAME=
SUPERTOKENS_API_DOMAIN=
SUPERTOKENS_WEBSITE_DOMAIN=

See files inside src/config/* that uses process.env. Those are the environment variables that you can configure.

Recommended Workflow

Update your database models in the Prisma Schema

If your feature requires modifications to the database, update the Prisma Schema accordingly then run pnpm exec prisma migrate dev.

See docs if you're unfamiliar with this command.

Since we have prisma-kysely configured, this should also generate the corresponding typescript types for your database models and should be usable with Kysely right away for your database queries.

(Optional) Update the seed script

If you want to seed data, update the seed file at prisma/seed.ts accordingly.

Create/Update the corresponding Zod schema for your database model

Based on your database model changes, make sure to reflect your schema changes as well in the src/db/schema/index.ts file.

Create a folder for your Feature Module

  1. Create one under the src/modules/<module_name> directory.

Create a Use Case (and/or Services)

  1. Create a use case TS file under your module.
    • Should be under src/modules/<module_name>/use-cases/<use_case_name>.use-case.ts
  2. Create a Zod schema for your DTO.
  3. Infer the DTO type based on the Zod schema for your use case.
  4. Check for auth requirements if applicable.
  5. Validate DTO.
  6. Write your business logic for the use case.
    • If some operations warrants creating a service, don't hesitate to create one.
  7. Make sure to return an object-like data on your use case functions so that you can easily extend the results if needed.
    • Unless it's a write operation and is idempotent in nature, then you might not need to return anything.

Important:

  • Your Interface Layer shouldn't do any DB Operations directly.
  • Use cases should never call another use case. There will be tight coupling if you do that. In the case you decide to change a parameter or behavior on the child use case, the dependent use case would've to adjust accordingly. Instead, create a service that both use cases can use.
  • If you foresee an operation that you think will be reused whether by the same or other modules, put it in a service and let this service be called by the module that wants to use it.

GraphQL - Create the type definitions for your entity

  1. Create a GraphQL file (.graphql) under a graphql folder of your module: src/modules/<module_name>/graphql/.
    • The folder structure convention is important. This particular path is being used by GraphQL Code Generator and graphql-tools/load-files to locate your type definitions.
  2. Define your GraphQL SDL.
  3. Think in graphs.
    • Your schema doesn't necessarily have to be a reflection of your database schema.

GraphQL - Create a factory for your entity type

  1. If not yet present, create a new folder named factories under src/modules/<module_name>/.

  2. Create a factory file under that folder.

    • Recommended file name should be in the format: <entity-name>.factory.ts
  3. Create the main factory function inside the file:

    • Recommended function name should be in the format: createGQL<graphql-object-type-name>

    • Make sure to specify the return type of this function to the equivalent GraphQL Object Type generated by GraphQL Code Generator.

    • Example:

      function createGQLUser(user: Selectable<User>): GQL_User {
        // ...
      }

GraphQL - Create your resolvers

  1. If not yet present, create a new folder named resolvers under src/modules/<module_name>/graphql/.
  2. Create an index.ts file under the folder.
    • Note: This is important as src/graphql/schema.ts uses the index barrels (TS files) to load the resolvers.
  3. Create the query/mutation/subscription resolver file under the folder.
  4. Implement. Refer to the resolvers under user for examples.

Tip: Keep your resolvers thin by making the business logic layer (use cases) do the actual work and calling those in the resolvers.

If you have enums defined in your GraphQL Schema, most likely you have your own internal values which means you'll have to resolve these enums to match your internal values which, most of the time, are internal enums you use in the app. Simply define the enums under the src/graphql/enums directory and make sure to export it on the index barrel.

This is also true to scalars at src/graphql/scalars.

REST - Define routes

  1. If not yet present, create a new folder named routes under src/modules/<module_name>/.
  2. Create an index.ts file inside that folder.
    • Note: This file will contain all your route definitions (router) for that module.
    • Important: Do not forget to import this router on app.ts.
  3. Define the router. See existing index files for examples.

REST - Define route handlers

  1. If not yet present, create a new folder named handlers under src/modules/<module_name>/routes/.
  2. Create a route handler file under that folder.
    • Recommended file name should be in the format: <graphql-object-type-name>.handler.ts
  3. Implement. Refer to the route handlers under user for examples.
  4. Add this route handler to your router.
    • Make sure to wrap this with the async handler util.

Database Migrations and Seeding

Migrations and Seeding is handled by Prisma. Refer to docs on how to work with this.

The environment variables (see src/config/environment.ts file) are currently configured with consideration to a running Docker container so running the scripts below directly from the Host machine wouldn't work. As you can see from the file, the connection strings are pointing to addresses that based on the Docker containers name (defined in docker-compose.yml) and the default Docker Network created from running docker-compose up makes this work.

This means you should run the Prisma commands within the Docker container. You can do this in two simple ways, either use (1) the Docker extension from VS Code or (2) do it via CLI by:

First, identify the Container ID:

docker ps

Then run the following command:

docker exec -it <container_id> sh

Tip: You don't need to supply the entire Container ID, just the first few unique sequence. For example, your target Container ID is dc123f66554b. You can just run docker-exec -it dc sh and you'll be inside the container.

Once inside the container, you can run your prisma commands.

To create a migration and/or run migrations

pnpm exec prisma migrate dev

This will run database migrations. If there are changes in the Prisma Schema before you run this command, it will create a migration file and you will be prompted for a name for the migration. Make sure to double check the output.

Important: If you're not using Docker to run this app, you need to configure the connection strings of the database servers (e.g. Redis, PostgreSQL) via the environment variables.

To seed the database

pnpm exec prisma db seed

Important: Make sure to write idempotent seed scripts!

Resetting the database also runs the seed automatically.

pnpm exec prisma migrate reset

Session Management with Supertokens

This boilerplate makes use of SuperTokens to handle sessions (with cookies) because security is very hard. The folks from Supertokens know really well what they're doing and it'd be in your best interest to know how this topic should be handled properly. Here are some relevant resources:

That said, see SuperTokens docs for specifics.

Unauthenticated requests

When trying this boilerplate and heading to the playground right away to test some GraphQL queries, you'll probably get an Unauthenticated Error. Something like the one below for example when querying users:

{
  "errors": [
    {
      "message": "Unauthenticated. Please try logging in again.",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": ["users"],
      "extensions": {
        "code": "UNAUTHENTICATED",
        "data": {
          "$errorId": "801aecca-b19f-410c-9a7e-e9724b6f0e9f"
        }
      }
    }
  ],
  "data": null
}

This is because you're trying to request a resource that requires authentication (and in turn, you'll need a session). See this issue for implementation details of this boilerplate. Basically, the idea is you need the session cookies to be attached on your requests and this can be done after logging in successfully.

You can hit the /api/v1/auth/login/superadmin endpoint to login as superadmin and get a session cookie to make requests on the playground. Your access token might expire after some time so you'll need to refresh it. Under normal circumstances, the frontend SDK of Supertokens will handle this for you but since we're working purely on the backend side for this boilerplate, you'll have to clear the cookies manually and then hit the endpoint again.

If sessions with cookies is not an option for you, Supertokens also supports header-based sessions (basically token-based). See this link for specifics. In the scenario that you're not using any of the Frontend SDK of Supertokens, then refer here.

Note: If you're not using Docker to run this app or prefer using Supertokens' Managed Service, make sure to configure the environment variables. See src/config/supertokens.ts.

Naming Convention

For files and folders

Generally, use snake-case.

In most cases, we include the file functionality in its file name in the format:

<file-name>.<functionality>.<extension>

For example:

  • get-users.use-case.ts
  • sort-direction.enum.ts
  • user.model.ts
  • async-handler.util.ts

TypeScript Interface and Type file names should match their definition name.

For example:

Interface/Type name File name
IAccessTokenPayload IAccessTokenPayload.ts
IContext IContext.ts
UniqueID UniqueID.ts
Maybe Maybe.ts

Deployment

SuperTokens Core

Read more on the SuperTokens docs based on your infrastructure.

Make sure to configure the database setup.

Node App

There are a few ways to go about this and can vary based on your setup.

Docker Way

Simply build the image from your local machine (or let your CI tool do it) with the command below.

# The dot on the end is important.
docker build -f <docker_file> -t <image_name>[:<image_tag>] .

# Example:
docker build -f Dockerfile -t graphql-starter:latest .

Then push the image you just built from your local machine (or from your CI tool) to your docker image repository (e.g. Docker Hub, AWS ECR, GitHub Container Registry).

docker push <typically_a_url_to_your_docker_image_repository>

Let your host server pull this image and run it from there.

Note: Typically, the tag for the image you built should match the url to your docker image repository. Refer to your image repository provider for specific details.

Extra: If you want to test the image you just built with your local setup, then configure the docker-compose file such that it points to your image instead of building from a Dockerfile. For example:

From:

services:
  api:
    build:
      context: .
      dockerfile: Dockerfile.dev
    command: npm start
    volumes:
      - .:/usr/src/app
      - /usr/src/app/node_modules
    restart: always

To:

services:
  api:
    # Set it to the image you just built
    image: graphql-starter:latest
    restart: always

Build Locally

If you're not using Docker, you can still get your hands on the build files. Normally, you'd want this approach if you're deploying to a VPS and have to move the files via FTP manually to your server or you have a cloud provider that accepts node apps where you'll have to provide it your project files with a package.json file in the project folder (typically zipped).

Build the project:

pnpm build:prod

This will create a build folder in the project directory which you can deploy.

Note: You might need to manually install the dependencies yourself if you're using a VPS. Otherwise, your cloud provider's NodeJS container will typically just need a package.json from the root folder and they'll do the installation on every deploy.

Pro Tips

  • When resolver types are generated by GraphQL Code Generator, the type of the 1st parameter of a field resolver is the parent type by default. This is not always true because at runtime, what the parent resolver returns is the actual type/object that will arrive in the field resolver 1st (parent) parameter. In cases like this, you'd need to type assert the parent. See full-name.query.ts.

    • Another example for this is let's say we have a pets table and a pet has an ownerId but in your GraphQL Schema, what you expose is owner and not ownerId. You won't have access to the ownerId in your resolver because GraphQL Code Generator generates what you defined in the schema. You'll have then to type assert in your resolver the type you know you returned.
  • If you want to rename a field in your GraphQL Schema:

    • DO NOT rename first on the GraphQL schema file (*.graphql).
      • Because if you do, the files that references the to-be-renamed field will break and TypeScript will fail to compile.
      • GraphQL Code Generator only generates what's defined in the schema and overwrites the generated file so the old name that was previously being referenced is now missing.
    • What you SHOULD DO instead is:
      • Rename first the field in the generated graphql type definition file by GraphQL Code Generator at src/generated/graphql/index.ts then apply the new name in the GraphQL schema file.
        • Saves your time and sanity.
  • When trying to auto import a GraphQL resolver to its respective index barrel (index.ts file), you might notice you're not getting code completion when typing the resolver name in the export default object. This is normal because your IDE thinks you're typing the key/property name (remember key-value pair).

  • When trying to debug async functions in VSCode and the breakpoints on the inner lines won't hit, try adding trace: true to launch.json file.

  • Generated custom scalars by GraphQL Code Generator are given a type of any. As with TypeScript, if you can help it, give it a type/interface then map it to GraphQL Code Generator. You can read more here. But basically:

    1. You define the type/interface in the corresponding scalar file and export it.
    2. Then map it to GraphQL Code Generator by adding scalars config in codegen.yml such that it points to its corresponding scalar file.
      • Format is: <Scalar_Name_in_GQL_Schema>: <path_to_custom_type>#<name_of_type>
  • If you're using this repo template with Docker Windows, you'll notice that when you save any file, the watchers aren't reacting. This has something to do with Linux not being able to detect file changes from Windows because the file system events are not being propagated down to Linux. You'll have to resort to polling mechanisms in that case for any tools that watches file changes. I recommend using WSL and developing inside WSL instead.

  • If you want to boost your knowledge about GraphQL, I highly recommend reading the e-book: Production Ready GraphQL by Marc-Andre Giroux.

  • If you have a use case for File Uploads, I recommend reading Apollo's blog post for options. There used to be a simple example in this boilerplate but has been removed. See Issue #42 and the associated pull request to see relevant code.

Contributing

If something is unclear, confusing, or needs to be refactored, please let me know. Pull requests are always welcome but do consider the opinionated nature of this project. Please open an issue before submitting a pull request.

Known dependency issues

It might not look good to list it here but I think it's important for you to be aware of the issues currently being worked on (and hopefully get a fix from their respective authors/maintainers soon) as you'd still end up with these issues if you were to implement these yourself and use the same dependencies.

  • Wrong TypeScript types from Apollo's graphql-subscriptions vs graphql-codegen. More info.

License

MIT License

Copyright (c) 2019-Present Cerino O. Ligutom III

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

graphql-starter's People

Contributors

cerinoligutom avatar jeydpeaschocolatebar avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

graphql-starter's Issues

Knex built in timestamps method

Hi @cerino-ligutom, thanks for sharing this great boiler plate!

I noticed you're using a custom addTimeStamps helper to add created_at and updated_at columns to your tables.
Knex actually has a built in table.timestamps method. Are you using your custom method to gain that extra precision?

How would you feel about a PR that switched to use that method?

Example:

    await knex.schema.createTable(TABLE_NAME, (t) => {
      t.uuid('id').primary();
      t.string('firstName').notNullable();
      t.string('middleName');
      t.string('lastName').notNullable();
      t.string('email').unique().notNullable();
      t.string('hash').notNullable();
    });
    await addTimeStamps(knex, TABLE_NAME, {
      createdAt: true,
      updatedAt: true,
    });
  }

becomes:

  await knex.schema.createTable(TABLE_NAME, (t) => {
    t.uuid('id').primary();
    t.string('firstName').notNullable();
    t.string('middleName');
    t.string('lastName').notNullable();
    t.string('email').unique().notNullable();
    t.string('hash').notNullable();
    t.timestamps(false, true)
  });
}

How to use migrate:latest

Thank you for the wonderful boilerplate, Amazing work.

I would like run the migrate/seed the existing src/db/migrations but unfortunately getting the below error, I have followed the steps and running all on the docker.

`
npm run migrate:latest

graphql-starter@ migrate:latest
knex --knexfile ./knexfile.* migrate:latest

Requiring external module ts-node/register
getaddrinfo ENOTFOUND db
Error: getaddrinfo ENOTFOUND db
at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:69:26)
`

Upgrade Dependencies

This issue is mainly to track dependencies that needs updating as of opening the issue.

  • knex@latest from 0.95.4
  • objection@3 from 2.2.14
  • supertokens-node@8 from 7.0.0
  • eslint and associated plugins
  • [email protected] from 4.3.5
  • Docker Image node:14-alpine to node:16-alpine
  • graphql@16 from 15.7.2
  • graphql-subscriptions@2 from 1.2.1
  • graphql-codegen and associated plugins
  • graphql-ws with backwards-compat to subscriptions-transport-ws

graphql unauthenticated

getting below error in graphql studio while query objects, even with verifySession({ sessionRequired: false })

{
  "errors": [
    {
      "message": "Unauthenticated. Please try logging in again.",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "users"
      ],
      "extensions": {
        "code": "UNAUTHENTICATED",
        "data": {
          "$errorId": "c684dd6d-f810-4ce8-b0a8-8d2f68259e00"
        },
        "stacktrace": "ErrorID: c684dd6d-f810-4ce8-b0a8-8d2f68259e00\nError: Unauthenticated. Please try logging in again.\n    at checkAuthentication (/usr/src/app/src/modules/auth/helpers/check-authentication.ts:7:11)\n    at getUsersUseCase (/usr/src/app/src/modules/user/use-cases/get-users.use-case.ts:18:28)\n    at Object.usersResolver [as users] (/usr/src/app/src/modules/user/graphql/resolvers/users.query.ts:8:39)\n    at field.resolve (/usr/src/app/node_modules/apollo-server-core/src/utils/schemaInstrumentation.ts:106:18)\n    at executeField (/usr/src/app/node_modules/graphql/execution/execute.js:479:20)\n    at executeFields (/usr/src/app/node_modules/graphql/execution/execute.js:411:20)\n    at executeOperation (/usr/src/app/node_modules/graphql/execution/execute.js:342:14)\n    at execute (/usr/src/app/node_modules/graphql/execution/execute.js:136:20)\n    at execute (/usr/src/app/node_modules/apollo-server-core/src/requestPipeline.ts:497:34)\n    at processGraphQLRequest (/usr/src/app/node_modules/apollo-server-core/src/requestPipeline.ts:403:28)"
      }
    }
  ],
  "data": null
}

I have comment checkAuthentication temporarily then got this error:
image

Drop graphql-upload

The upload feature via the GraphQL layer demonstrated currently in this boilerplate is really just to show an example that it is possible to do it via GraphQL. However, there have have been people using this boilerplate for their production apps reaching out to me and asking clarifications regarding how to handle file uploads at scale which in any case, I always end up recommending not to use graphql-upload if they have plans to scale. Apollo has a blog post about the topic and I highly recommend you to read it and go with Option 1 or 2 described there.

This boilerplate currently uses Option 3 which is not recommended. The original motivation for having this in the boilerplate was to show a working code for a file upload feature applied on a layered architecture with an opinionated folder structure along with GraphQL Code Generator. See singleUpload and multipleUpload mutations.

With that said, I'll be dropping the file upload feature and link the blog post as a tip instead. I won't be adding an opinionated way of handling file uploads as that is dependent on the implementation details of the 3rd party service and I don't want to lock this boilerplate to a specific service.

Elasticsearch support

Hi again, is there a chance to add elasticsearch support to this template in the docker-compose and the client @elastic/elasticsearch? Thanks.

micro-services integration approach

Thank you again for your usual help and support. we always looking to get expert advice like you to confirm the design approach.

let's say we have two running micro-services communicate with this template as the main micro-service, the authentication happens on the main one which is this template then it will prepare the userid with other things then pass it to the micro-service through an internal HTTP call or should we use a service bus or a library?

I'm worried about the response time, for example, assume one of the micro-service is for reference data management which supposes to return the categories if we use a queue the response will not be easy to merge with the main response from the main service. what is your advice?

Process managers & Load balancing

Hi @cerino-ligutom amazing design and nice architect, let me ask you about your suggested approach for Cluster/Load balancing? I usually use PN2 Ecosystem where there is no point in actually using it here.

Tech Stack Changes/Updates

This project has been based on my experience and opinions so far and it's time to revisit this again ๐Ÿ™‚

  • Move from npm to pnpm (#58)
  • Drop ObjectionJS and Knex in favor of Prisma (for migrations) and Kysely (query builder) (#64)
  • Drop yup in favor of zod (#64)
  • Upgrade to TypeScript 5 (#62)
  • Upgrade to Apollo Server v4 (#65)
  • Upgrade SuperTokens to latest version (#66)
  • Drop inquirer and hygen (#61)
  • Drop commitlint (#59)
  • Drop standard-version (5eb9d12)
  • Simplify ESLint rules (#63)
  • Implement graceful shutdown (#67)

After everything has been checked above:

  • Revisit websockets for GraphQL Subscription (#68)
  • Revisit seeding (#71)
  • Revisit error handling (#70)
  • Revisit UniqueID type (#69)
  • Introduce tRPC maybe?
    • I guess not since tRPC works best when both the client server are either part of the same app or part of a monorepo.
  • Update README and diagram (#72)

ESBuild architecture mismatch on arm64 mac

Thanks for creating this project! It seems really useful and well-organized. I started digging through it and trying to get it running locally and ran into some issues with ESBuild.

TLDR: arm64 mac and arm64 linux docker image mismatch esbuild dependency architecture, not sure how node_modules are shared

After following the setup instructions, and then trying to run docker-compose up, many of the services start successfully. The API service fails with the following error.

image

I researched this and many have similar problems in different projects, but the solutions I have found don't work so far. I have tried entering the docker container bash, deleting node modules, and re-running the install in hopes that the correct architecture of esbuild would be installed at that location. But that did not work. I also attempted switching the build process to use yarn and the supportedArchitectures yarnrc feature but this introduced breaking errors about the async iterator symbol TSC problems during docker build that I could not find a fix for.

I am not that familiar with docker so I have been trying to slowly decipher the process that takes place here, and at what points the node_modules are shared and what could be done to prevent that or force reinstall inside the container. Any further insight into the process here or ideas about how to resolve this?

Thanks again! Even if I can't get this working it's giving me a lot of good ideas about how to structure a project like this.

Support for logging, graphql-shield, and a real-world example

Hi

This is a very wonderful boilerplate I have come across so far.

It'd be great if you can add the following support to this..
a. logging
b. Authorization using graphql-shield
c. A real world example for example Post and Comment.

Thank you for your contributions to the community.

Rethink the index barrels since they now need to be part of the import (e.g. `@/db/index.js`)

Now that #45 thru #73 has been addressed. Have to rethink those index files as they look ugly being explicitly declared now everywhere along with the file extension name. Should this be kept or removed then have more imports? ๐Ÿค”

Back in CommonJS, given a directory

/src/db/supporting.ts
/src/db/files.ts
/src/db/here.ts
/src/db/exported.ts
/src/db/at.ts
/src/db/index.ts # Assume this is an index barrel that re-exports all the files above
/app.ts

We could import the index file from app.ts as:

import { supporting, files, here, exported, at } from './src/db';

But this doesn't work with ESModules and now have to be imported as:

import { supporting, files, here, exported, at } from './src/db/index.js'';

Which I find ugly.

I could go for a directory structure like:

/src/db/supporting.ts
/src/db/files.ts
/src/db/here.ts
/src/db/exported.ts
/src/db/at.ts
/src/db.ts

But this'd mean having a TS/JS file with an accompanying folder with the same name if there are supporting files. Take for example:

/src/dir1/**
/src/dir2/**
/src/dir3/**
/src/dir4/**
/src/dir1.ts
/src/dir2.ts
/src/dir3.ts
/src/dir4.ts

Just to avoid the index.js everywhere while looking succinct when imported.

GraphQL ICursorResult (not a bug)

Hi @cerino-ligutom Thanks again for your help and support.

I struggle to return results without paging, let's assume we would like to return all roles without paging, can you please share an example? should we create a new interface ICursorResult or just IResults ?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.